From 12d7e180448411dc0d6bd2b86c2d16f007a3fa99 Mon Sep 17 00:00:00 2001 From: Oleksii Kosynskyi Date: Thu, 24 Oct 2024 09:44:50 -0400 Subject: [PATCH] Fix Contract methods input param type any[] (#7340) * fix types * fix * Update type.test.ts * fix tests * fix test --- packages/web3-eth-contract/src/contract.ts | 6 +- .../web3-eth-contract/test/fixtures/erc20.ts | 2 +- .../contract_overloaded_methods.test.ts | 5 +- .../test/unit/contract_typing.test.ts | 6 +- .../web3-eth-contract/test/unit/type.test.ts | 79 +++++++++++++++++++ packages/web3-types/src/eth_abi_types.ts | 4 +- 6 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 packages/web3-eth-contract/test/unit/type.test.ts diff --git a/packages/web3-eth-contract/src/contract.ts b/packages/web3-eth-contract/src/contract.ts index 60602497e65..cff3a440bfe 100644 --- a/packages/web3-eth-contract/src/contract.ts +++ b/packages/web3-eth-contract/src/contract.ts @@ -127,7 +127,11 @@ type ContractBoundMethod< Abi extends AbiFunctionFragment, Method extends ContractMethod = ContractMethod, > = ( - ...args: Method['Inputs'] extends undefined | unknown ? any[] : Method['Inputs'] + ...args: Abi extends undefined + ? any[] + : Method['Inputs'] extends never + ? any[] + : Method['Inputs'] ) => Method['Abi']['stateMutability'] extends 'payable' | 'pure' ? PayableMethodObject : NonPayableMethodObject; diff --git a/packages/web3-eth-contract/test/fixtures/erc20.ts b/packages/web3-eth-contract/test/fixtures/erc20.ts index 18b51748a4e..30168dc4505 100644 --- a/packages/web3-eth-contract/test/fixtures/erc20.ts +++ b/packages/web3-eth-contract/test/fixtures/erc20.ts @@ -22,7 +22,7 @@ import { ContractEventOptions, PayableMethodObject, NonPayableMethodObject } fro export interface Erc20Interface { methods: { [key: string]: ( - ...args: ReadonlyArray + ...args: any[] ) => | PayableMethodObject, ReadonlyArray> | NonPayableMethodObject, ReadonlyArray>; diff --git a/packages/web3-eth-contract/test/integration/local_account/contract_overloaded_methods.test.ts b/packages/web3-eth-contract/test/integration/local_account/contract_overloaded_methods.test.ts index 96985cda3a7..0c905e8ece2 100644 --- a/packages/web3-eth-contract/test/integration/local_account/contract_overloaded_methods.test.ts +++ b/packages/web3-eth-contract/test/integration/local_account/contract_overloaded_methods.test.ts @@ -102,11 +102,14 @@ describe('contract ERC721 overloaded functions', () => { }); it('transferFrom with 3 invalid arguments', () => { - expect(() => contractDeployed.methods.safeTransferFrom(1, 2, 3)).toThrow('Web3 validator'); + expect(() => contractDeployed.methods.safeTransferFrom('1', '2', 3)).toThrow( + 'Web3 validator', + ); }); it('transferFrom with 2 arguments', () => { expect(() => + // @ts-expect-error invalid arguments so ts will throw an error contractDeployed.methods.safeTransferFrom(localAccount.address, localAccount.address), ).toThrow('Web3 validator'); }); diff --git a/packages/web3-eth-contract/test/unit/contract_typing.test.ts b/packages/web3-eth-contract/test/unit/contract_typing.test.ts index 79f66bdca29..c5190e10cda 100644 --- a/packages/web3-eth-contract/test/unit/contract_typing.test.ts +++ b/packages/web3-eth-contract/test/unit/contract_typing.test.ts @@ -37,8 +37,10 @@ describe('contract typing', () => { ]); typecheck('should allow any input params', () => [ - expectTypeOf>().toBe(), - expectTypeOf>().toBe(), + expectTypeOf>().toBe< + any[] | [] + >(), + expectTypeOf>().toBe(), ]); }); describe('custom abi', () => { diff --git a/packages/web3-eth-contract/test/unit/type.test.ts b/packages/web3-eth-contract/test/unit/type.test.ts new file mode 100644 index 00000000000..0b7082ca1de --- /dev/null +++ b/packages/web3-eth-contract/test/unit/type.test.ts @@ -0,0 +1,79 @@ +/* +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 { Contract } from '../../src'; + +describe('Contract method types', () => { + it('contract method params types test', () => { + const abiAsConst = [ + { + inputs: [ + { internalType: 'uint256', name: 'testArg1', type: 'uint256' }, + { internalType: 'uint256', name: 'testArg2', type: 'uint256' }, + ], + name: 'testWithParams', + outputs: [{ internalType: 'uint256', name: 'testRes1', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'testWithoutParams', + outputs: [{ internalType: 'uint256', name: 'testRes1', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, + ] as const; + + const abiAsArray = [ + { + inputs: [ + { internalType: 'uint256', name: 'testArg1', type: 'uint256' }, + { internalType: 'uint256', name: 'testArg2', type: 'uint256' }, + ], + name: 'testWithParams', + outputs: [{ internalType: 'uint256', name: 'testRes1', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'testWithoutParams', + outputs: [{ internalType: 'uint256', name: 'testRes1', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, + ]; + + // abi as const + const contract = new Contract(abiAsConst); + contract.methods.testWithParams(1, 2); // no ts error - works as expected + // @ts-expect-error ts compiler error + expect(() => contract.methods.testWithParams()).toThrow(); // ts error - works as expected + // @ts-expect-error ts compiler error + contract.methods.testWithoutParams(1, 2); // ts error - works as expected + contract.methods.testWithoutParams(); // no ts error - works as expected + + // abi as usual array type + const contract2 = new Contract(abiAsArray); + // because we do not know exact type without const or provided type + contract2.methods.testWithParams(1, 2); // no ts error - works as expected + contract2.methods.testWithoutParams(); // no ts error - works as expected + contract2.methods.testWithoutParams(1, 2); // no ts error - works as expected + expect(() => contract2.methods.testWithParams()).toThrow(); // no ts error - works as expected + }); +}); diff --git a/packages/web3-types/src/eth_abi_types.ts b/packages/web3-types/src/eth_abi_types.ts index 0daaa2db877..06c56afa14f 100644 --- a/packages/web3-types/src/eth_abi_types.ts +++ b/packages/web3-types/src/eth_abi_types.ts @@ -320,7 +320,9 @@ export type ContractMethodOutputParameters : []; export type ContractMethodInputParameters | undefined> = - Params extends readonly [] + Params extends undefined + ? any[] + : Params extends readonly [] ? [] : Params extends readonly [infer H, ...infer R] ? H extends AbiParameter