From 9cae1f98b608db89d1f8db21f75ec46659d5ffe6 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Tue, 6 Sep 2022 11:35:21 +0200 Subject: [PATCH 1/8] fix typing compatibility when linking a contract to a context --- packages/web3-core/src/web3_context.ts | 16 ++++++++-------- .../web3-eth/test/integration/defaults.test.ts | 6 +++--- .../web3_eth/send_signed_transaction.test.ts | 3 +-- packages/web3-plugin-example/src/index.ts | 2 +- packages/web3-types/src/web3_api_types.ts | 13 +++++++------ scripts/system_tests_utils.ts | 4 ++-- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/web3-core/src/web3_context.ts b/packages/web3-core/src/web3_context.ts index af138c199cd..7c8a43e401c 100644 --- a/packages/web3-core/src/web3_context.ts +++ b/packages/web3-core/src/web3_context.ts @@ -72,21 +72,21 @@ export type Web3ContextInitOptions< export type Web3ContextConstructor< // eslint-disable-next-line no-use-before-define, @typescript-eslint/no-explicit-any - T extends Web3Context, + T extends Web3Context, T2 extends unknown[], > = new (...args: [...extras: T2, context: Web3ContextObject]) => T; // To avoid circular dependencies, we need to export type from here. export type Web3ContextFactory< // eslint-disable-next-line no-use-before-define, @typescript-eslint/no-explicit-any - T extends Web3Context, + T extends Web3Context, T2 extends unknown[], > = Web3ContextConstructor & { fromContextObject(this: Web3ContextConstructor, contextObject: Web3ContextObject): T; }; export class Web3Context< - API extends Web3APISpec, + API extends Web3APISpec = any, RegisteredSubs extends { [key: string]: Web3SubscriptionConstructor; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -95,7 +95,7 @@ export class Web3Context< public static readonly providers = Web3RequestManager.providers; public static givenProvider?: SupportedProviders; public readonly providers = Web3RequestManager.providers; - protected _requestManager: Web3RequestManager; + protected _requestManager: Web3RequestManager; protected _subscriptionManager?: Web3SubscriptionManager; protected _accountProvider?: Web3AccountProvider; protected _wallet?: Web3BaseWallet; @@ -174,14 +174,14 @@ export class Web3Context< } // eslint-disable-next-line @typescript-eslint/no-explicit-any - public static fromContextObject, T3 extends unknown[]>( + public static fromContextObject( this: Web3ContextConstructor, ...args: [Web3ContextObject, ...T3] ) { return new this(...(args.reverse() as [...T3, Web3ContextObject])); } - public getContextObject(): Web3ContextObject { + public getContextObject(): Web3ContextObject { return { config: this.getConfig(), provider: this.provider, @@ -201,7 +201,7 @@ export class Web3Context< * and then use it to create new objects of any type extended by `Web3Context`. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any - public use, T2 extends unknown[]>( + public use( ContextRef: Web3ContextConstructor, ...args: [...T2] ) { @@ -220,7 +220,7 @@ export class Web3Context< /** * Link current context to another context. */ - public link>(parentContext: T) { + public link(parentContext: T) { this.setConfig(parentContext.getConfig()); this._requestManager = parentContext.requestManager; this.provider = parentContext.provider; diff --git a/packages/web3-eth/test/integration/defaults.test.ts b/packages/web3-eth/test/integration/defaults.test.ts index 17051668cbf..4278e602aea 100644 --- a/packages/web3-eth/test/integration/defaults.test.ts +++ b/packages/web3-eth/test/integration/defaults.test.ts @@ -628,7 +628,7 @@ describe('defaults', () => { value: '0x174876e800', gas: '0x5208', }, - web3Context: eth2 as Web3Context, + web3Context: eth2 as Web3Context, }); expect(res.networkId).toBe(4); @@ -641,7 +641,7 @@ describe('defaults', () => { gas: '0x5208', networkId: 5, }, - web3Context: eth2 as Web3Context, + web3Context: eth2 as Web3Context, }); expect(resWithPassNetworkId.networkId).toBe(BigInt(5)); @@ -671,7 +671,7 @@ describe('defaults', () => { value: '0x174876e800', gas: '0x5208', }, - web3Context: eth2 as Web3Context, + web3Context: eth2 as Web3Context, }); expect(res.chain).toBe('rinkeby'); }); diff --git a/packages/web3-eth/test/integration/web3_eth/send_signed_transaction.test.ts b/packages/web3-eth/test/integration/web3_eth/send_signed_transaction.test.ts index 0165e16317f..01a490a3048 100644 --- a/packages/web3-eth/test/integration/web3_eth/send_signed_transaction.test.ts +++ b/packages/web3-eth/test/integration/web3_eth/send_signed_transaction.test.ts @@ -18,14 +18,13 @@ along with web3.js. If not, see . import { Bytes, SignedTransactionInfoAPI, Transaction } from 'web3-types'; import { DEFAULT_RETURN_FORMAT, FMT_BYTES, FMT_NUMBER, format, hexToNumber } from 'web3-utils'; import { isHexStrict } from 'web3-validator'; -import { Web3Eth, InternalTransaction } from '../../../src'; +import { Web3Eth, InternalTransaction, transactionSchema } from '../../../src'; import { closeOpenConnection, createTempAccount, getSystemTestProvider, } from '../../fixtures/system_test_utils'; import { getTransactionGasPricing } from '../../../src/utils/get_transaction_gas_pricing'; -import { transactionSchema } from '../../../src'; const HEX_NUMBER_DATA_FORMAT = { bytes: FMT_BYTES.HEX, number: FMT_NUMBER.HEX } as const; diff --git a/packages/web3-plugin-example/src/index.ts b/packages/web3-plugin-example/src/index.ts index db1545365bd..8a12b1dd00a 100644 --- a/packages/web3-plugin-example/src/index.ts +++ b/packages/web3-plugin-example/src/index.ts @@ -41,7 +41,7 @@ declare module 'web3' { } } -export class ChainlinkPlugin extends Web3PluginBase { +export class ChainlinkPlugin extends Web3PluginBase { public pluginNamespace = 'chainlink'; protected readonly _contract: Contract; diff --git a/packages/web3-types/src/web3_api_types.ts b/packages/web3-types/src/web3_api_types.ts index a316e342e79..43d52f1aa59 100644 --- a/packages/web3-types/src/web3_api_types.ts +++ b/packages/web3-types/src/web3_api_types.ts @@ -17,11 +17,12 @@ along with web3.js. If not, see . import { JsonRpcId, JsonRpcIdentifier } from './json_rpc_types'; -export type Web3APISpec = Record any>; -export type Web3APIMethod = string & keyof T; -export type Web3APIParams> = Parameters< - API[Method] ->; +export type Web3APISpec = Record any> | any; +export type Web3APIMethod = string & keyof Exclude; +export type Web3APIParams< + API extends Web3APISpec, + Method extends Web3APIMethod, +> = API extends Record any> ? Parameters : any; export interface Web3APIRequest> { method: Method; @@ -37,4 +38,4 @@ export interface Web3APIPayload, -> = ReturnType; +> = API extends Record any> ? ReturnType : any; diff --git a/scripts/system_tests_utils.ts b/scripts/system_tests_utils.ts index 53e94e3200a..60c5e77e57d 100644 --- a/scripts/system_tests_utils.ts +++ b/scripts/system_tests_utils.ts @@ -81,7 +81,7 @@ const maxNumberOfAttempts = 10; const intervalTime = 5000; // ms export const waitForOpenConnection = async ( - web3Context: Web3Context, + web3Context: Web3Context, currentAttempt = 1, status = 'connected', ) => @@ -106,7 +106,7 @@ export const waitForOpenConnection = async ( }, intervalTime); }); -export const closeOpenConnection = async (web3Context: Web3Context) => { +export const closeOpenConnection = async (web3Context: Web3Context) => { if (!isWs && !isIpc) { return; } From 3e3cb8e20e7eb9909f4082faa0487b3d54e67cb2 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Thu, 8 Sep 2022 11:38:39 +0200 Subject: [PATCH 2/8] replace any with unknown at a suggested code for Web3Context --- packages/web3-core/src/web3_context.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/web3-core/src/web3_context.ts b/packages/web3-core/src/web3_context.ts index 7c8a43e401c..f7b148c8292 100644 --- a/packages/web3-core/src/web3_context.ts +++ b/packages/web3-core/src/web3_context.ts @@ -36,8 +36,7 @@ import { Web3BatchRequest } from './web3_batch_request'; // To avoid circular dependencies, we need to export type from here. export type Web3ContextObject< - // eslint-disable-next-line @typescript-eslint/no-explicit-any - API extends Web3APISpec = any, + API extends Web3APISpec = unknown, RegisteredSubs extends { [key: string]: Web3SubscriptionConstructor; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -54,8 +53,7 @@ export type Web3ContextObject< }; export type Web3ContextInitOptions< - // eslint-disable-next-line @typescript-eslint/no-explicit-any - API extends Web3APISpec = any, + API extends Web3APISpec = unknown, RegisteredSubs extends { [key: string]: Web3SubscriptionConstructor; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -86,7 +84,7 @@ export type Web3ContextFactory< }; export class Web3Context< - API extends Web3APISpec = any, + API extends Web3APISpec = unknown, RegisteredSubs extends { [key: string]: Web3SubscriptionConstructor; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -95,7 +93,7 @@ export class Web3Context< public static readonly providers = Web3RequestManager.providers; public static givenProvider?: SupportedProviders; public readonly providers = Web3RequestManager.providers; - protected _requestManager: Web3RequestManager; + protected _requestManager: Web3RequestManager; protected _subscriptionManager?: Web3SubscriptionManager; protected _accountProvider?: Web3AccountProvider; protected _wallet?: Web3BaseWallet; @@ -283,10 +281,9 @@ export class Web3Context< // To avoid cycle dependency declare this type in this file // TODO: When we have `web3-types` package we can share TransactionType -export type TransactionBuilder< - // eslint-disable-next-line @typescript-eslint/no-explicit-any - API extends Web3APISpec = any, -> = >(options: { +export type TransactionBuilder = < + ReturnType = Record, +>(options: { transaction: Record; web3Context: Web3Context; privateKey?: HexString | Buffer; From d43ac3a96a74a8760e92c2993049d26131428c98 Mon Sep 17 00:00:00 2001 From: Nikos Iliakis Date: Tue, 27 Sep 2022 15:43:25 +0300 Subject: [PATCH 3/8] Web3 net system tests (#5457) * Add tests * Update geth script * Fix geth script * Reset to initial chainId * Remove uneeded space * Ganace chainId --- .../test/fixtures/system_tests_utils.ts | 1 + .../test/integration/web3_net.test.ts | 54 +++++++++++++++++++ scripts/ganache.sh | 4 +- 3 files changed, 57 insertions(+), 2 deletions(-) create mode 120000 packages/web3-net/test/fixtures/system_tests_utils.ts create mode 100644 packages/web3-net/test/integration/web3_net.test.ts diff --git a/packages/web3-net/test/fixtures/system_tests_utils.ts b/packages/web3-net/test/fixtures/system_tests_utils.ts new file mode 120000 index 00000000000..2ab08a83752 --- /dev/null +++ b/packages/web3-net/test/fixtures/system_tests_utils.ts @@ -0,0 +1 @@ +../../../../scripts/system_tests_utils.ts \ No newline at end of file diff --git a/packages/web3-net/test/integration/web3_net.test.ts b/packages/web3-net/test/integration/web3_net.test.ts new file mode 100644 index 00000000000..394f7ac0e3f --- /dev/null +++ b/packages/web3-net/test/integration/web3_net.test.ts @@ -0,0 +1,54 @@ +/* +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 Net from '../../src'; + +import { getSystemTestProvider, closeOpenConnection } from '../fixtures/system_tests_utils'; + +describe('Web3 net', () => { + let clientUrl: string; + let web3Net: Net; + + beforeAll(async () => { + clientUrl = getSystemTestProvider(); + }); + + afterAll(async () => { + await closeOpenConnection(web3Net); + }); + + it('should be able to create instance', () => { + web3Net = new Net(clientUrl); + + expect(web3Net).toBeInstanceOf(Net); + }); + + it('should be able to get id', async () => { + const networkId = await web3Net.getId(); + expect(networkId).toBe(BigInt(1337)); + }); + + it('should be able to listen', async () => { + const isListening = await web3Net.isListening(); + expect(isListening).toBeTruthy(); + }); + + it('should fetch peer count', async () => { + const peerCount = await web3Net.getPeerCount(); + expect(peerCount).toBe(BigInt(0)); + }); +}); diff --git a/scripts/ganache.sh b/scripts/ganache.sh index d98926ab183..f7f9089b686 100755 --- a/scripts/ganache.sh +++ b/scripts/ganache.sh @@ -12,10 +12,10 @@ start() { if [ -z "${ORIGARGS[1]}" ] then - docker run --publish 8545:8545 trufflesuite/ganache:latest -m "$WEB3_SYSTEM_TEST_MNEMONIC" -a 5 -p $WEB3_SYSTEM_TEST_PORT --wallet.passphrase "123" + docker run --publish 8545:8545 trufflesuite/ganache:latest -m "$WEB3_SYSTEM_TEST_MNEMONIC" -a 5 -p $WEB3_SYSTEM_TEST_PORT --wallet.passphrase "123" --networkId 1337 else echo "Starting ganache ..." - docker run --detach --publish 8545:8545 trufflesuite/ganache:latest -m "$WEB3_SYSTEM_TEST_MNEMONIC" -a 5 -p $WEB3_SYSTEM_TEST_PORT --wallet.passphrase "123" + docker run --detach --publish 8545:8545 trufflesuite/ganache:latest -m "$WEB3_SYSTEM_TEST_MNEMONIC" -a 5 -p $WEB3_SYSTEM_TEST_PORT --wallet.passphrase "123" --networkId 1337 echo "Waiting for ganache..." npx wait-port "$WEB3_SYSTEM_TEST_PORT" From 6408b572d7a235a3e2f586b25fb67996a2fa1a38 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Wed, 28 Sep 2022 18:52:45 +0200 Subject: [PATCH 4/8] more fixes and sample calls for the plugin draft --- packages/web3-core/src/web3_context.ts | 27 ++++++---- packages/web3-plugin-example/src/index.ts | 62 +++++++++++++++++------ 2 files changed, 62 insertions(+), 27 deletions(-) diff --git a/packages/web3-core/src/web3_context.ts b/packages/web3-core/src/web3_context.ts index f7b148c8292..9c0fe714c5d 100644 --- a/packages/web3-core/src/web3_context.ts +++ b/packages/web3-core/src/web3_context.ts @@ -22,6 +22,7 @@ import { Web3AccountProvider, SupportedProviders, HexString, + EthExecutionAPI, } from 'web3-types'; import { isNullish } from 'web3-utils'; import { ExistingPluginNamespaceError } from 'web3-errors'; @@ -68,15 +69,14 @@ export type Web3ContextInitOptions< wallet?: Web3BaseWallet; }; -export type Web3ContextConstructor< - // eslint-disable-next-line no-use-before-define, @typescript-eslint/no-explicit-any - T extends Web3Context, - T2 extends unknown[], -> = new (...args: [...extras: T2, context: Web3ContextObject]) => T; +// eslint-disable-next-line no-use-before-define +export type Web3ContextConstructor = new ( + ...args: [...extras: T2, context: Web3ContextObject] +) => T; // To avoid circular dependencies, we need to export type from here. export type Web3ContextFactory< - // eslint-disable-next-line no-use-before-define, @typescript-eslint/no-explicit-any + // eslint-disable-next-line no-use-before-define T extends Web3Context, T2 extends unknown[], > = Web3ContextConstructor & { @@ -93,7 +93,7 @@ export class Web3Context< public static readonly providers = Web3RequestManager.providers; public static givenProvider?: SupportedProviders; public readonly providers = Web3RequestManager.providers; - protected _requestManager: Web3RequestManager; + protected _requestManager: Web3RequestManager; protected _subscriptionManager?: Web3SubscriptionManager; protected _accountProvider?: Web3AccountProvider; protected _wallet?: Web3BaseWallet; @@ -179,7 +179,7 @@ export class Web3Context< return new this(...(args.reverse() as [...T3, Web3ContextObject])); } - public getContextObject(): Web3ContextObject { + public getContextObject(): Web3ContextObject { return { config: this.getConfig(), provider: this.provider, @@ -198,7 +198,6 @@ export class Web3Context< * and link it to current context. This can be used to initiate a global context object * and then use it to create new objects of any type extended by `Web3Context`. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any public use( ContextRef: Web3ContextConstructor, ...args: [...T2] @@ -233,7 +232,7 @@ export class Web3Context< } // eslint-disable-next-line no-use-before-define - public registerPlugin(plugin: Web3PluginBase) { + public registerPlugin(plugin: Web3PluginBase) { // @ts-expect-error No index signature with a parameter of type 'string' was found on type 'Web3Context' if (this[plugin.pluginNamespace] !== undefined) throw new ExistingPluginNamespaceError(plugin.pluginNamespace); @@ -289,6 +288,12 @@ export type TransactionBuilder = < privateKey?: HexString | Buffer; }) => Promise; -export abstract class Web3PluginBase extends Web3Context { +export abstract class Web3PluginBase< + API extends Web3APISpec = EthExecutionAPI, +> extends Web3Context { public abstract pluginNamespace: string; } + +export abstract class Web3EthPluginBase extends Web3PluginBase< + API & EthExecutionAPI +> {} diff --git a/packages/web3-plugin-example/src/index.ts b/packages/web3-plugin-example/src/index.ts index 8a12b1dd00a..f039b536d42 100644 --- a/packages/web3-plugin-example/src/index.ts +++ b/packages/web3-plugin-example/src/index.ts @@ -16,10 +16,10 @@ along with web3.js. If not, see . */ import { ContractAbi } from 'web3-eth-abi'; import Contract from 'web3-eth-contract'; -import { Web3PluginBase } from 'web3-core'; -import { Address, Web3APISpec } from 'web3-types'; -// @ts-expect-error 'Web3' is declared but its value is never read. -import { Web3 } from 'web3'; +import { Web3EthPluginBase } from 'web3-core'; +import { Address, BlockNumberOrTag } from 'web3-types'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +// import { Web3 } from 'web3'; import { AggregatorV3InterfaceABI } from './aggregator_v3_interface_abi'; @@ -31,17 +31,11 @@ interface Price { answeredInRound: string; } -interface ChainlinkPluginAPI extends Web3APISpec { - getPrice: () => Promise; -} - -declare module 'web3' { - interface Web3 { - chainlink: ChainlinkPluginAPI; - } -} +export type ChainlinkPluginAPI = { + customJsonRpcMethod: () => Promise; +}; -export class ChainlinkPlugin extends Web3PluginBase { +export class ChainlinkPlugin extends Web3EthPluginBase { public pluginNamespace = 'chainlink'; protected readonly _contract: Contract; @@ -51,8 +45,44 @@ export class ChainlinkPlugin extends Web3PluginBase { this._contract = new Contract(abi, address); } - public async getPrice() { + /** + * An example for calling a custom JSON RPC function called customJsonRpcMethod + * Supposing that the connected Ethereum Node provides non-standard function called `customJsonRpcMethod` + */ + public async customApi() { + return this._requestManager.send({ + method: 'customJsonRpcMethod', + params: [], + }); + } + + /** + * An example for providing a method that calls a smart contract and do possibly do some processing + * @returns a promise to Price + */ + public async getPrice(): Promise { + // call any function(s) or smart contract method(s) if (this._contract.currentProvider === undefined) this._contract.link(this); - return this._contract.methods.latestRoundData().call(); + const price = await this._contract.methods.latestRoundData().call(); + // do whatever processing needed and return + return price as unknown as Price; + } + + /** + * Just to show how the standard JSON RPC methods could be called as usual + * @returns a promise to a string + */ + public async getBalance(address: Address, blockNumber: BlockNumberOrTag) { + // call any standard + return this._requestManager.send({ + method: 'eth_getBalance', + params: [address, blockNumber], + }); + } +} + +declare module 'web3' { + interface Web3 { + chainlink: ChainlinkPlugin; } } From d93c86c6433f094456e928344ffaba862d86d938 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Wed, 28 Sep 2022 19:13:43 +0200 Subject: [PATCH 5/8] tiny code revert for unused variable --- packages/web3-plugin-example/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web3-plugin-example/src/index.ts b/packages/web3-plugin-example/src/index.ts index f039b536d42..f203d7167a5 100644 --- a/packages/web3-plugin-example/src/index.ts +++ b/packages/web3-plugin-example/src/index.ts @@ -18,8 +18,8 @@ import { ContractAbi } from 'web3-eth-abi'; import Contract from 'web3-eth-contract'; import { Web3EthPluginBase } from 'web3-core'; import { Address, BlockNumberOrTag } from 'web3-types'; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -// import { Web3 } from 'web3'; +// @ts-expect-error 'Web3' is declared but its value is never read. +import { Web3 } from 'web3'; import { AggregatorV3InterfaceABI } from './aggregator_v3_interface_abi'; From 37cec514479f94057118fd58744b9ea225718e33 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Fri, 30 Sep 2022 11:41:03 +0200 Subject: [PATCH 6/8] add comments and replace some `any`s with `unknown`s for plugin related code --- packages/web3-core/src/web3_context.ts | 18 ++++++++++++++++-- packages/web3-types/src/web3_api_types.ts | 6 +++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/web3-core/src/web3_context.ts b/packages/web3-core/src/web3_context.ts index 9c0fe714c5d..7652f34cd60 100644 --- a/packages/web3-core/src/web3_context.ts +++ b/packages/web3-core/src/web3_context.ts @@ -288,12 +288,26 @@ export type TransactionBuilder = < privateKey?: HexString | Buffer; }) => Promise; +/** + * To implement a plugin that connects to a Node that does not fully implements the Ethereum JSON RPC standard methods, + * inherit from this Class. + * This class would also be used if the plugin was for non-Ethereum systems that could be supported later. + * However, for such implementation, more research and modifications would be needed. + */ export abstract class Web3PluginBase< - API extends Web3APISpec = EthExecutionAPI, + API extends Web3APISpec = Web3APISpec, > extends Web3Context { public abstract pluginNamespace: string; } -export abstract class Web3EthPluginBase extends Web3PluginBase< +/** + * To implement a plugin that connects to a Node that fully implements the Ethereum JSON RPC standard methods, + * inherit from this Class. + * And if the plugin provides more than the Ethereum JSON RPC standard methods, you can pass + * the specification of additional methods similar to the following: + * `export class CustomPlugin extends Web3EthPluginBase {...}` + * + */ +export abstract class Web3EthPluginBase extends Web3PluginBase< API & EthExecutionAPI > {} diff --git a/packages/web3-types/src/web3_api_types.ts b/packages/web3-types/src/web3_api_types.ts index 43d52f1aa59..fe96cc52a69 100644 --- a/packages/web3-types/src/web3_api_types.ts +++ b/packages/web3-types/src/web3_api_types.ts @@ -17,12 +17,12 @@ along with web3.js. If not, see . import { JsonRpcId, JsonRpcIdentifier } from './json_rpc_types'; -export type Web3APISpec = Record any> | any; -export type Web3APIMethod = string & keyof Exclude; +export type Web3APISpec = Record any> | unknown; +export type Web3APIMethod = string & keyof Exclude; export type Web3APIParams< API extends Web3APISpec, Method extends Web3APIMethod, -> = API extends Record any> ? Parameters : any; +> = API extends Record any> ? Parameters : unknown; export interface Web3APIRequest> { method: Method; From 903d64241bbed856350713d4f078a0337914ab8d Mon Sep 17 00:00:00 2001 From: Junaid <86780488+jdevcs@users.noreply.github.com> Date: Fri, 30 Sep 2022 12:05:34 +0200 Subject: [PATCH 7/8] 5427 contracts unit tests (#5465) * sample abi of a contract * unit tests contract * contract events test * unit test data * additional tests for more coverage * default common check * test contract source and updated abi * common type fix --- .../contracts/SampleStorageContract.sol | 34 ++ .../test/fixtures/storage.ts | 59 +++ .../test/fixtures/unitTestFixtures.ts | 142 ++++++ .../test/unit/contract.test.ts | 409 ++++++++++++++++++ 4 files changed, 644 insertions(+) create mode 100644 packages/web3-eth-contract/test/fixtures/contracts/SampleStorageContract.sol create mode 100644 packages/web3-eth-contract/test/fixtures/storage.ts create mode 100644 packages/web3-eth-contract/test/fixtures/unitTestFixtures.ts diff --git a/packages/web3-eth-contract/test/fixtures/contracts/SampleStorageContract.sol b/packages/web3-eth-contract/test/fixtures/contracts/SampleStorageContract.sol new file mode 100644 index 00000000000..2c0c640135d --- /dev/null +++ b/packages/web3-eth-contract/test/fixtures/contracts/SampleStorageContract.sol @@ -0,0 +1,34 @@ +/* +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 . +*/ + +pragma solidity ^0.8.7; + +contract SampleStorageContract { + + uint256 uintNum; + + event NEWNUM(uint256 param); + + function storeNum(uint256 param) public { + uintNum = param; + emit NEWNUM(param); + } + + function retrieveNum() public view returns (uint256){ + return uintNum; + } +} \ No newline at end of file diff --git a/packages/web3-eth-contract/test/fixtures/storage.ts b/packages/web3-eth-contract/test/fixtures/storage.ts new file mode 100644 index 00000000000..5ec10ed2c37 --- /dev/null +++ b/packages/web3-eth-contract/test/fixtures/storage.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 . +*/ + +// sample storage contract ABI +export const sampleStorageContractABI = [ + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'param', + type: 'uint256', + }, + ], + name: 'NEWNUM', + type: 'event', + }, + { + inputs: [], + name: 'retrieveNum', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'param', + type: 'uint256', + }, + ], + name: 'storeNum', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +]; diff --git a/packages/web3-eth-contract/test/fixtures/unitTestFixtures.ts b/packages/web3-eth-contract/test/fixtures/unitTestFixtures.ts new file mode 100644 index 00000000000..1316e0f6e58 --- /dev/null +++ b/packages/web3-eth-contract/test/fixtures/unitTestFixtures.ts @@ -0,0 +1,142 @@ +/* +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 . +*/ + +export const getLogsData = { + request: { + fromBlock: 'earliest', + toBlock: 'latest', + topics: ['0x7d7846723bda52976e0286c6efffee937ee9f76817a867ec70531ad29fb1fc0e'], + }, + + response: [ + { + logIndex: 1, + transactionIndex: 0, + transactionHash: '0xbe70733bcf87282c0ba9bf3c0e2d545084fad48bd571c314140c8dc1db882673', + blockHash: '0x78755c18c9a0a1283fa04b2f78c7794c249395b08f7f7dff304034d64d6a1607', + blockNumber: 25, + address: '0x2D029a4bd792d795f35e0583F64eD9DedeBBa849', + data: '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000', + topics: ['0x7d7846723bda52976e0286c6efffee937ee9f76817a867ec70531ad29fb1fc0e'], + type: 'mined', + id: 'log_886b29f0', + }, + ], +}; + +export const getPastEventsData = { + event: 'GREETING_CHANGED', + response: [ + { + logIndex: BigInt(1), + transactionIndex: BigInt(0), + transactionHash: '0xbe70733bcf87282c0ba9bf3c0e2d545084fad48bd571c314140c8dc1db882673', + blockHash: '0x78755c18c9a0a1283fa04b2f78c7794c249395b08f7f7dff304034d64d6a1607', + blockNumber: BigInt(25), + address: '0x2D029a4bd792d795f35e0583F64eD9DedeBBa849', + data: '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000', + topics: ['0x7d7846723bda52976e0286c6efffee937ee9f76817a867ec70531ad29fb1fc0e'], + returnValues: { + '0': 'Hello', + __length__: 1, + greeting: 'Hello', + }, + event: 'GREETING_CHANGED', + signature: '0x7d7846723bda52976e0286c6efffee937ee9f76817a867ec70531ad29fb1fc0e', + raw: { + data: '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000', + topics: ['0x7d7846723bda52976e0286c6efffee937ee9f76817a867ec70531ad29fb1fc0e'], + }, + }, + ], +}; + +export const AllGetPastEventsData = { + getLogsData: [ + { + logIndex: 0, + transactionIndex: 0, + transactionHash: '0x1ba478ce1810bfa8a0725c0ca94f3cfe163a70c396037a1f3c94cad34e497959', + blockHash: '0x79eece1fb22b7109f302b65bd826b1cebf9f704642e86ae9086ed93baf44a45e', + blockNumber: 20, + address: '0x20bc23D0598b12c34cBDEf1fae439Ba8744DB426', + data: '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010416e6f74686572204772656574696e6700000000000000000000000000000000', + topics: ['0x0d363f2fba46ab11b6db8da0125b0d5484787c44e265b48810735998bab12b75'], + type: 'mined', + id: 'log_0a03b06c', + }, + { + logIndex: 1, + transactionIndex: 0, + transactionHash: '0x1ba478ce1810bfa8a0725c0ca94f3cfe163a70c396037a1f3c94cad34e497959', + blockHash: '0x79eece1fb22b7109f302b65bd826b1cebf9f704642e86ae9086ed93baf44a45e', + blockNumber: 20, + address: '0x20bc23D0598b12c34cBDEf1fae439Ba8744DB426', + data: '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010416e6f74686572204772656574696e6700000000000000000000000000000000', + topics: ['0x7d7846723bda52976e0286c6efffee937ee9f76817a867ec70531ad29fb1fc0e'], + type: 'mined', + id: 'log_389ae161', + }, + ], + + response: [ + { + logIndex: BigInt(0), + transactionIndex: BigInt(0), + transactionHash: '0x1ba478ce1810bfa8a0725c0ca94f3cfe163a70c396037a1f3c94cad34e497959', + blockHash: '0x79eece1fb22b7109f302b65bd826b1cebf9f704642e86ae9086ed93baf44a45e', + blockNumber: BigInt(20), + address: '0x20bc23D0598b12c34cBDEf1fae439Ba8744DB426', + data: '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010416e6f74686572204772656574696e6700000000000000000000000000000000', + topics: ['0x0d363f2fba46ab11b6db8da0125b0d5484787c44e265b48810735998bab12b75'], + returnValues: { + '0': 'Hello', + '1': 'Another Greeting', + __length__: 2, + from: 'Hello', + to: 'Another Greeting', + }, + event: 'GREETING_CHANGING', + signature: '0x0d363f2fba46ab11b6db8da0125b0d5484787c44e265b48810735998bab12b75', + raw: { + data: '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010416e6f74686572204772656574696e6700000000000000000000000000000000', + topics: ['0x0d363f2fba46ab11b6db8da0125b0d5484787c44e265b48810735998bab12b75'], + }, + }, + { + logIndex: BigInt(1), + transactionIndex: BigInt(0), + transactionHash: '0x1ba478ce1810bfa8a0725c0ca94f3cfe163a70c396037a1f3c94cad34e497959', + blockHash: '0x79eece1fb22b7109f302b65bd826b1cebf9f704642e86ae9086ed93baf44a45e', + blockNumber: BigInt(20), + address: '0x20bc23D0598b12c34cBDEf1fae439Ba8744DB426', + data: '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010416e6f74686572204772656574696e6700000000000000000000000000000000', + topics: ['0x7d7846723bda52976e0286c6efffee937ee9f76817a867ec70531ad29fb1fc0e'], + returnValues: { + '0': 'Another Greeting', + __length__: 1, + greeting: 'Another Greeting', + }, + event: 'GREETING_CHANGED', + signature: '0x7d7846723bda52976e0286c6efffee937ee9f76817a867ec70531ad29fb1fc0e', + raw: { + data: '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010416e6f74686572204772656574696e6700000000000000000000000000000000', + topics: ['0x7d7846723bda52976e0286c6efffee937ee9f76817a867ec70531ad29fb1fc0e'], + }, + }, + ], +}; diff --git a/packages/web3-eth-contract/test/unit/contract.test.ts b/packages/web3-eth-contract/test/unit/contract.test.ts index 27feea9d8e9..54b7ca0ede4 100644 --- a/packages/web3-eth-contract/test/unit/contract.test.ts +++ b/packages/web3-eth-contract/test/unit/contract.test.ts @@ -15,7 +15,14 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ +import * as eth from 'web3-eth'; +import { ValidChains } from 'web3-types'; import { Contract } from '../../src'; +import { sampleStorageContractABI } from '../fixtures/storage'; +import { GreeterAbi, GreeterBytecode } from '../shared_fixtures/build/Greeter'; +import { AllGetPastEventsData, getLogsData, getPastEventsData } from '../fixtures/unitTestFixtures'; + +jest.mock('web3-eth'); describe('Contract', () => { describe('constructor', () => { @@ -66,4 +73,406 @@ describe('Contract', () => { expect(contract).toBeInstanceOf(Contract); }); }); + + describe('Contract functions and defaults', () => { + let sendOptions: Record; + const deployedAddr = '0x20bc23D0598b12c34cBDEf1fae439Ba8744DB426'; + + beforeAll(() => { + sendOptions = { + from: '0x12364916b10Ae90076dDa6dE756EE1395BB69ec2', + gas: '1000000', + }; + }); + + it('should deploy contract', async () => { + const data = + '0x60806040523480156200001157600080fd5b5060405162000a6a38038062000a6a8339818101604052810190620000379190620002a4565b80600090805190602001906200004f92919062000057565b505062000359565b828054620000659062000324565b90600052602060002090601f016020900481019282620000895760008555620000d5565b82601f10620000a457805160ff1916838001178555620000d5565b82800160010185558215620000d5579182015b82811115620000d4578251825591602001919060010190620000b7565b5b509050620000e49190620000e8565b5090565b5b8082111562000103576000816000905550600101620000e9565b5090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620001708262000125565b810181811067ffffffffffffffff8211171562000192576200019162000136565b5b80604052505050565b6000620001a762000107565b9050620001b5828262000165565b919050565b600067ffffffffffffffff821115620001d857620001d762000136565b5b620001e38262000125565b9050602081019050919050565b60005b8381101562000210578082015181840152602081019050620001f3565b8381111562000220576000848401525b50505050565b60006200023d6200023784620001ba565b6200019b565b9050828152602081018484840111156200025c576200025b62000120565b5b62000269848285620001f0565b509392505050565b600082601f8301126200028957620002886200011b565b5b81516200029b84826020860162000226565b91505092915050565b600060208284031215620002bd57620002bc62000111565b5b600082015167ffffffffffffffff811115620002de57620002dd62000116565b5b620002ec8482850162000271565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200033d57607f821691505b602082108103620003535762000352620002f5565b5b50919050565b61070180620003696000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063a41368621461003b578063cfae32171461006c575b600080fd5b6100556004803603810190610050919061043f565b61008a565b60405161006392919061052b565b60405180910390f35b6100746101b0565b604051610081919061055b565b60405180910390f35b600060607f0d363f2fba46ab11b6db8da0125b0d5484787c44e265b48810735998bab12b756000846040516100c0929190610672565b60405180910390a182600090805190602001906100de929190610242565b507f7d7846723bda52976e0286c6efffee937ee9f76817a867ec70531ad29fb1fc0e600060405161010f91906106a9565b60405180910390a160016000808054610127906105ac565b80601f0160208091040260200160405190810160405280929190818152602001828054610153906105ac565b80156101a05780601f10610175576101008083540402835291602001916101a0565b820191906000526020600020905b81548152906001019060200180831161018357829003601f168201915b5050505050905091509150915091565b6060600080546101bf906105ac565b80601f01602080910402602001604051908101604052809291908181526020018280546101eb906105ac565b80156102385780601f1061020d57610100808354040283529160200191610238565b820191906000526020600020905b81548152906001019060200180831161021b57829003601f168201915b5050505050905090565b82805461024e906105ac565b90600052602060002090601f01602090048101928261027057600085556102b7565b82601f1061028957805160ff19168380011785556102b7565b828001600101855582156102b7579182015b828111156102b657825182559160200191906001019061029b565b5b5090506102c491906102c8565b5090565b5b808211156102e15760008160009055506001016102c9565b5090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61034c82610303565b810181811067ffffffffffffffff8211171561036b5761036a610314565b5b80604052505050565b600061037e6102e5565b905061038a8282610343565b919050565b600067ffffffffffffffff8211156103aa576103a9610314565b5b6103b382610303565b9050602081019050919050565b82818337600083830152505050565b60006103e26103dd8461038f565b610374565b9050828152602081018484840111156103fe576103fd6102fe565b5b6104098482856103c0565b509392505050565b600082601f830112610426576104256102f9565b5b81356104368482602086016103cf565b91505092915050565b600060208284031215610455576104546102ef565b5b600082013567ffffffffffffffff811115610473576104726102f4565b5b61047f84828501610411565b91505092915050565b60008115159050919050565b61049d81610488565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b838110156104dd5780820151818401526020810190506104c2565b838111156104ec576000848401525b50505050565b60006104fd826104a3565b61050781856104ae565b93506105178185602086016104bf565b61052081610303565b840191505092915050565b60006040820190506105406000830185610494565b818103602083015261055281846104f2565b90509392505050565b6000602082019050818103600083015261057581846104f2565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806105c457607f821691505b6020821081036105d7576105d661057d565b5b50919050565b60008190508160005260206000209050919050565b600081546105ff816105ac565b61060981866104ae565b94506001821660008114610624576001811461063657610669565b60ff1983168652602086019350610669565b61063f856105dd565b60005b8381101561066157815481890152600182019150602081019050610642565b808801955050505b50505092915050565b6000604082019050818103600083015261068c81856105f2565b905081810360208301526106a081846104f2565b90509392505050565b600060208201905081810360008301526106c381846105f2565b90509291505056fea26469706673582212203746baed62e6fd543d947eb69460c5abf2eef8b85afff65b442695d4abaaebbc64736f6c634300080d00330000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b4d79204772656574696e67000000000000000000000000000000000000000000'; + const contract = new Contract(GreeterAbi); + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const deploySpy = jest + .spyOn(eth, 'sendTransaction') + .mockImplementation((_objInstance, tx) => { + expect(tx.to).toBeUndefined(); + expect(tx.gas).toStrictEqual(sendOptions.gas); + expect(tx.gasPrice).toBeUndefined(); + expect(tx.from).toStrictEqual(sendOptions.from); + expect(tx.data).toStrictEqual(data); // padded data + + const newContract = contract.clone(); + newContract.options.address = deployedAddr; + + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Promise.resolve(newContract) as any; + }); + + const deployedContract = await contract + .deploy({ + data: GreeterBytecode, + arguments: ['My Greeting'], + }) + .send(sendOptions); + + expect(deployedContract).toBeDefined(); + expect(deployedContract.options.address).toStrictEqual(deployedAddr); + deploySpy.mockClear(); + }); + + // eslint-disable-next-line @typescript-eslint/require-await + it('should not deploy contract with empty data', async () => { + const contract = new Contract(GreeterAbi); + + expect(() => contract.deploy({ data: '' }).send(sendOptions)).toThrow( + 'contract creation without any data provided', + ); + }); + + // eslint-disable-next-line @typescript-eslint/require-await + it('send method on deployed contract should work', async () => { + const arg = 'Hello'; + const contract = new Contract(GreeterAbi); + + const spyTx = jest + .spyOn(eth, 'sendTransaction') + .mockImplementation((_objInstance, _tx) => { + const newContract = contract.clone(); + newContract.options.address = deployedAddr; + + if ( + _tx.data === + '0xa41368620000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000' + ) { + // eslint-disable-next-line + expect(_tx.to).toStrictEqual(deployedAddr); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Promise.resolve({ status: '0x1' }) as any; + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Promise.resolve(newContract) as any; + }); + + const deployedContract = await contract + .deploy({ + data: GreeterBytecode, + arguments: ['My Greeting'], + }) + .send(sendOptions); + + const receipt = await deployedContract.methods.setGreeting(arg).send(sendOptions); + expect(receipt.status).toBe('0x1'); + + spyTx.mockClear(); + }); + + it('call on deployed contract should decode result', async () => { + const arg = 'Hello'; + const encodedArg = + '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000'; + + const contract = new Contract(GreeterAbi); + + const spyTx = jest + .spyOn(eth, 'sendTransaction') + .mockImplementation((_objInstance, _tx) => { + const newContract = contract.clone(); + newContract.options.address = deployedAddr; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Promise.resolve(newContract) as any; + }); + + const spyEthCall = jest.spyOn(eth, 'call').mockImplementation((_objInstance, _tx) => { + expect(_tx.to).toStrictEqual(deployedAddr); + expect(_tx.data).toBe('0xcfae3217'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Promise.resolve(encodedArg) as any; // contract class should decode encodedArg + }); + + const deployedContract = await contract + .deploy({ + data: GreeterBytecode, + arguments: ['My Greeting'], + }) + .send(sendOptions); + + const res = await deployedContract.methods.greet().call(); + expect(res).toStrictEqual(arg); + + spyTx.mockClear(); + spyEthCall.mockClear(); + }); + + it('should clone pre deployed contract with address', () => { + const contract = new Contract( + sampleStorageContractABI, + '0x00000000219ab540356cBB839Cbe05303d7705Fa', + { gas: '0x97254' }, + ); + + const clonnedContract = contract.clone(); + + expect(JSON.stringify(contract)).toStrictEqual(JSON.stringify(clonnedContract)); + }); + + it('should clone new contract', () => { + const contract = new Contract(sampleStorageContractABI); + + const clonnedContract = contract.clone(); + expect(JSON.stringify(contract)).toStrictEqual(JSON.stringify(clonnedContract)); + }); + + it('defaults set and get should work', () => { + const contract = new Contract([], '0x00000000219ab540356cBB839Cbe05303d7705Fa'); + + const defaultAddr = '0xd7E30ae310C1D1800F5B641Baa7af95b2e1FD98C'; + expect(contract.defaultAccount).toBeUndefined(); + contract.defaultAccount = defaultAddr; + expect(contract.defaultAccount).toStrictEqual(defaultAddr); + + const defaultBlock = '0xC43A'; + expect(contract.defaultBlock).toBe('latest'); + contract.defaultBlock = defaultBlock; + expect(contract.defaultBlock).toStrictEqual(defaultBlock); + + const defaultHardfork = 'constantinople'; + expect(contract.defaultHardfork).toBe('london'); + contract.defaultHardfork = defaultHardfork; + expect(contract.defaultHardfork).toStrictEqual(defaultHardfork); + + const defaultCommon = { + customChain: { name: 'testnet', networkId: '5678', chainId: '5634' }, + baseChain: 'mainnet' as ValidChains, + hardFork: 'petersburg', + }; + expect(contract.defaultCommon).toBeUndefined(); + contract.defaultCommon = defaultCommon; + expect(contract.defaultCommon).toStrictEqual(defaultCommon); // TODO check if is being fixed in 5419, it should throw error as contract.defaultHardfork != contract.defaultCommon.hardfork + + const transactionBlockTimeout = 130; + expect(contract.transactionBlockTimeout).toBe(50); + contract.transactionBlockTimeout = transactionBlockTimeout; + expect(contract.transactionBlockTimeout).toStrictEqual(transactionBlockTimeout); + + const transactionConfirmationBlocks = 30; + expect(contract.transactionConfirmationBlocks).toBe(24); + contract.transactionConfirmationBlocks = transactionConfirmationBlocks; + expect(contract.transactionConfirmationBlocks).toStrictEqual( + transactionConfirmationBlocks, + ); + + const transactionPollingInterval = 1000; + expect(contract.transactionPollingInterval).toBe(1000); + contract.transactionPollingInterval = transactionPollingInterval; + expect(contract.transactionPollingInterval).toStrictEqual(transactionPollingInterval); + + const transactionPollingTimeout = 800000; + expect(contract.transactionPollingTimeout).toBe(750000); + contract.transactionPollingTimeout = transactionPollingTimeout; + expect(contract.transactionPollingTimeout).toStrictEqual(transactionPollingTimeout); + + const transactionReceiptPollingInterval = 2000; // its new in 4.x + expect(contract.transactionReceiptPollingInterval).toBe(1000); + contract.transactionReceiptPollingInterval = transactionReceiptPollingInterval; + expect(contract.transactionReceiptPollingInterval).toStrictEqual( + transactionReceiptPollingInterval, + ); + + const transactionConfirmationPollingInterval = 2501; // its new in 4.x + expect(contract.transactionConfirmationPollingInterval).toBe(1000); + contract.transactionConfirmationPollingInterval = + transactionConfirmationPollingInterval; + expect(contract.transactionConfirmationPollingInterval).toStrictEqual( + transactionConfirmationPollingInterval, + ); + + const transactionSendTimeout = 730000; // its new in 4.x + expect(contract.transactionSendTimeout).toBe(750000); + contract.transactionSendTimeout = transactionSendTimeout; + expect(contract.transactionSendTimeout).toStrictEqual(transactionSendTimeout); + + const blockHeaderTimeout = 12; + expect(contract.blockHeaderTimeout).toBe(10); + contract.blockHeaderTimeout = blockHeaderTimeout; + expect(contract.blockHeaderTimeout).toStrictEqual(blockHeaderTimeout); + + expect(contract.handleRevert).toBe(false); + contract.handleRevert = true; + expect(contract.handleRevert).toBe(true); + }); + + it('should set and get correct address', () => { + const addr = '0x1230B93ffd14F2F022039675fA3fc3A46eE4C701'; + const contract = new Contract( + [], + '', + { gas: '123' }, + { config: { defaultAccount: '0x00000000219ab540356cBB839Cbe05303d7705Fa' } }, + ); + + contract.options.address = addr; + expect(contract.options.address).toStrictEqual(addr); + }); + + it.skip('should set and get jsonInterface', () => { + const contract = new Contract( + sampleStorageContractABI, + '0x1230B93ffd14F2F022039675fA3fc3A46eE4C701', + { gas: '123' }, + { config: { defaultAccount: '0x00000000219ab540356cBB839Cbe05303d7705Fa' } }, + ); + + // contract.options.jsonInterface = ERC20TokenAbi; //TODO also check changing abi on the fly bug: https://github.com/web3/web3.js/issues/5474 + expect(contract.options.jsonInterface).toStrictEqual(sampleStorageContractABI); + }); + + it('getPastEvents with filter should work', async () => { + const contract = new Contract(GreeterAbi); + + const spyTx = jest + .spyOn(eth, 'sendTransaction') + .mockImplementation((_objInstance, _tx) => { + const newContract = contract.clone(); + newContract.options.address = deployedAddr; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Promise.resolve(newContract) as any; + }); + + const spyGetLogs = jest + .spyOn(eth, 'getLogs') + .mockImplementation((_objInstance, _params) => { + expect(_params.address).toStrictEqual(deployedAddr.toLocaleLowerCase()); + expect(_params.fromBlock).toStrictEqual(getLogsData.request.fromBlock); + expect(_params.toBlock).toStrictEqual(getLogsData.request.toBlock); + expect(_params.topics).toStrictEqual(getLogsData.request.topics); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Promise.resolve(getLogsData.response) as any; + }); + + const deployedContract = await contract + .deploy({ + data: GreeterBytecode, + arguments: ['My Greeting'], + }) + .send(sendOptions); + + const fromBlock = 'earliest'; + const toBlock = 'latest'; + const pastEvent = await deployedContract.getPastEvents(getPastEventsData.event as any, { + fromBlock, + toBlock, + }); + + expect(pastEvent).toStrictEqual(getPastEventsData.response); + spyTx.mockClear(); + spyGetLogs.mockClear(); + }); + + it('getPastEvents for all events should work', async () => { + const contract = new Contract(GreeterAbi); + + const spyTx = jest + .spyOn(eth, 'sendTransaction') + .mockImplementation((_objInstance, _tx) => { + const newContract = contract.clone(); + newContract.options.address = deployedAddr; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Promise.resolve(newContract) as any; + }); + + const spyGetLogs = jest + .spyOn(eth, 'getLogs') + .mockImplementation((_objInstance, _params) => { + expect(_params.address).toStrictEqual(deployedAddr.toLocaleLowerCase()); + expect(_params.fromBlock).toBeUndefined(); + expect(_params.toBlock).toBeUndefined(); + expect(_params.topics).toBeUndefined(); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Promise.resolve(AllGetPastEventsData.getLogsData) as any; // AllGetPastEventsData.getLogsData data test is for: assume two transactions sent to contract with contractInstance.methods.setGreeting("Hello") and contractInstance.methods.setGreeting("Another Greeting") + }); + + const deployedContract = await contract + .deploy({ + data: GreeterBytecode, + arguments: ['My Greeting'], + }) + .send(sendOptions); + + const pastEvent = await deployedContract.getPastEvents('allEvents'); + + expect(pastEvent).toStrictEqual(AllGetPastEventsData.response); + spyTx.mockClear(); + spyGetLogs.mockClear(); + }); + + it('estimateGas should work', async () => { + const arg = 'Hello'; + + const contract = new Contract( + GreeterAbi, + // {data: GreeterBytecode,} // TODO bug fix https://github.com/web3/web3.js/issues/5473 setting data via options causing this issue + ); + + const spyTx = jest + .spyOn(eth, 'sendTransaction') + .mockImplementation((_objInstance, _tx) => { + const newContract = contract.clone(); + newContract.options.address = deployedAddr; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Promise.resolve(newContract) as any; + }); + + const spyEstimateGas = jest + .spyOn(eth, 'estimateGas') + .mockImplementation((_objInstance, _tx, _block) => { + expect(_block).toBe('latest'); + expect(_tx.to).toStrictEqual(deployedAddr); + expect(_tx.from).toStrictEqual(sendOptions.from); + expect(_tx.data).toBe( + '0xa41368620000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000', + ); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Promise.resolve(BigInt(36916)) as any; + }); + + const deployedContract = await contract + .deploy({ + data: GreeterBytecode, + arguments: ['My Greeting'], + }) + .send(sendOptions); + + const result = await deployedContract.methods.setGreeting(arg).estimateGas(sendOptions); + expect(result).toStrictEqual(BigInt(36916)); + + spyTx.mockClear(); + spyEstimateGas.mockClear(); + }); + + it('contract method send without contract address should throw error', async () => { + const arg = 'Hello'; + + const contract = new Contract(GreeterAbi); + + expect(() => contract.methods.setGreeting(arg).send(sendOptions)).toThrow( + 'Contract address not specified', + ); + }); + + it('contract method send without from address should throw error', async () => { + const gas = '1000000'; + const sendOptionsSpecial = { gas }; + const arg = 'Hello'; + + const contract = new Contract(GreeterAbi); + contract.options.address = '0x12364916b10Ae90076dDa6dE756EE1395BB69ec2'; + + /* eslint-disable no-useless-escape */ + expect(() => contract.methods.setGreeting(arg).send(sendOptionsSpecial)).toThrow( + 'Contract "from" address not specified', + ); + }); + }); }); From 49424473b77ed4dd86f8e1fb371541ed94c9c5db Mon Sep 17 00:00:00 2001 From: Wyatt Barnes Date: Tue, 4 Oct 2022 16:01:37 -1000 Subject: [PATCH 8/8] Merge with 5091 --- packages/web3-core/src/web3_context.ts | 36 +++-- .../web3-core/test/unit/web3_context.test.ts | 1 - packages/web3-plugin-example/package.json | 15 +- .../web3-plugin-example/src/ERC20Token.ts | 141 ++++++++++++++++++ .../src/aggregator_v3_interface_abi.ts | 65 -------- .../src/contract_method_wrappers.ts | 94 ++++++++++++ .../src/custom_rpc_methods.ts | 47 ++++++ packages/web3-plugin-example/src/index.ts | 72 --------- .../unit/contract_method_wrappers.test.ts | 119 +++++++++++++++ .../test/unit/custom_rpc_methods.test.ts | 68 +++++++++ .../test/unit/plugin.test.ts | 39 ----- 11 files changed, 507 insertions(+), 190 deletions(-) create mode 100644 packages/web3-plugin-example/src/ERC20Token.ts delete mode 100644 packages/web3-plugin-example/src/aggregator_v3_interface_abi.ts create mode 100644 packages/web3-plugin-example/src/contract_method_wrappers.ts create mode 100644 packages/web3-plugin-example/src/custom_rpc_methods.ts create mode 100644 packages/web3-plugin-example/test/unit/contract_method_wrappers.test.ts create mode 100644 packages/web3-plugin-example/test/unit/custom_rpc_methods.test.ts delete mode 100644 packages/web3-plugin-example/test/unit/plugin.test.ts diff --git a/packages/web3-core/src/web3_context.ts b/packages/web3-core/src/web3_context.ts index 7652f34cd60..7b0b60b8ae4 100644 --- a/packages/web3-core/src/web3_context.ts +++ b/packages/web3-core/src/web3_context.ts @@ -289,10 +289,20 @@ export type TransactionBuilder = < }) => Promise; /** - * To implement a plugin that connects to a Node that does not fully implements the Ethereum JSON RPC standard methods, - * inherit from this Class. - * This class would also be used if the plugin was for non-Ethereum systems that could be supported later. - * However, for such implementation, more research and modifications would be needed. + * Extend this class when creating a plugin that either doesn't require {@link EthExecutionAPI}, + * or interacts with a RPC node that doesn't fully implement {@link EthExecutionAPI}. + * + * To add type support for RPC methods to the {@link Web3RequestManager}, + * define a {@link Web3APISpec} and pass it as a generic to Web3PluginBase like so: + * + * ```ts + * type CustomRpcApi = { + * custom_rpc_method: () => string; + * custom_rpc_method_with_parameters: (parameter1: string, parameter2: number) => string; + * }; + * + * class CustomPlugin extends Web3PluginBase {...} + * ``` */ export abstract class Web3PluginBase< API extends Web3APISpec = Web3APISpec, @@ -301,12 +311,20 @@ export abstract class Web3PluginBase< } /** - * To implement a plugin that connects to a Node that fully implements the Ethereum JSON RPC standard methods, - * inherit from this Class. - * And if the plugin provides more than the Ethereum JSON RPC standard methods, you can pass - * the specification of additional methods similar to the following: - * `export class CustomPlugin extends Web3EthPluginBase {...}` + * Extend this class when creating a plugin that makes use of {@link EthExecutionAPI}, + * or depends on other Web3 packages (such as `web3-eth-contract`) that depend on {@link EthExecutionAPI}. + * + * To add type support for RPC methods to the {@link Web3RequestManager} (in addition to {@link EthExecutionAPI}), + * define a {@link Web3APISpec} and pass it as a generic to Web3PluginBase like so: + * + * ```ts + * type CustomRpcApi = { + * custom_rpc_method: () => string; + * custom_rpc_method_with_parameters: (parameter1: string, parameter2: number) => string; + * }; * + * class CustomPlugin extends Web3PluginBase {...} + * ``` */ export abstract class Web3EthPluginBase extends Web3PluginBase< API & EthExecutionAPI diff --git a/packages/web3-core/test/unit/web3_context.test.ts b/packages/web3-core/test/unit/web3_context.test.ts index 44e58cf2c5c..1bedd82ba5b 100644 --- a/packages/web3-core/test/unit/web3_context.test.ts +++ b/packages/web3-core/test/unit/web3_context.test.ts @@ -18,7 +18,6 @@ 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 { Web3APISpec } from 'web3-types'; import { Web3Context, Web3PluginBase } from '../../src/web3_context'; import { Web3RequestManager } from '../../src/web3_request_manager'; diff --git a/packages/web3-plugin-example/package.json b/packages/web3-plugin-example/package.json index ea39a6a4f18..c1ff19b15b7 100644 --- a/packages/web3-plugin-example/package.json +++ b/packages/web3-plugin-example/package.json @@ -43,13 +43,20 @@ "jest-extended": "^3.0.1", "prettier": "^2.7.1", "ts-jest": "^28.0.7", - "typescript": "^4.7.4" - }, - "dependencies": { + "typescript": "^4.7.4", "web3": "^4.0.1-alpha.0", "web3-core": "^4.0.1-alpha.0", "web3-eth-abi": "^4.0.1-alpha.0", "web3-eth-contract": "^4.0.1-alpha.0", - "web3-types": "^0.1.1-alpha.0" + "web3-types": "^0.1.1-alpha.0", + "web3-utils": "^4.0.1-alpha.0" + }, + "peerDependencies": { + "web3": ">= 4.0.1-alpha.0 < 5", + "web3-core": ">= 4.0.1-alpha.0 < 5", + "web3-eth-abi": ">= 4.0.1-alpha.0 < 5", + "web3-eth-contract": ">= 4.0.1-alpha.0 < 5", + "web3-types": ">= 0.1.1-alpha.0 < 5", + "web3-utils": ">= ^4.0.1-alpha.0 < 5" } } diff --git a/packages/web3-plugin-example/src/ERC20Token.ts b/packages/web3-plugin-example/src/ERC20Token.ts new file mode 100644 index 00000000000..b13578a62d1 --- /dev/null +++ b/packages/web3-plugin-example/src/ERC20Token.ts @@ -0,0 +1,141 @@ +/* +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 . +*/ +export const ERC20TokenAbi = [ + { + inputs: [{ internalType: 'uint256', name: 'initialSupply', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'owner', type: 'address' }, + { indexed: true, internalType: 'address', name: 'spender', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'value', type: 'uint256' }, + ], + name: 'Approval', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'from', type: 'address' }, + { indexed: true, internalType: 'address', name: 'to', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'value', type: 'uint256' }, + ], + name: 'Transfer', + type: 'event', + }, + { + inputs: [ + { internalType: 'address', name: 'owner', type: 'address' }, + { internalType: 'address', name: 'spender', type: 'address' }, + ], + name: 'allowance', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'spender', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + ], + name: 'approve', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'account', type: 'address' }], + name: 'balanceOf', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'decimals', + outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'spender', type: 'address' }, + { internalType: 'uint256', name: 'subtractedValue', type: 'uint256' }, + ], + name: 'decreaseAllowance', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'spender', type: 'address' }, + { internalType: 'uint256', name: 'addedValue', type: 'uint256' }, + ], + name: 'increaseAllowance', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'name', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'symbol', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalSupply', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + ], + name: 'transfer', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'from', type: 'address' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + ], + name: 'transferFrom', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const; +export const ERC20TokenBytecode = + '0x60806040523480156200001157600080fd5b50604051620017a8380380620017a8833981810160405281019062000037919062000362565b6040518060400160405280600481526020017f476f6c64000000000000000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f474c4400000000000000000000000000000000000000000000000000000000008152508160039080519060200190620000bb92919062000272565b508060049080519060200190620000d492919062000272565b505050620000e93382620000f060201b60201c565b5062000535565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160362000162576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200015990620003f5565b60405180910390fd5b62000176600083836200026860201b60201c565b80600260008282546200018a919062000446565b92505081905550806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254620001e1919062000446565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051620002489190620004b4565b60405180910390a362000264600083836200026d60201b60201c565b5050565b505050565b505050565b828054620002809062000500565b90600052602060002090601f016020900481019282620002a45760008555620002f0565b82601f10620002bf57805160ff1916838001178555620002f0565b82800160010185558215620002f0579182015b82811115620002ef578251825591602001919060010190620002d2565b5b509050620002ff919062000303565b5090565b5b808211156200031e57600081600090555060010162000304565b5090565b600080fd5b6000819050919050565b6200033c8162000327565b81146200034857600080fd5b50565b6000815190506200035c8162000331565b92915050565b6000602082840312156200037b576200037a62000322565b5b60006200038b848285016200034b565b91505092915050565b600082825260208201905092915050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b6000620003dd601f8362000394565b9150620003ea82620003a5565b602082019050919050565b600060208201905081810360008301526200041081620003ce565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000620004538262000327565b9150620004608362000327565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111562000498576200049762000417565b5b828201905092915050565b620004ae8162000327565b82525050565b6000602082019050620004cb6000830184620004a3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200051957607f821691505b6020821081036200052f576200052e620004d1565b5b50919050565b61126380620005456000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633950935111610071578063395093511461016857806370a082311461019857806395d89b41146101c8578063a457c2d7146101e6578063a9059cbb14610216578063dd62ed3e14610246576100a9565b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100fc57806323b872dd1461011a578063313ce5671461014a575b600080fd5b6100b6610276565b6040516100c39190610b1e565b60405180910390f35b6100e660048036038101906100e19190610bd9565b610308565b6040516100f39190610c34565b60405180910390f35b61010461032b565b6040516101119190610c5e565b60405180910390f35b610134600480360381019061012f9190610c79565b610335565b6040516101419190610c34565b60405180910390f35b610152610364565b60405161015f9190610ce8565b60405180910390f35b610182600480360381019061017d9190610bd9565b61036d565b60405161018f9190610c34565b60405180910390f35b6101b260048036038101906101ad9190610d03565b6103a4565b6040516101bf9190610c5e565b60405180910390f35b6101d06103ec565b6040516101dd9190610b1e565b60405180910390f35b61020060048036038101906101fb9190610bd9565b61047e565b60405161020d9190610c34565b60405180910390f35b610230600480360381019061022b9190610bd9565b6104f5565b60405161023d9190610c34565b60405180910390f35b610260600480360381019061025b9190610d30565b610518565b60405161026d9190610c5e565b60405180910390f35b60606003805461028590610d9f565b80601f01602080910402602001604051908101604052809291908181526020018280546102b190610d9f565b80156102fe5780601f106102d3576101008083540402835291602001916102fe565b820191906000526020600020905b8154815290600101906020018083116102e157829003601f168201915b5050505050905090565b60008061031361059f565b90506103208185856105a7565b600191505092915050565b6000600254905090565b60008061034061059f565b905061034d858285610770565b6103588585856107fc565b60019150509392505050565b60006012905090565b60008061037861059f565b905061039981858561038a8589610518565b6103949190610dff565b6105a7565b600191505092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6060600480546103fb90610d9f565b80601f016020809104026020016040519081016040528092919081815260200182805461042790610d9f565b80156104745780601f1061044957610100808354040283529160200191610474565b820191906000526020600020905b81548152906001019060200180831161045757829003601f168201915b5050505050905090565b60008061048961059f565b905060006104978286610518565b9050838110156104dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104d390610ec7565b60405180910390fd5b6104e982868684036105a7565b60019250505092915050565b60008061050061059f565b905061050d8185856107fc565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610616576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161060d90610f59565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610685576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161067c90610feb565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516107639190610c5e565b60405180910390a3505050565b600061077c8484610518565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146107f657818110156107e8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107df90611057565b60405180910390fd5b6107f584848484036105a7565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361086b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610862906110e9565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036108da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d19061117b565b60405180910390fd5b6108e5838383610a7b565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561096b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109629061120d565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546109fe9190610dff565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610a629190610c5e565b60405180910390a3610a75848484610a80565b50505050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610abf578082015181840152602081019050610aa4565b83811115610ace576000848401525b50505050565b6000601f19601f8301169050919050565b6000610af082610a85565b610afa8185610a90565b9350610b0a818560208601610aa1565b610b1381610ad4565b840191505092915050565b60006020820190508181036000830152610b388184610ae5565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610b7082610b45565b9050919050565b610b8081610b65565b8114610b8b57600080fd5b50565b600081359050610b9d81610b77565b92915050565b6000819050919050565b610bb681610ba3565b8114610bc157600080fd5b50565b600081359050610bd381610bad565b92915050565b60008060408385031215610bf057610bef610b40565b5b6000610bfe85828601610b8e565b9250506020610c0f85828601610bc4565b9150509250929050565b60008115159050919050565b610c2e81610c19565b82525050565b6000602082019050610c496000830184610c25565b92915050565b610c5881610ba3565b82525050565b6000602082019050610c736000830184610c4f565b92915050565b600080600060608486031215610c9257610c91610b40565b5b6000610ca086828701610b8e565b9350506020610cb186828701610b8e565b9250506040610cc286828701610bc4565b9150509250925092565b600060ff82169050919050565b610ce281610ccc565b82525050565b6000602082019050610cfd6000830184610cd9565b92915050565b600060208284031215610d1957610d18610b40565b5b6000610d2784828501610b8e565b91505092915050565b60008060408385031215610d4757610d46610b40565b5b6000610d5585828601610b8e565b9250506020610d6685828601610b8e565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610db757607f821691505b602082108103610dca57610dc9610d70565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610e0a82610ba3565b9150610e1583610ba3565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115610e4a57610e49610dd0565b5b828201905092915050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000610eb1602583610a90565b9150610ebc82610e55565b604082019050919050565b60006020820190508181036000830152610ee081610ea4565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000610f43602483610a90565b9150610f4e82610ee7565b604082019050919050565b60006020820190508181036000830152610f7281610f36565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000610fd5602283610a90565b9150610fe082610f79565b604082019050919050565b6000602082019050818103600083015261100481610fc8565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b6000611041601d83610a90565b915061104c8261100b565b602082019050919050565b6000602082019050818103600083015261107081611034565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b60006110d3602583610a90565b91506110de82611077565b604082019050919050565b60006020820190508181036000830152611102816110c6565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000611165602383610a90565b915061117082611109565b604082019050919050565b6000602082019050818103600083015261119481611158565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b60006111f7602683610a90565b91506112028261119b565b604082019050919050565b60006020820190508181036000830152611226816111ea565b905091905056fea264697066735822122039c50588d39e359807efa398bd1b3aff1b082ef738dddcfa8475048e11f9024e64736f6c634300080d0033'; diff --git a/packages/web3-plugin-example/src/aggregator_v3_interface_abi.ts b/packages/web3-plugin-example/src/aggregator_v3_interface_abi.ts deleted file mode 100644 index 6c22f5f5b5c..00000000000 --- a/packages/web3-plugin-example/src/aggregator_v3_interface_abi.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* -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 . -*/ -export const AggregatorV3InterfaceABI = [ - { - inputs: [], - name: 'decimals', - outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'description', - outputs: [{ internalType: 'string', name: '', type: 'string' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [{ internalType: 'uint80', name: '_roundId', type: 'uint80' }], - name: 'getRoundData', - outputs: [ - { internalType: 'uint80', name: 'roundId', type: 'uint80' }, - { internalType: 'int256', name: 'answer', type: 'int256' }, - { internalType: 'uint256', name: 'startedAt', type: 'uint256' }, - { internalType: 'uint256', name: 'updatedAt', type: 'uint256' }, - { internalType: 'uint80', name: 'answeredInRound', type: 'uint80' }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'latestRoundData', - outputs: [ - { internalType: 'uint80', name: 'roundId', type: 'uint80' }, - { internalType: 'int256', name: 'answer', type: 'int256' }, - { internalType: 'uint256', name: 'startedAt', type: 'uint256' }, - { internalType: 'uint256', name: 'updatedAt', type: 'uint256' }, - { internalType: 'uint80', name: 'answeredInRound', type: 'uint80' }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'version', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - stateMutability: 'view', - type: 'function', - }, -] as const; diff --git a/packages/web3-plugin-example/src/contract_method_wrappers.ts b/packages/web3-plugin-example/src/contract_method_wrappers.ts new file mode 100644 index 00000000000..674cb672ad7 --- /dev/null +++ b/packages/web3-plugin-example/src/contract_method_wrappers.ts @@ -0,0 +1,94 @@ +/* +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 'web3'; +import { Web3Context, Web3PluginBase } from 'web3-core'; +import { ContractAbi } from 'web3-eth-abi'; +import Contract from 'web3-eth-contract'; +import { Address, Numbers } from 'web3-types'; +import { DataFormat, DEFAULT_RETURN_FORMAT, format, numberToHex } from 'web3-utils'; + +import { ERC20TokenAbi } from './ERC20Token'; + +export class ContractMethodWrappersPlugin extends Web3PluginBase { + public pluginNamespace = 'contractMethodWrappersPlugin'; + + // This should be private, but it's public so _contract.requestManager.send can + // be mocked in contract_method_wrappers.test.ts + public readonly _contract: Contract; + + public constructor(abi: ContractAbi, address: Address) { + super(); + this._contract = new Contract(abi, address); + } + + /** + * This method overrides the inherited `link` method from `Web3PluginBase` + * to add to a configured `RequestManager` to our Contract instance + * when `Web3.registerPlugin` is called. + * + * @param parentContext - The context to be added to the instance of `ChainlinkPlugin`, + * and by extension, the instance of `Contract`. + */ + public link(parentContext: Web3Context) { + super.link(parentContext); + this._contract.link(parentContext); + } + + public async getFormattedBalance( + address: Address, + returnFormat: ReturnFormat, + ) { + return format( + { eth: 'unit' }, + await this._contract.methods.balanceOf(address).call(), + returnFormat, + ); + } + + public async transferAndGetBalances( + sender: Address, + recipient: Address, + amount: Numbers, + returnFormat?: ReturnFormat, + ) { + await this._contract.methods + .transfer(recipient, numberToHex(amount)) + .send({ from: sender }); + return { + sender: { + address: sender, + balance: await this.getFormattedBalance( + sender, + returnFormat ?? DEFAULT_RETURN_FORMAT, + ), + }, + recipient: { + address: recipient, + balance: await this.getFormattedBalance( + recipient, + returnFormat ?? DEFAULT_RETURN_FORMAT, + ), + }, + }; + } +} + +declare module 'web3' { + interface Web3 { + contractMethodWrappersPlugin: ContractMethodWrappersPlugin; + } +} diff --git a/packages/web3-plugin-example/src/custom_rpc_methods.ts b/packages/web3-plugin-example/src/custom_rpc_methods.ts new file mode 100644 index 00000000000..d6680b08e44 --- /dev/null +++ b/packages/web3-plugin-example/src/custom_rpc_methods.ts @@ -0,0 +1,47 @@ +/* +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 'web3'; +import { Web3PluginBase } from 'web3-core'; + +type CustomRpcApi = { + custom_rpc_method: () => string; + custom_rpc_method_with_parameters: (parameter1: string, parameter2: number) => string; +}; + +export class CustomRpcMethodsPlugin extends Web3PluginBase { + public pluginNamespace = 'customRpcMethods'; + + public async customRpcMethod() { + return this.requestManager.send({ + method: 'custom_rpc_method', + params: [], + }); + } + + public async customRpcMethodWithParameters(parameter1: string, parameter2: number) { + return this.requestManager.send({ + method: 'custom_rpc_method_with_parameters', + params: [parameter1, parameter2], + }); + } +} + +declare module 'web3' { + interface Web3 { + customRpcMethods: CustomRpcMethodsPlugin; + } +} diff --git a/packages/web3-plugin-example/src/index.ts b/packages/web3-plugin-example/src/index.ts index f203d7167a5..0f615ca7243 100644 --- a/packages/web3-plugin-example/src/index.ts +++ b/packages/web3-plugin-example/src/index.ts @@ -14,75 +14,3 @@ 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 { ContractAbi } from 'web3-eth-abi'; -import Contract from 'web3-eth-contract'; -import { Web3EthPluginBase } from 'web3-core'; -import { Address, BlockNumberOrTag } from 'web3-types'; -// @ts-expect-error 'Web3' is declared but its value is never read. -import { Web3 } from 'web3'; - -import { AggregatorV3InterfaceABI } from './aggregator_v3_interface_abi'; - -interface Price { - roundId: string; - answer: string; - startedAt: string; - updatedAt: string; - answeredInRound: string; -} - -export type ChainlinkPluginAPI = { - customJsonRpcMethod: () => Promise; -}; - -export class ChainlinkPlugin extends Web3EthPluginBase { - public pluginNamespace = 'chainlink'; - - protected readonly _contract: Contract; - - public constructor(abi: ContractAbi, address: Address) { - super(); - this._contract = new Contract(abi, address); - } - - /** - * An example for calling a custom JSON RPC function called customJsonRpcMethod - * Supposing that the connected Ethereum Node provides non-standard function called `customJsonRpcMethod` - */ - public async customApi() { - return this._requestManager.send({ - method: 'customJsonRpcMethod', - params: [], - }); - } - - /** - * An example for providing a method that calls a smart contract and do possibly do some processing - * @returns a promise to Price - */ - public async getPrice(): Promise { - // call any function(s) or smart contract method(s) - if (this._contract.currentProvider === undefined) this._contract.link(this); - const price = await this._contract.methods.latestRoundData().call(); - // do whatever processing needed and return - return price as unknown as Price; - } - - /** - * Just to show how the standard JSON RPC methods could be called as usual - * @returns a promise to a string - */ - public async getBalance(address: Address, blockNumber: BlockNumberOrTag) { - // call any standard - return this._requestManager.send({ - method: 'eth_getBalance', - params: [address, blockNumber], - }); - } -} - -declare module 'web3' { - interface Web3 { - chainlink: ChainlinkPlugin; - } -} diff --git a/packages/web3-plugin-example/test/unit/contract_method_wrappers.test.ts b/packages/web3-plugin-example/test/unit/contract_method_wrappers.test.ts new file mode 100644 index 00000000000..0366d4aa510 --- /dev/null +++ b/packages/web3-plugin-example/test/unit/contract_method_wrappers.test.ts @@ -0,0 +1,119 @@ +/* +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 Web3 from 'web3'; +import { DEFAULT_RETURN_FORMAT } from 'web3-utils'; + +import { ContractMethodWrappersPlugin } from '../../src/contract_method_wrappers'; +import { ERC20TokenAbi } from '../../src/ERC20Token'; + +describe('CustomRpcMethodsPlugin Tests', () => { + it('should register CustomRpcMethodsPlugin plugin', () => { + const web3 = new Web3('http://127.0.0.1:8545'); + web3.registerPlugin( + new ContractMethodWrappersPlugin( + ERC20TokenAbi, + '0xdAC17F958D2ee523a2206206994597C13D831ec7', + ), + ); + expect(web3.contractMethodWrappersPlugin).toBeDefined(); + }); + + describe('CustomRpcMethodsPlugin methods tests', () => { + const contractAddress = '0xdAC17F958D2ee523a2206206994597C13D831ec7'; + const sender = '0x8da5e39ec14b57fb9bcd9aa2b4500e909119795d'; + const recipient = '0x4f641def1e7845caab95ac717c80416082430d0d'; + const amount = BigInt(42); + const expectedSenderBalance = + '0x0000000000000000000000000000000000000000000000000000000000000280'; + const expectedRecipientBalance = + '0x0000000000000000000000000000000000000000000000000000000000000120'; + const requestManagerSendSpy = jest.fn(); + + let web3: Web3; + + beforeAll(() => { + web3 = new Web3('http://127.0.0.1:8545'); + web3.registerPlugin(new ContractMethodWrappersPlugin(ERC20TokenAbi, contractAddress)); + web3.contractMethodWrappersPlugin._contract.requestManager.send = requestManagerSendSpy; + }); + + it('should call contractMethodWrappersPlugin.getFormattedBalance with expected RPC object', async () => { + requestManagerSendSpy.mockResolvedValueOnce(expectedSenderBalance); + + await web3.contractMethodWrappersPlugin.getFormattedBalance( + sender, + DEFAULT_RETURN_FORMAT, + ); + expect(requestManagerSendSpy).toHaveBeenCalledWith({ + method: 'eth_call', + params: [ + { + data: '0x70a082310000000000000000000000008da5e39ec14b57fb9bcd9aa2b4500e909119795d', + to: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + }, + 'latest', + ], + }); + }); + + it('should call CustomRpcMethodsPlugin.customRpcMethodWithParameters with expected RPC object', async () => { + const expectedGasPrice = '0x1ca14bd70'; + const expectedTransactionHash = + '0xc41b9a4f654c44552e135f770945916f57c069b80326f9a5f843e613491ab6b1'; + + requestManagerSendSpy.mockResolvedValueOnce(expectedGasPrice); + // Not sure what's being mocked here + requestManagerSendSpy.mockResolvedValueOnce('0x1'); + requestManagerSendSpy.mockResolvedValueOnce(expectedTransactionHash); + // Not sure what's being mocked here + requestManagerSendSpy.mockResolvedValueOnce('0x42'); + // Not sure what's being mocked here + requestManagerSendSpy.mockResolvedValueOnce('0x42'); + requestManagerSendSpy.mockResolvedValueOnce(expectedSenderBalance); + requestManagerSendSpy.mockResolvedValueOnce(expectedRecipientBalance); + + const balances = await web3.contractMethodWrappersPlugin.transferAndGetBalances( + sender, + recipient, + amount, + ); + expect(requestManagerSendSpy).toHaveBeenCalledWith({ + method: 'eth_sendTransaction', + params: [ + { + data: '0xa9059cbb0000000000000000000000004f641def1e7845caab95ac717c80416082430d0d000000000000000000000000000000000000000000000000000000000000002a', + from: sender, + gasPrice: expectedGasPrice, + maxFeePerGas: undefined, + maxPriorityFeePerGas: undefined, + to: contractAddress, + }, + ], + }); + expect(balances).toStrictEqual({ + sender: { + address: sender, + balance: BigInt(expectedSenderBalance), + }, + recipient: { + address: recipient, + balance: BigInt(expectedRecipientBalance), + }, + }); + }); + }); +}); diff --git a/packages/web3-plugin-example/test/unit/custom_rpc_methods.test.ts b/packages/web3-plugin-example/test/unit/custom_rpc_methods.test.ts new file mode 100644 index 00000000000..349216d92ed --- /dev/null +++ b/packages/web3-plugin-example/test/unit/custom_rpc_methods.test.ts @@ -0,0 +1,68 @@ +/* +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 Web3 from 'web3'; + +import { CustomRpcMethodsPlugin } from '../../src/custom_rpc_methods'; + +describe('CustomRpcMethodsPlugin Tests', () => { + it('should register CustomRpcMethodsPlugin plugin', () => { + const web3 = new Web3('http://127.0.0.1:8545'); + web3.registerPlugin(new CustomRpcMethodsPlugin()); + // Both CustomRpcMethodsPlugin and ContractMethodWrappersPlugin + // redeclare the web3 module, and this seems to confuse the TypeScript server + // @ts-expect-error Unsafe call of an `any` typed value + expect(web3.customRpcMethods).toBeDefined(); + }); + + describe('CustomRpcMethodsPlugin methods tests', () => { + const requestManagerSendSpy = jest.fn(); + + let web3: Web3; + + beforeAll(() => { + web3 = new Web3('http://127.0.0.1:8545'); + web3.registerPlugin(new CustomRpcMethodsPlugin()); + web3.requestManager.send = requestManagerSendSpy; + }); + + it('should call CustomRpcMethodsPlugin.customRpcMethod with expected RPC object', async () => { + // Both CustomRpcMethodsPlugin and ContractMethodWrappersPlugin + // redeclare the web3 module, and this seems to confuse the TypeScript server + // @ts-expect-error Unsafe call of an `any` typed value + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + await web3.customRpcMethods.customRpcMethod(); + expect(requestManagerSendSpy).toHaveBeenCalledWith({ + method: 'custom_rpc_method', + params: [], + }); + }); + + it('should call CustomRpcMethodsPlugin.customRpcMethodWithParameters with expected RPC object', async () => { + const parameter1 = 'myString'; + const parameter2 = 42; + // Both CustomRpcMethodsPlugin and ContractMethodWrappersPlugin + // redeclare the web3 module, and this seems to confuse the TypeScript server + // @ts-expect-error Unsafe call of an `any` typed value + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + await web3.customRpcMethods.customRpcMethodWithParameters(parameter1, parameter2); + expect(requestManagerSendSpy).toHaveBeenCalledWith({ + method: 'custom_rpc_method_with_parameters', + params: [parameter1, parameter2], + }); + }); + }); +}); diff --git a/packages/web3-plugin-example/test/unit/plugin.test.ts b/packages/web3-plugin-example/test/unit/plugin.test.ts deleted file mode 100644 index 353734a3f3b..00000000000 --- a/packages/web3-plugin-example/test/unit/plugin.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* -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 Web3 from 'web3'; - -import { ChainlinkPlugin } from '../../src/index'; -import { AggregatorV3InterfaceABI } from '../../src/aggregator_v3_interface_abi'; - -const aggregatorAddress = '0xECe365B379E1dD183B20fc5f022230C044d51404'; - -describe('Chainlink Plugin Tests', () => { - it('should register ChainlinkPlugin and make the getPrice call', async () => { - const web3 = new Web3('https://rpc.ankr.com/eth_rinkeby'); - web3.registerPlugin(new ChainlinkPlugin(AggregatorV3InterfaceABI, aggregatorAddress)); - const price = await web3.chainlink.getPrice(); - expect(Object.keys(price)).toEqual( - expect.arrayContaining([ - 'roundId', - 'answer', - 'startedAt', - 'updatedAt', - 'answeredInRound', - ]), - ); - }); -});