From 19381696788b6f460c3997ab989a8b76636683a9 Mon Sep 17 00:00:00 2001 From: fredlacs <32464905+fredlacs@users.noreply.github.com> Date: Sun, 1 May 2022 15:15:10 +0100 Subject: [PATCH 1/5] Add initial support for custom errors --- contracts/v0.8.9/CustomError.sol | 26 ++++ .../test/CustomError.test.ts | 48 +++++++ packages/target-ethers-v5-test/test/common.ts | 2 +- .../v0.8.9/CustomError/TestToken__factory.ts | 60 +++++++++ .../factories/v0.8.9/CustomError/index.ts | 4 + .../factories/v0.8.9/CustomError__factory.ts | 65 +++++++++ .../types/factories/v0.8.9/index.ts | 1 + packages/target-ethers-v5-test/types/index.ts | 2 + .../types/v0.6.4/DataTypesInput.ts | 2 + .../types/v0.6.4/DataTypesPure.ts | 2 + .../types/v0.6.4/DataTypesView.ts | 2 + .../types/v0.6.4/Events.ts | 2 + .../types/v0.6.4/Issue428_Reproduction/A.ts | 2 + .../types/v0.6.4/Issue428_Reproduction/B.ts | 2 + .../types/v0.6.4/Library/Lib.ts | 2 + .../types/v0.6.4/LibraryConsumer.ts | 2 + .../v0.6.4/Name-Mangling/NAME12mangling.ts | 2 + .../types/v0.6.4/Overloads.ts | 2 + .../types/v0.6.4/Payable/Payable.ts | 2 + .../types/v0.6.4/Payable/PayableFactory.ts | 2 + .../types/v0.8.9/CustomError.ts | 123 ++++++++++++++++++ .../types/v0.8.9/ISimpleToken.ts | 2 + .../types/v0.8.9/Issue552_Reproduction.ts | 2 + .../v0.8.9/KingOfTheHill/KingOfTheHill.ts | 12 ++ .../v0.8.9/KingOfTheHill/Withdrawable.ts | 2 + .../types/v0.8.9/Rarity/ERC721.ts | 2 + .../types/v0.8.9/Rarity/ERC721Enumerable.ts | 2 + .../types/v0.8.9/Rarity/IERC721.ts | 2 + .../types/v0.8.9/Rarity/IERC721Enumerable.ts | 2 + .../types/v0.8.9/Rarity/IERC721Receiver.ts | 2 + .../types/v0.8.9/Rarity/Rarity.ts | 2 + .../types/v0.8.9/SimpleToken.ts | 2 + .../types/v0.8.9/index.ts | 1 + .../types/v0.8.9/nested/a/NestedLibrary.ts | 2 + .../types/v0.8.9/nested/b/NestedLibrary.ts | 2 + .../target-ethers-v5/src/codegen/errors.ts | 64 +++++++++ .../target-ethers-v5/src/codegen/index.ts | 21 ++- .../types/v0.8.9/CustomError.ts | 40 ++++++ .../types/v0.8.9/CustomError/TestToken.ts | 40 ++++++ .../types/v0.8.9/CustomError/index.ts | 4 + .../target-web3-v1-test/types/v0.8.9/index.ts | 1 + packages/typechain/src/parser/abiParser.ts | 27 ++++ packages/typechain/src/utils/signatures.ts | 4 +- .../typechain/test/parser/abiParser.test.ts | 33 +++++ 44 files changed, 621 insertions(+), 5 deletions(-) create mode 100644 contracts/v0.8.9/CustomError.sol create mode 100644 packages/target-ethers-v5-test/test/CustomError.test.ts create mode 100644 packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError/TestToken__factory.ts create mode 100644 packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError/index.ts create mode 100644 packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError__factory.ts create mode 100644 packages/target-ethers-v5-test/types/v0.8.9/CustomError.ts create mode 100644 packages/target-ethers-v5/src/codegen/errors.ts create mode 100644 packages/target-web3-v1-test/types/v0.8.9/CustomError.ts create mode 100644 packages/target-web3-v1-test/types/v0.8.9/CustomError/TestToken.ts create mode 100644 packages/target-web3-v1-test/types/v0.8.9/CustomError/index.ts diff --git a/contracts/v0.8.9/CustomError.sol b/contracts/v0.8.9/CustomError.sol new file mode 100644 index 000000000..4508b5a60 --- /dev/null +++ b/contracts/v0.8.9/CustomError.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.4; + +/// Insufficient balance for transfer. Needed `required` but only +/// `available` available. +/// @param available balance available. +/// @param required requested amount to transfer. +error InsufficientBalance(uint256 available, uint256 required); + +/// @notice example contract from https://blog.soliditylang.org/2021/04/21/custom-errors/ +contract CustomError { + mapping(address => uint) balance; + + function transfer(address to, uint256 amount) public { + if (amount > balance[msg.sender]) + // Error call using named parameters. Equivalent to + // revert InsufficientBalance(balance[msg.sender], amount); + revert InsufficientBalance({ + available: balance[msg.sender], + required: amount + }); + balance[msg.sender] -= amount; + balance[to] += amount; + } + // ... +} diff --git a/packages/target-ethers-v5-test/test/CustomError.test.ts b/packages/target-ethers-v5-test/test/CustomError.test.ts new file mode 100644 index 000000000..7485c130b --- /dev/null +++ b/packages/target-ethers-v5-test/test/CustomError.test.ts @@ -0,0 +1,48 @@ +import { BigNumber, ethers } from 'ethers' +import { AssertTrue, IsExact, typedAssert } from 'test-utils' + +// import type { TypedCustomEvent } from '../types/common' + +import type { + CustomError +} from '../types/v0.8.9/CustomError' +import { createNewBlockchain, deployContract } from './common' + +describe('CustomError support', () => { + const chain = createNewBlockchain('CustomError') + + let contract!: CustomError + + beforeEach(async () => { + contract = chain.contract + const { signer } = chain + + signer.provider.pollingInterval = 100 + contract = await deployContract(signer, 'CustomError') + }) + + afterEach(async () => { + }) + + it.only('typed event import', async () => { + try { + const foo = await contract.callStatic.transfer('0x0000000000000000000000000000000000000000', 1) + + console.log('in here') + } catch (err) { + console.log(err) + console.log(Object.keys(err as any)) + } + // const filter = contract.errors.Event1(null, null) + // const results = (await contract.queryFilter(filter)) as any + + // const results2 = results as Event1Event[] + // results2.map((r) => { + // typedAssert(r.args.value1, BigNumber.from(1)) + // typedAssert(r.args.value2, BigNumber.from(2)) + // typedAssert(r.args[0], BigNumber.from(1)) + // typedAssert(r.args[1], BigNumber.from(2)) + // }) + }) + +}) diff --git a/packages/target-ethers-v5-test/test/common.ts b/packages/target-ethers-v5-test/test/common.ts index 7084b5573..cf5072cc8 100644 --- a/packages/target-ethers-v5-test/test/common.ts +++ b/packages/target-ethers-v5-test/test/common.ts @@ -26,7 +26,7 @@ export function createNewBlockchain(contractName: string) { const ctx: Partial = {} beforeEach(async () => { - const ganache = createGanacheServer({ logging: { quiet: true } }) + const ganache = createGanacheServer({ logging: { quiet: false } }) await ganache.listen(8545) diff --git a/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError/TestToken__factory.ts b/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError/TestToken__factory.ts new file mode 100644 index 000000000..566652e32 --- /dev/null +++ b/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError/TestToken__factory.ts @@ -0,0 +1,60 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ + +import { Contract, Signer, utils } from "ethers"; +import type { Provider } from "@ethersproject/providers"; +import type { + TestToken, + TestTokenInterface, +} from "../../../v0.8.9/CustomError/TestToken"; + +const _abi = [ + { + inputs: [ + { + internalType: "uint256", + name: "available", + type: "uint256", + }, + { + internalType: "uint256", + name: "required", + type: "uint256", + }, + ], + name: "InsufficientBalance", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "transfer", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; + +export class TestToken__factory { + static readonly abi = _abi; + static createInterface(): TestTokenInterface { + return new utils.Interface(_abi) as TestTokenInterface; + } + static connect( + address: string, + signerOrProvider: Signer | Provider + ): TestToken { + return new Contract(address, _abi, signerOrProvider) as TestToken; + } +} diff --git a/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError/index.ts b/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError/index.ts new file mode 100644 index 000000000..33edb32a2 --- /dev/null +++ b/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError/index.ts @@ -0,0 +1,4 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +export { TestToken__factory } from "./TestToken__factory"; diff --git a/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError__factory.ts b/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError__factory.ts new file mode 100644 index 000000000..d7cc71ff6 --- /dev/null +++ b/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError__factory.ts @@ -0,0 +1,65 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ + +import { Contract, Signer, utils } from "ethers"; +import type { Provider } from "@ethersproject/providers"; +import type { + CustomError, + CustomErrorInterface, +} from "../../v0.8.9/CustomError"; + +const _abi = [ + { + inputs: [ + { + internalType: "uint256", + name: "magic", + type: "uint256", + }, + { + internalType: "uint256", + name: "available", + type: "uint256", + }, + { + internalType: "uint256", + name: "required", + type: "uint256", + }, + ], + name: "InsufficientBalance", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "transfer", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; + +export class CustomError__factory { + static readonly abi = _abi; + static createInterface(): CustomErrorInterface { + return new utils.Interface(_abi) as CustomErrorInterface; + } + static connect( + address: string, + signerOrProvider: Signer | Provider + ): CustomError { + return new Contract(address, _abi, signerOrProvider) as CustomError; + } +} diff --git a/packages/target-ethers-v5-test/types/factories/v0.8.9/index.ts b/packages/target-ethers-v5-test/types/factories/v0.8.9/index.ts index 964695c88..9ce92dfec 100644 --- a/packages/target-ethers-v5-test/types/factories/v0.8.9/index.ts +++ b/packages/target-ethers-v5-test/types/factories/v0.8.9/index.ts @@ -4,6 +4,7 @@ export * as kingOfTheHill from "./KingOfTheHill"; export * as rarity from "./Rarity"; export * as nested from "./nested"; +export { CustomError__factory } from "./CustomError__factory"; export { ISimpleToken__factory } from "./ISimpleToken__factory"; export { Issue552_Reproduction__factory } from "./Issue552_Reproduction__factory"; export { SimpleToken__factory } from "./SimpleToken__factory"; diff --git a/packages/target-ethers-v5-test/types/index.ts b/packages/target-ethers-v5-test/types/index.ts index 86c671afb..29f70f9dd 100644 --- a/packages/target-ethers-v5-test/types/index.ts +++ b/packages/target-ethers-v5-test/types/index.ts @@ -30,6 +30,8 @@ export type { Payable } from "./v0.6.4/Payable/Payable"; export { Payable__factory } from "./factories/v0.6.4/Payable/Payable__factory"; export type { PayableFactory } from "./v0.6.4/Payable/PayableFactory"; export { PayableFactory__factory } from "./factories/v0.6.4/Payable/PayableFactory__factory"; +export type { CustomError } from "./v0.8.9/CustomError"; +export { CustomError__factory } from "./factories/v0.8.9/CustomError__factory"; export type { ISimpleToken } from "./v0.8.9/ISimpleToken"; export { ISimpleToken__factory } from "./factories/v0.8.9/ISimpleToken__factory"; export type { Issue552_Reproduction } from "./v0.8.9/Issue552_Reproduction"; diff --git a/packages/target-ethers-v5-test/types/v0.6.4/DataTypesInput.ts b/packages/target-ethers-v5-test/types/v0.6.4/DataTypesInput.ts index a14041bcd..6af137ebd 100644 --- a/packages/target-ethers-v5-test/types/v0.6.4/DataTypesInput.ts +++ b/packages/target-ethers-v5-test/types/v0.6.4/DataTypesInput.ts @@ -388,6 +388,8 @@ export interface DataTypesInputInterface extends utils.Interface { ): Result; events: {}; + + errors: {}; } export interface DataTypesInput extends BaseContract { diff --git a/packages/target-ethers-v5-test/types/v0.6.4/DataTypesPure.ts b/packages/target-ethers-v5-test/types/v0.6.4/DataTypesPure.ts index 911257c2e..7de7f3dd6 100644 --- a/packages/target-ethers-v5-test/types/v0.6.4/DataTypesPure.ts +++ b/packages/target-ethers-v5-test/types/v0.6.4/DataTypesPure.ts @@ -153,6 +153,8 @@ export interface DataTypesPureInterface extends utils.Interface { decodeFunctionResult(functionFragment: "pure_uint8", data: BytesLike): Result; events: {}; + + errors: {}; } export interface DataTypesPure extends BaseContract { diff --git a/packages/target-ethers-v5-test/types/v0.6.4/DataTypesView.ts b/packages/target-ethers-v5-test/types/v0.6.4/DataTypesView.ts index afe48c687..5ddfea9a1 100644 --- a/packages/target-ethers-v5-test/types/v0.6.4/DataTypesView.ts +++ b/packages/target-ethers-v5-test/types/v0.6.4/DataTypesView.ts @@ -153,6 +153,8 @@ export interface DataTypesViewInterface extends utils.Interface { decodeFunctionResult(functionFragment: "view_uint8", data: BytesLike): Result; events: {}; + + errors: {}; } export interface DataTypesView extends BaseContract { diff --git a/packages/target-ethers-v5-test/types/v0.6.4/Events.ts b/packages/target-ethers-v5-test/types/v0.6.4/Events.ts index b9dc14576..24c1489a9 100644 --- a/packages/target-ethers-v5-test/types/v0.6.4/Events.ts +++ b/packages/target-ethers-v5-test/types/v0.6.4/Events.ts @@ -121,6 +121,8 @@ export interface EventsInterface extends utils.Interface { getEvent(nameOrSignatureOrTopic: "Event4"): EventFragment; getEvent(nameOrSignatureOrTopic: "NoArgsEvent"): EventFragment; getEvent(nameOrSignatureOrTopic: "UpdateFrequencySet"): EventFragment; + + errors: {}; } export interface AnonEvent1EventObject { diff --git a/packages/target-ethers-v5-test/types/v0.6.4/Issue428_Reproduction/A.ts b/packages/target-ethers-v5-test/types/v0.6.4/Issue428_Reproduction/A.ts index 98287f216..41e7a32bc 100644 --- a/packages/target-ethers-v5-test/types/v0.6.4/Issue428_Reproduction/A.ts +++ b/packages/target-ethers-v5-test/types/v0.6.4/Issue428_Reproduction/A.ts @@ -19,6 +19,8 @@ export interface AInterface extends utils.Interface { }; getEvent(nameOrSignatureOrTopic: "Committed"): EventFragment; + + errors: {}; } export interface CommittedEventObject { diff --git a/packages/target-ethers-v5-test/types/v0.6.4/Issue428_Reproduction/B.ts b/packages/target-ethers-v5-test/types/v0.6.4/Issue428_Reproduction/B.ts index 2a49529ef..a0f0f3810 100644 --- a/packages/target-ethers-v5-test/types/v0.6.4/Issue428_Reproduction/B.ts +++ b/packages/target-ethers-v5-test/types/v0.6.4/Issue428_Reproduction/B.ts @@ -21,6 +21,8 @@ export interface BInterface extends utils.Interface { getEvent(nameOrSignatureOrTopic: "Committed(uint256)"): EventFragment; getEvent(nameOrSignatureOrTopic: "Committed(address[])"): EventFragment; + + errors: {}; } export interface Committed_uint256_EventObject { diff --git a/packages/target-ethers-v5-test/types/v0.6.4/Library/Lib.ts b/packages/target-ethers-v5-test/types/v0.6.4/Library/Lib.ts index 2bd5f1dfb..87a131f7e 100644 --- a/packages/target-ethers-v5-test/types/v0.6.4/Library/Lib.ts +++ b/packages/target-ethers-v5-test/types/v0.6.4/Library/Lib.ts @@ -32,6 +32,8 @@ export interface LibInterface extends utils.Interface { decodeFunctionResult(functionFragment: "other", data: BytesLike): Result; events: {}; + + errors: {}; } export interface Lib extends BaseContract { diff --git a/packages/target-ethers-v5-test/types/v0.6.4/LibraryConsumer.ts b/packages/target-ethers-v5-test/types/v0.6.4/LibraryConsumer.ts index 70fdfd756..439234fc8 100644 --- a/packages/target-ethers-v5-test/types/v0.6.4/LibraryConsumer.ts +++ b/packages/target-ethers-v5-test/types/v0.6.4/LibraryConsumer.ts @@ -35,6 +35,8 @@ export interface LibraryConsumerInterface extends utils.Interface { decodeFunctionResult(functionFragment: "someOther", data: BytesLike): Result; events: {}; + + errors: {}; } export interface LibraryConsumer extends BaseContract { diff --git a/packages/target-ethers-v5-test/types/v0.6.4/Name-Mangling/NAME12mangling.ts b/packages/target-ethers-v5-test/types/v0.6.4/Name-Mangling/NAME12mangling.ts index 143ad127c..f00d9c734 100644 --- a/packages/target-ethers-v5-test/types/v0.6.4/Name-Mangling/NAME12mangling.ts +++ b/packages/target-ethers-v5-test/types/v0.6.4/Name-Mangling/NAME12mangling.ts @@ -34,6 +34,8 @@ export interface NAME12manglingInterface extends utils.Interface { decodeFunctionResult(functionFragment: "works", data: BytesLike): Result; events: {}; + + errors: {}; } export interface NAME12mangling extends BaseContract { diff --git a/packages/target-ethers-v5-test/types/v0.6.4/Overloads.ts b/packages/target-ethers-v5-test/types/v0.6.4/Overloads.ts index dbe661e54..518e2b35f 100644 --- a/packages/target-ethers-v5-test/types/v0.6.4/Overloads.ts +++ b/packages/target-ethers-v5-test/types/v0.6.4/Overloads.ts @@ -49,6 +49,8 @@ export interface OverloadsInterface extends utils.Interface { ): Result; events: {}; + + errors: {}; } export interface Overloads extends BaseContract { diff --git a/packages/target-ethers-v5-test/types/v0.6.4/Payable/Payable.ts b/packages/target-ethers-v5-test/types/v0.6.4/Payable/Payable.ts index a04ee47b3..d25a696e0 100644 --- a/packages/target-ethers-v5-test/types/v0.6.4/Payable/Payable.ts +++ b/packages/target-ethers-v5-test/types/v0.6.4/Payable/Payable.ts @@ -51,6 +51,8 @@ export interface PayableInterface extends utils.Interface { ): Result; events: {}; + + errors: {}; } export interface Payable extends BaseContract { diff --git a/packages/target-ethers-v5-test/types/v0.6.4/Payable/PayableFactory.ts b/packages/target-ethers-v5-test/types/v0.6.4/Payable/PayableFactory.ts index 094774c8e..eaa1aaa53 100644 --- a/packages/target-ethers-v5-test/types/v0.6.4/Payable/PayableFactory.ts +++ b/packages/target-ethers-v5-test/types/v0.6.4/Payable/PayableFactory.ts @@ -36,6 +36,8 @@ export interface PayableFactoryInterface extends utils.Interface { decodeFunctionResult(functionFragment: "newPayable", data: BytesLike): Result; events: {}; + + errors: {}; } export interface PayableFactory extends BaseContract { diff --git a/packages/target-ethers-v5-test/types/v0.8.9/CustomError.ts b/packages/target-ethers-v5-test/types/v0.8.9/CustomError.ts new file mode 100644 index 000000000..709d86fda --- /dev/null +++ b/packages/target-ethers-v5-test/types/v0.8.9/CustomError.ts @@ -0,0 +1,123 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import type { + BaseContract, + BigNumber, + BigNumberish, + BytesLike, + CallOverrides, + ContractTransaction, + Overrides, + PopulatedTransaction, + Signer, + utils, +} from "ethers"; +import type { + FunctionFragment, + Result, + ErrorFragment, +} from "@ethersproject/abi"; +import type { Listener, Provider } from "@ethersproject/providers"; +import type { + TypedEventFilter, + TypedEvent, + TypedListener, + OnEvent, +} from "../common"; + +export interface CustomErrorInterface extends utils.Interface { + functions: { + "transfer(address,uint256)": FunctionFragment; + }; + + getFunction(nameOrSignatureOrTopic: "transfer"): FunctionFragment; + + encodeFunctionData( + functionFragment: "transfer", + values: [string, BigNumberish] + ): string; + + decodeFunctionResult(functionFragment: "transfer", data: BytesLike): Result; + + events: {}; + + errors: { + "InsufficientBalance(uint256,uint256,uint256)": ErrorFragment; + }; + + getError(nameOrSignature: "InsufficientBalance"): ErrorFragment; +} + +export interface InsufficientBalanceErrorObject { + magic: BigNumber; + available: BigNumber; + required: BigNumber; +} + +export interface CustomError extends BaseContract { + connect(signerOrProvider: Signer | Provider | string): this; + attach(addressOrName: string): this; + deployed(): Promise; + + interface: CustomErrorInterface; + + queryFilter( + event: TypedEventFilter, + fromBlockOrBlockhash?: string | number | undefined, + toBlock?: string | number | undefined + ): Promise>; + + listeners( + eventFilter?: TypedEventFilter + ): Array>; + listeners(eventName?: string): Array; + removeAllListeners( + eventFilter: TypedEventFilter + ): this; + removeAllListeners(eventName?: string): this; + off: OnEvent; + on: OnEvent; + once: OnEvent; + removeListener: OnEvent; + + functions: { + transfer( + to: string, + amount: BigNumberish, + overrides?: Overrides & { from?: string | Promise } + ): Promise; + }; + + transfer( + to: string, + amount: BigNumberish, + overrides?: Overrides & { from?: string | Promise } + ): Promise; + + callStatic: { + transfer( + to: string, + amount: BigNumberish, + overrides?: CallOverrides + ): Promise; + }; + + filters: {}; + + estimateGas: { + transfer( + to: string, + amount: BigNumberish, + overrides?: Overrides & { from?: string | Promise } + ): Promise; + }; + + populateTransaction: { + transfer( + to: string, + amount: BigNumberish, + overrides?: Overrides & { from?: string | Promise } + ): Promise; + }; +} diff --git a/packages/target-ethers-v5-test/types/v0.8.9/ISimpleToken.ts b/packages/target-ethers-v5-test/types/v0.8.9/ISimpleToken.ts index 601e12db8..7e19915ff 100644 --- a/packages/target-ethers-v5-test/types/v0.8.9/ISimpleToken.ts +++ b/packages/target-ethers-v5-test/types/v0.8.9/ISimpleToken.ts @@ -37,6 +37,8 @@ export interface ISimpleTokenInterface extends utils.Interface { decodeFunctionResult(functionFragment: "transfer", data: BytesLike): Result; events: {}; + + errors: {}; } export interface ISimpleToken extends BaseContract { diff --git a/packages/target-ethers-v5-test/types/v0.8.9/Issue552_Reproduction.ts b/packages/target-ethers-v5-test/types/v0.8.9/Issue552_Reproduction.ts index afb3a8f9f..f6803cde9 100644 --- a/packages/target-ethers-v5-test/types/v0.8.9/Issue552_Reproduction.ts +++ b/packages/target-ethers-v5-test/types/v0.8.9/Issue552_Reproduction.ts @@ -78,6 +78,8 @@ export interface Issue552_ReproductionInterface extends utils.Interface { ): Result; events: {}; + + errors: {}; } export interface Issue552_Reproduction extends BaseContract { diff --git a/packages/target-ethers-v5-test/types/v0.8.9/KingOfTheHill/KingOfTheHill.ts b/packages/target-ethers-v5-test/types/v0.8.9/KingOfTheHill/KingOfTheHill.ts index f2d13fffe..4b0a5f4ce 100644 --- a/packages/target-ethers-v5-test/types/v0.8.9/KingOfTheHill/KingOfTheHill.ts +++ b/packages/target-ethers-v5-test/types/v0.8.9/KingOfTheHill/KingOfTheHill.ts @@ -18,6 +18,7 @@ import type { FunctionFragment, Result, EventFragment, + ErrorFragment, } from "@ethersproject/abi"; import type { Listener, Provider } from "@ethersproject/providers"; import type { @@ -63,6 +64,12 @@ export interface KingOfTheHillInterface extends utils.Interface { }; getEvent(nameOrSignatureOrTopic: "HighestBidIncreased"): EventFragment; + + errors: { + "BidNotHighEnough(uint256,uint256)": ErrorFragment; + }; + + getError(nameOrSignature: "BidNotHighEnough"): ErrorFragment; } export interface HighestBidIncreasedEventObject { @@ -76,6 +83,11 @@ export type HighestBidIncreasedEvent = TypedEvent< export type HighestBidIncreasedEventFilter = TypedEventFilter; +export interface BidNotHighEnoughErrorObject { + value: BigNumber; + highestBidValue: BigNumber; +} + export interface KingOfTheHill extends BaseContract { connect(signerOrProvider: Signer | Provider | string): this; attach(addressOrName: string): this; diff --git a/packages/target-ethers-v5-test/types/v0.8.9/KingOfTheHill/Withdrawable.ts b/packages/target-ethers-v5-test/types/v0.8.9/KingOfTheHill/Withdrawable.ts index dc139bedf..b128e3fe2 100644 --- a/packages/target-ethers-v5-test/types/v0.8.9/KingOfTheHill/Withdrawable.ts +++ b/packages/target-ethers-v5-test/types/v0.8.9/KingOfTheHill/Withdrawable.ts @@ -33,6 +33,8 @@ export interface WithdrawableInterface extends utils.Interface { decodeFunctionResult(functionFragment: "withdraw", data: BytesLike): Result; events: {}; + + errors: {}; } export interface Withdrawable extends BaseContract { diff --git a/packages/target-ethers-v5-test/types/v0.8.9/Rarity/ERC721.ts b/packages/target-ethers-v5-test/types/v0.8.9/Rarity/ERC721.ts index 6a51a1c99..166a7afa7 100644 --- a/packages/target-ethers-v5-test/types/v0.8.9/Rarity/ERC721.ts +++ b/packages/target-ethers-v5-test/types/v0.8.9/Rarity/ERC721.ts @@ -123,6 +123,8 @@ export interface ERC721Interface extends utils.Interface { getEvent(nameOrSignatureOrTopic: "Approval"): EventFragment; getEvent(nameOrSignatureOrTopic: "ApprovalForAll"): EventFragment; getEvent(nameOrSignatureOrTopic: "Transfer"): EventFragment; + + errors: {}; } export interface ApprovalEventObject { diff --git a/packages/target-ethers-v5-test/types/v0.8.9/Rarity/ERC721Enumerable.ts b/packages/target-ethers-v5-test/types/v0.8.9/Rarity/ERC721Enumerable.ts index 01e5ca999..b35969aa5 100644 --- a/packages/target-ethers-v5-test/types/v0.8.9/Rarity/ERC721Enumerable.ts +++ b/packages/target-ethers-v5-test/types/v0.8.9/Rarity/ERC721Enumerable.ts @@ -153,6 +153,8 @@ export interface ERC721EnumerableInterface extends utils.Interface { getEvent(nameOrSignatureOrTopic: "Approval"): EventFragment; getEvent(nameOrSignatureOrTopic: "ApprovalForAll"): EventFragment; getEvent(nameOrSignatureOrTopic: "Transfer"): EventFragment; + + errors: {}; } export interface ApprovalEventObject { diff --git a/packages/target-ethers-v5-test/types/v0.8.9/Rarity/IERC721.ts b/packages/target-ethers-v5-test/types/v0.8.9/Rarity/IERC721.ts index 437b6d1d3..9a8682793 100644 --- a/packages/target-ethers-v5-test/types/v0.8.9/Rarity/IERC721.ts +++ b/packages/target-ethers-v5-test/types/v0.8.9/Rarity/IERC721.ts @@ -123,6 +123,8 @@ export interface IERC721Interface extends utils.Interface { getEvent(nameOrSignatureOrTopic: "Approval"): EventFragment; getEvent(nameOrSignatureOrTopic: "ApprovalForAll"): EventFragment; getEvent(nameOrSignatureOrTopic: "Transfer"): EventFragment; + + errors: {}; } export interface ApprovalEventObject { diff --git a/packages/target-ethers-v5-test/types/v0.8.9/Rarity/IERC721Enumerable.ts b/packages/target-ethers-v5-test/types/v0.8.9/Rarity/IERC721Enumerable.ts index 2cb29f60e..8e0686edf 100644 --- a/packages/target-ethers-v5-test/types/v0.8.9/Rarity/IERC721Enumerable.ts +++ b/packages/target-ethers-v5-test/types/v0.8.9/Rarity/IERC721Enumerable.ts @@ -153,6 +153,8 @@ export interface IERC721EnumerableInterface extends utils.Interface { getEvent(nameOrSignatureOrTopic: "Approval"): EventFragment; getEvent(nameOrSignatureOrTopic: "ApprovalForAll"): EventFragment; getEvent(nameOrSignatureOrTopic: "Transfer"): EventFragment; + + errors: {}; } export interface ApprovalEventObject { diff --git a/packages/target-ethers-v5-test/types/v0.8.9/Rarity/IERC721Receiver.ts b/packages/target-ethers-v5-test/types/v0.8.9/Rarity/IERC721Receiver.ts index d348d3663..12c09d38e 100644 --- a/packages/target-ethers-v5-test/types/v0.8.9/Rarity/IERC721Receiver.ts +++ b/packages/target-ethers-v5-test/types/v0.8.9/Rarity/IERC721Receiver.ts @@ -40,6 +40,8 @@ export interface IERC721ReceiverInterface extends utils.Interface { ): Result; events: {}; + + errors: {}; } export interface IERC721Receiver extends BaseContract { diff --git a/packages/target-ethers-v5-test/types/v0.8.9/Rarity/Rarity.ts b/packages/target-ethers-v5-test/types/v0.8.9/Rarity/Rarity.ts index a77ce527e..a796992f2 100644 --- a/packages/target-ethers-v5-test/types/v0.8.9/Rarity/Rarity.ts +++ b/packages/target-ethers-v5-test/types/v0.8.9/Rarity/Rarity.ts @@ -248,6 +248,8 @@ export interface RarityInterface extends utils.Interface { getEvent(nameOrSignatureOrTopic: "Transfer"): EventFragment; getEvent(nameOrSignatureOrTopic: "leveled"): EventFragment; getEvent(nameOrSignatureOrTopic: "summoned"): EventFragment; + + errors: {}; } export interface ApprovalEventObject { diff --git a/packages/target-ethers-v5-test/types/v0.8.9/SimpleToken.ts b/packages/target-ethers-v5-test/types/v0.8.9/SimpleToken.ts index 89978520c..326032ec3 100644 --- a/packages/target-ethers-v5-test/types/v0.8.9/SimpleToken.ts +++ b/packages/target-ethers-v5-test/types/v0.8.9/SimpleToken.ts @@ -37,6 +37,8 @@ export interface SimpleTokenInterface extends utils.Interface { decodeFunctionResult(functionFragment: "transfer", data: BytesLike): Result; events: {}; + + errors: {}; } export interface SimpleToken extends BaseContract { diff --git a/packages/target-ethers-v5-test/types/v0.8.9/index.ts b/packages/target-ethers-v5-test/types/v0.8.9/index.ts index 25c6eeca5..b9e9025e9 100644 --- a/packages/target-ethers-v5-test/types/v0.8.9/index.ts +++ b/packages/target-ethers-v5-test/types/v0.8.9/index.ts @@ -7,6 +7,7 @@ import type * as rarity from "./Rarity"; export type { rarity }; import type * as nested from "./nested"; export type { nested }; +export type { CustomError } from "./CustomError"; export type { ISimpleToken } from "./ISimpleToken"; export type { Issue552_Reproduction } from "./Issue552_Reproduction"; export type { SimpleToken } from "./SimpleToken"; diff --git a/packages/target-ethers-v5-test/types/v0.8.9/nested/a/NestedLibrary.ts b/packages/target-ethers-v5-test/types/v0.8.9/nested/a/NestedLibrary.ts index 3bc95b6bb..e68e30463 100644 --- a/packages/target-ethers-v5-test/types/v0.8.9/nested/a/NestedLibrary.ts +++ b/packages/target-ethers-v5-test/types/v0.8.9/nested/a/NestedLibrary.ts @@ -31,6 +31,8 @@ export interface NestedLibraryInterface extends utils.Interface { decodeFunctionResult(functionFragment: "getValue", data: BytesLike): Result; events: {}; + + errors: {}; } export interface NestedLibrary extends BaseContract { diff --git a/packages/target-ethers-v5-test/types/v0.8.9/nested/b/NestedLibrary.ts b/packages/target-ethers-v5-test/types/v0.8.9/nested/b/NestedLibrary.ts index 5eef3ca0b..3e2e3b3e2 100644 --- a/packages/target-ethers-v5-test/types/v0.8.9/nested/b/NestedLibrary.ts +++ b/packages/target-ethers-v5-test/types/v0.8.9/nested/b/NestedLibrary.ts @@ -31,6 +31,8 @@ export interface NestedLibraryInterface extends utils.Interface { decodeFunctionResult(functionFragment: "getValue", data: BytesLike): Result; events: {}; + + errors: {}; } export interface NestedLibrary extends BaseContract { diff --git a/packages/target-ethers-v5/src/codegen/errors.ts b/packages/target-ethers-v5/src/codegen/errors.ts new file mode 100644 index 000000000..ea2717bd8 --- /dev/null +++ b/packages/target-ethers-v5/src/codegen/errors.ts @@ -0,0 +1,64 @@ +import { + createPositionalIdentifier, + CustomErrorDeclaration, + getSignatureForFn, + } from 'typechain' + + import { generateOutputComplexTypeAsArray, generateOutputComplexTypesAsObject } from './types' + + + + export function generateErrorTypeExports(errors: CustomErrorDeclaration[]) { + if (errors.length === 1) { + return generateErrorTypeExport(errors[0], false) + } else { + return errors.map((e) => generateErrorTypeExport(e, true)).join('\n') + } + } + + + function generateErrorTypeExport(error: CustomErrorDeclaration, includeArgTypes: boolean) { + const components = error.inputs.map((input, i) => ({ name: input.name ?? `arg${i.toString()}`, type: input.type })) + const objectOutput = generateOutputComplexTypesAsObject(components, { useStructs: true }) || '{}' + + const identifier = generateErrorIdentifier(error, { includeArgTypes }) + + return ` + export interface ${identifier}Object ${objectOutput}; + ` + } + + export function generateInterfaceErrorDescription(error: CustomErrorDeclaration): string { + return `'${generateErrorSignature(error)}': ErrorFragment;` + } + + export function generateErrorSignature(error: CustomErrorDeclaration): string { + return `${error.name}(${error.inputs.map((input: any) => input.type.originalType).join(',')})` + } + + export function generateErrorInputs(errorArgs: CustomErrorDeclaration["inputs"]) { + if (errorArgs.length === 0) { + return '' + } + return ( + errorArgs + .map((arg, index) => { + return createPositionalIdentifier(arg.name) + }) + .join(', ') + ', ' + ) + } + + export function generateGetError(error: CustomErrorDeclaration, useSignature: boolean): string { + return `getError(nameOrSignature: '${ + useSignature ? generateErrorSignature(error) : error.name + }'): ErrorFragment;` + } + + function generateErrorIdentifier(error: CustomErrorDeclaration, { includeArgTypes }: { includeArgTypes?: boolean } = {}) { + if (includeArgTypes) { + return getSignatureForFn(error) + '_Error' + } else { + return error.name + 'Error' + } + } diff --git a/packages/target-ethers-v5/src/codegen/index.ts b/packages/target-ethers-v5/src/codegen/index.ts index de8fd9e60..0e3c5ff75 100644 --- a/packages/target-ethers-v5/src/codegen/index.ts +++ b/packages/target-ethers-v5/src/codegen/index.ts @@ -5,6 +5,7 @@ import { Contract, createImportsForUsedIdentifiers, createImportTypeDeclaration, + CustomErrorDeclaration, EventDeclaration, FunctionDeclaration, StructType, @@ -19,6 +20,11 @@ import { generateGetEvent, generateInterfaceEventDescription, } from './events' +import { + generateInterfaceErrorDescription, + generateGetError, + generateErrorTypeExports +} from './errors' import { codegenFunctions, generateDecodeFunctionResultOverload, @@ -69,9 +75,20 @@ export function codegenContractTypings(contract: Contract, codegenConfig: Codege ${values(contract.events) .flatMap((v) => processDeclaration(v, alwaysGenerateOverloads, generateGetEvent)) .join('\n')} + + errors: { + ${values(contract.errors) + .flatMap((v) => v.map(generateInterfaceErrorDescription)) + .join('\n')} + }; + + ${values(contract.errors) + .flatMap((v) => processDeclaration(v, alwaysGenerateOverloads, generateGetError)) + .join('\n')} } ${values(contract.events).map(generateEventTypeExports).join('\n')} + ${values(contract.errors).map(generateErrorTypeExports).join('\n')} export interface ${contract.name} extends BaseContract { ${codegenConfig.discriminateTypes ? `contractName: '${contract.name}';\n` : ``} @@ -137,7 +154,7 @@ export function codegenContractTypings(contract: Contract, codegenConfig: Codege 'Signer', 'utils', ], - 'type @ethersproject/abi': ['FunctionFragment', 'Result', 'EventFragment'], + 'type @ethersproject/abi': ['FunctionFragment', 'Result', 'EventFragment', 'ErrorFragment'], 'type @ethersproject/providers': ['Listener', 'Provider'], }, source, @@ -375,7 +392,7 @@ function generateLibraryAddressesInterface(contract: Contract, bytecode: Bytecod * @param stringGen - function generating source code based on the declaration * @returns generated source code */ -function processDeclaration( +function processDeclaration( fns: D[], forceGenerateOverloads: boolean, stringGen: (fn: D, useSignature: boolean) => string, diff --git a/packages/target-web3-v1-test/types/v0.8.9/CustomError.ts b/packages/target-web3-v1-test/types/v0.8.9/CustomError.ts new file mode 100644 index 000000000..5beec33e5 --- /dev/null +++ b/packages/target-web3-v1-test/types/v0.8.9/CustomError.ts @@ -0,0 +1,40 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ + +import type BN from "bn.js"; +import type { ContractOptions } from "web3-eth-contract"; +import type { EventLog } from "web3-core"; +import type { EventEmitter } from "events"; +import type { + Callback, + PayableTransactionObject, + NonPayableTransactionObject, + BlockType, + ContractEventLog, + BaseContract, +} from "../types"; + +export interface EventOptions { + filter?: object; + fromBlock?: BlockType; + topics?: string[]; +} + +export interface CustomError extends BaseContract { + constructor( + jsonInterface: any[], + address?: string, + options?: ContractOptions + ): CustomError; + clone(): CustomError; + methods: { + transfer( + to: string, + amount: number | string | BN + ): NonPayableTransactionObject; + }; + events: { + allEvents(options?: EventOptions, cb?: Callback): EventEmitter; + }; +} diff --git a/packages/target-web3-v1-test/types/v0.8.9/CustomError/TestToken.ts b/packages/target-web3-v1-test/types/v0.8.9/CustomError/TestToken.ts new file mode 100644 index 000000000..c925f1c15 --- /dev/null +++ b/packages/target-web3-v1-test/types/v0.8.9/CustomError/TestToken.ts @@ -0,0 +1,40 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ + +import type BN from "bn.js"; +import type { ContractOptions } from "web3-eth-contract"; +import type { EventLog } from "web3-core"; +import type { EventEmitter } from "events"; +import type { + Callback, + PayableTransactionObject, + NonPayableTransactionObject, + BlockType, + ContractEventLog, + BaseContract, +} from "../../types"; + +export interface EventOptions { + filter?: object; + fromBlock?: BlockType; + topics?: string[]; +} + +export interface TestToken extends BaseContract { + constructor( + jsonInterface: any[], + address?: string, + options?: ContractOptions + ): TestToken; + clone(): TestToken; + methods: { + transfer( + to: string, + amount: number | string | BN + ): NonPayableTransactionObject; + }; + events: { + allEvents(options?: EventOptions, cb?: Callback): EventEmitter; + }; +} diff --git a/packages/target-web3-v1-test/types/v0.8.9/CustomError/index.ts b/packages/target-web3-v1-test/types/v0.8.9/CustomError/index.ts new file mode 100644 index 000000000..14ee95744 --- /dev/null +++ b/packages/target-web3-v1-test/types/v0.8.9/CustomError/index.ts @@ -0,0 +1,4 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +export type { TestToken } from "./TestToken"; diff --git a/packages/target-web3-v1-test/types/v0.8.9/index.ts b/packages/target-web3-v1-test/types/v0.8.9/index.ts index 25c6eeca5..b9e9025e9 100644 --- a/packages/target-web3-v1-test/types/v0.8.9/index.ts +++ b/packages/target-web3-v1-test/types/v0.8.9/index.ts @@ -7,6 +7,7 @@ import type * as rarity from "./Rarity"; export type { rarity }; import type * as nested from "./nested"; export type { nested }; +export type { CustomError } from "./CustomError"; export type { ISimpleToken } from "./ISimpleToken"; export type { Issue552_Reproduction } from "./Issue552_Reproduction"; export type { SimpleToken } from "./SimpleToken"; diff --git a/packages/typechain/src/parser/abiParser.ts b/packages/typechain/src/parser/abiParser.ts index 0f054ed36..bab958c16 100644 --- a/packages/typechain/src/parser/abiParser.ts +++ b/packages/typechain/src/parser/abiParser.ts @@ -61,6 +61,7 @@ export interface Contract { constructor: FunctionWithoutOutputDeclaration[] functions: Dictionary events: Dictionary + errors: Dictionary structs: Dictionary documentation?: | { @@ -88,6 +89,12 @@ export interface RawAbiDefinition { type: string } +export interface CustomErrorDeclaration { + name: string + inputs: AbiParameter[] + documentation?: FunctionDocumentation | undefined // should omit `return` from FunctionDocumentation? +} + export interface EventDeclaration { name: string isAnonymous: boolean @@ -150,6 +157,7 @@ export function parse(abi: RawAbiDefinition[], path: string, documentation?: Doc let fallback: FunctionWithoutInputDeclaration | undefined const functions: FunctionDeclaration[] = [] const events: EventDeclaration[] = [] + const errors: CustomErrorDeclaration[] = [] const structs: StructType[] = [] function registerStruct(newStruct: StructType) { @@ -196,6 +204,11 @@ export function parse(abi: RawAbiDefinition[], path: string, documentation?: Doc return } + if (abiPiece.type === 'error') { + errors.push(parseCustomErrorDeclaration(abiPiece, registerStruct, documentation)) + return + } + debug(`Unrecognized abi element: ${abiPiece.type}`) }) @@ -205,6 +218,7 @@ export function parse(abi: RawAbiDefinition[], path: string, documentation?: Doc constructor: constructors, functions: groupBy(functions, (f) => f.name), events: groupBy(events, (e) => e.name), + errors: groupBy(errors, (e) => e.name), structs: groupBy(structs, (e) => e.structName && e.structName.toString()), documentation: documentation ? omit(documentation, ['methods']) : undefined, } @@ -299,6 +313,19 @@ function parseFallback( } } +export function parseCustomErrorDeclaration( + abiPiece: RawAbiDefinition, + registerStruct: (struct: StructType) => void, + documentation?: DocumentationResult, +): CustomErrorDeclaration { + debug(`Parsing function declaration "${abiPiece.name}"`) + return { + name: abiPiece.name, + inputs: abiPiece.inputs.map(parseRawAbiParameter.bind(null, registerStruct)), + documentation: getFunctionDocumentation(abiPiece, documentation), + } +} + function parseFunctionDeclaration( abiPiece: RawAbiDefinition, registerStruct: (struct: StructType) => void, diff --git a/packages/typechain/src/utils/signatures.ts b/packages/typechain/src/utils/signatures.ts index e9717398d..4da9fc896 100644 --- a/packages/typechain/src/utils/signatures.ts +++ b/packages/typechain/src/utils/signatures.ts @@ -1,4 +1,4 @@ -import { AbiParameter, EventDeclaration, FunctionDeclaration } from '../parser/abiParser' +import { AbiParameter, CustomErrorDeclaration, EventDeclaration, FunctionDeclaration } from '../parser/abiParser' import { ArrayType, TupleType } from '../parser/parseEvmType' export function getFullSignatureAsSymbolForEvent(event: EventDeclaration): string { @@ -32,6 +32,6 @@ export function getArgumentForSignature(argument: AbiParameter): string { } } -export function getSignatureForFn(fn: FunctionDeclaration): string { +export function getSignatureForFn(fn: FunctionDeclaration | CustomErrorDeclaration): string { return `${fn.name}(${fn.inputs.map((i) => getArgumentForSignature(i)).join(',')})` } diff --git a/packages/typechain/test/parser/abiParser.test.ts b/packages/typechain/test/parser/abiParser.test.ts index ed4e67423..43671f125 100644 --- a/packages/typechain/test/parser/abiParser.test.ts +++ b/packages/typechain/test/parser/abiParser.test.ts @@ -12,6 +12,7 @@ import { isConstant, isConstantFn, parse, + parseCustomErrorDeclaration, parseEvent, RawAbiDefinition, RawEventAbiDefinition, @@ -368,6 +369,37 @@ describe('parseEvent', () => { }) }) +describe('parseError', () => { + it('works', () => { + const expectedError = { + inputs: [ + { + internalType: "uint256", + name: "magic", + type: "uint256" + }, + { + internalType: "address", + name: "target", + type: "address" + }, + ], + "name": "InsufficientBalance", + "type": "error" + } as RawAbiDefinition + const parsedError = parseCustomErrorDeclaration(expectedError, () => {}) + + expect(parsedError).toEqual({ + name: 'InsufficientBalance', + inputs: [ + { name: 'magic', type: { type: 'uinteger', bits: 256, originalType: 'uint256' } }, + { name: 'target', type: { type: 'address', originalType: 'address' } }, + ], + documentation: undefined + }) + }) +}) + describe('parse', () => { describe('functions', () => { const abiPiece = { @@ -504,6 +536,7 @@ describe('parse', () => { documentation: undefined, functions: {}, structs: {}, + errors: {}, name: 'Sc1', rawName: 'sc1', path: [], From 1edb272bbe73a3921e1c5cb5bd56c2dcc04909fe Mon Sep 17 00:00:00 2001 From: fredlacs <32464905+fredlacs@users.noreply.github.com> Date: Sun, 1 May 2022 15:22:42 +0100 Subject: [PATCH 2/5] Cleanup unit test --- .../test/CustomError.test.ts | 30 +++++-------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/packages/target-ethers-v5-test/test/CustomError.test.ts b/packages/target-ethers-v5-test/test/CustomError.test.ts index 7485c130b..77a48b971 100644 --- a/packages/target-ethers-v5-test/test/CustomError.test.ts +++ b/packages/target-ethers-v5-test/test/CustomError.test.ts @@ -1,11 +1,7 @@ import { BigNumber, ethers } from 'ethers' import { AssertTrue, IsExact, typedAssert } from 'test-utils' -// import type { TypedCustomEvent } from '../types/common' - -import type { - CustomError -} from '../types/v0.8.9/CustomError' +import type { CustomError } from '../types/v0.8.9/CustomError' import { createNewBlockchain, deployContract } from './common' describe('CustomError support', () => { @@ -24,25 +20,15 @@ describe('CustomError support', () => { afterEach(async () => { }) - it.only('typed event import', async () => { + it('typed error import', async () => { try { - const foo = await contract.callStatic.transfer('0x0000000000000000000000000000000000000000', 1) - - console.log('in here') + await contract.callStatic.transfer('0x0000000000000000000000000000000000000000', 1) } catch (err) { - console.log(err) - console.log(Object.keys(err as any)) + const expectedError = contract.interface.errors['InsufficientBalance(uint256,uint256,uint256)'] + console.log(expectedError) + console.log(err) + // TODO: still wip on how to best consume the errors here + // if(err.reason !== expectedError.) } - // const filter = contract.errors.Event1(null, null) - // const results = (await contract.queryFilter(filter)) as any - - // const results2 = results as Event1Event[] - // results2.map((r) => { - // typedAssert(r.args.value1, BigNumber.from(1)) - // typedAssert(r.args.value2, BigNumber.from(2)) - // typedAssert(r.args[0], BigNumber.from(1)) - // typedAssert(r.args[1], BigNumber.from(2)) - // }) }) - }) From 7b9c84cb8b2adba0bd41e92210672366953869f8 Mon Sep 17 00:00:00 2001 From: fredlacs <32464905+fredlacs@users.noreply.github.com> Date: Sun, 1 May 2022 15:43:04 +0100 Subject: [PATCH 3/5] Lint and cleanup --- .../hardhat-test/typechain-types/Counter.ts | 2 + packages/hardhat-test/typechain-types/Demo.ts | 2 + .../typechain-types/StructsInConstructor.ts | 2 + .../artifacts/contracts/EdgeCases.ts | 2 + .../artifacts/contracts/TestContract.ts | 2 + .../artifacts/contracts/TestContract1.ts | 2 + .../artifacts/contracts/lib/SafeMath.ts | 2 + .../externalArtifacts/ERC20.ts | 2 + .../test/CustomError.test.ts | 24 ++-- packages/target-ethers-v5-test/test/common.ts | 2 +- .../v0.8.9/CustomError/TestToken__factory.ts | 60 ---------- .../factories/v0.8.9/CustomError/index.ts | 4 - .../factories/v0.8.9/CustomError__factory.ts | 5 - .../types/v0.8.9/CustomError.ts | 3 +- .../target-ethers-v5/src/codegen/errors.ts | 112 +++++++++--------- .../target-ethers-v5/src/codegen/index.ts | 6 +- .../target-ethers-v5/test/generation.test.ts | 1 + .../typechain/test/parser/abiParser.test.ts | 18 +-- 18 files changed, 92 insertions(+), 159 deletions(-) delete mode 100644 packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError/TestToken__factory.ts delete mode 100644 packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError/index.ts diff --git a/packages/hardhat-test/typechain-types/Counter.ts b/packages/hardhat-test/typechain-types/Counter.ts index e33aaaeb8..bc4dfbb79 100644 --- a/packages/hardhat-test/typechain-types/Counter.ts +++ b/packages/hardhat-test/typechain-types/Counter.ts @@ -49,6 +49,8 @@ export interface CounterInterface extends utils.Interface { }; getEvent(nameOrSignatureOrTopic: "CountedTo"): EventFragment; + + errors: {}; } export interface CountedToEventObject { diff --git a/packages/hardhat-test/typechain-types/Demo.ts b/packages/hardhat-test/typechain-types/Demo.ts index fb66a8f88..b73ae05ab 100644 --- a/packages/hardhat-test/typechain-types/Demo.ts +++ b/packages/hardhat-test/typechain-types/Demo.ts @@ -37,6 +37,8 @@ export interface DemoInterface extends utils.Interface { functions: {}; events: {}; + + errors: {}; } export interface Demo extends BaseContract { diff --git a/packages/hardhat-test/typechain-types/StructsInConstructor.ts b/packages/hardhat-test/typechain-types/StructsInConstructor.ts index 386dabbc2..ab9cc3df6 100644 --- a/packages/hardhat-test/typechain-types/StructsInConstructor.ts +++ b/packages/hardhat-test/typechain-types/StructsInConstructor.ts @@ -28,6 +28,8 @@ export interface StructsInConstructorInterface extends utils.Interface { functions: {}; events: {}; + + errors: {}; } export interface StructsInConstructor extends BaseContract { diff --git a/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/artifacts/contracts/EdgeCases.ts b/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/artifacts/contracts/EdgeCases.ts index 8eba81138..10a6895dc 100644 --- a/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/artifacts/contracts/EdgeCases.ts +++ b/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/artifacts/contracts/EdgeCases.ts @@ -15,6 +15,8 @@ export interface EdgeCasesInterface extends utils.Interface { functions: {}; events: {}; + + errors: {}; } export interface EdgeCases extends BaseContract { diff --git a/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/artifacts/contracts/TestContract.ts b/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/artifacts/contracts/TestContract.ts index db01ca653..2345e16d5 100644 --- a/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/artifacts/contracts/TestContract.ts +++ b/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/artifacts/contracts/TestContract.ts @@ -15,6 +15,8 @@ export interface TestContractInterface extends utils.Interface { functions: {}; events: {}; + + errors: {}; } export interface TestContract extends BaseContract { diff --git a/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/artifacts/contracts/TestContract1.ts b/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/artifacts/contracts/TestContract1.ts index dff563516..ec51c9aff 100644 --- a/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/artifacts/contracts/TestContract1.ts +++ b/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/artifacts/contracts/TestContract1.ts @@ -15,6 +15,8 @@ export interface TestContract1Interface extends utils.Interface { functions: {}; events: {}; + + errors: {}; } export interface TestContract1 extends BaseContract { diff --git a/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/artifacts/contracts/lib/SafeMath.ts b/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/artifacts/contracts/lib/SafeMath.ts index 279a9ccff..741845129 100644 --- a/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/artifacts/contracts/lib/SafeMath.ts +++ b/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/artifacts/contracts/lib/SafeMath.ts @@ -55,6 +55,8 @@ export interface SafeMathInterface extends utils.Interface { decodeFunctionResult(functionFragment: "sub", data: BytesLike): Result; events: {}; + + errors: {}; } export interface SafeMath extends BaseContract { diff --git a/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/externalArtifacts/ERC20.ts b/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/externalArtifacts/ERC20.ts index b4cdaf3c1..285ddd955 100644 --- a/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/externalArtifacts/ERC20.ts +++ b/packages/hardhat/test/fixture-projects/hardhat-project/typechain-types/externalArtifacts/ERC20.ts @@ -120,6 +120,8 @@ export interface ERC20Interface extends utils.Interface { getEvent(nameOrSignatureOrTopic: "Approval"): EventFragment; getEvent(nameOrSignatureOrTopic: "Transfer"): EventFragment; + + errors: {}; } export interface ApprovalEventObject { diff --git a/packages/target-ethers-v5-test/test/CustomError.test.ts b/packages/target-ethers-v5-test/test/CustomError.test.ts index 77a48b971..4e4ff4361 100644 --- a/packages/target-ethers-v5-test/test/CustomError.test.ts +++ b/packages/target-ethers-v5-test/test/CustomError.test.ts @@ -1,6 +1,3 @@ -import { BigNumber, ethers } from 'ethers' -import { AssertTrue, IsExact, typedAssert } from 'test-utils' - import type { CustomError } from '../types/v0.8.9/CustomError' import { createNewBlockchain, deployContract } from './common' @@ -17,18 +14,17 @@ describe('CustomError support', () => { contract = await deployContract(signer, 'CustomError') }) - afterEach(async () => { - }) + afterEach(async () => {}) it('typed error import', async () => { - try { - await contract.callStatic.transfer('0x0000000000000000000000000000000000000000', 1) - } catch (err) { - const expectedError = contract.interface.errors['InsufficientBalance(uint256,uint256,uint256)'] - console.log(expectedError) - console.log(err) - // TODO: still wip on how to best consume the errors here - // if(err.reason !== expectedError.) - } + try { + await contract.callStatic.transfer('0x0000000000000000000000000000000000000000', 1) + } catch (err) { + const expectedError = contract.interface.errors['InsufficientBalance(uint256,uint256)'] + // eslint-disable-next-line no-console + console.log(expectedError, err) + // TODO: still wip on how to best consume the errors here + // if(err.reason !== expectedError.) + } }) }) diff --git a/packages/target-ethers-v5-test/test/common.ts b/packages/target-ethers-v5-test/test/common.ts index cf5072cc8..7084b5573 100644 --- a/packages/target-ethers-v5-test/test/common.ts +++ b/packages/target-ethers-v5-test/test/common.ts @@ -26,7 +26,7 @@ export function createNewBlockchain(contractName: string) { const ctx: Partial = {} beforeEach(async () => { - const ganache = createGanacheServer({ logging: { quiet: false } }) + const ganache = createGanacheServer({ logging: { quiet: true } }) await ganache.listen(8545) diff --git a/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError/TestToken__factory.ts b/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError/TestToken__factory.ts deleted file mode 100644 index 566652e32..000000000 --- a/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError/TestToken__factory.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from "ethers"; -import type { Provider } from "@ethersproject/providers"; -import type { - TestToken, - TestTokenInterface, -} from "../../../v0.8.9/CustomError/TestToken"; - -const _abi = [ - { - inputs: [ - { - internalType: "uint256", - name: "available", - type: "uint256", - }, - { - internalType: "uint256", - name: "required", - type: "uint256", - }, - ], - name: "InsufficientBalance", - type: "error", - }, - { - inputs: [ - { - internalType: "address", - name: "to", - type: "address", - }, - { - internalType: "uint256", - name: "amount", - type: "uint256", - }, - ], - name: "transfer", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, -]; - -export class TestToken__factory { - static readonly abi = _abi; - static createInterface(): TestTokenInterface { - return new utils.Interface(_abi) as TestTokenInterface; - } - static connect( - address: string, - signerOrProvider: Signer | Provider - ): TestToken { - return new Contract(address, _abi, signerOrProvider) as TestToken; - } -} diff --git a/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError/index.ts b/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError/index.ts deleted file mode 100644 index 33edb32a2..000000000 --- a/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -export { TestToken__factory } from "./TestToken__factory"; diff --git a/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError__factory.ts b/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError__factory.ts index d7cc71ff6..fad383002 100644 --- a/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError__factory.ts +++ b/packages/target-ethers-v5-test/types/factories/v0.8.9/CustomError__factory.ts @@ -12,11 +12,6 @@ import type { const _abi = [ { inputs: [ - { - internalType: "uint256", - name: "magic", - type: "uint256", - }, { internalType: "uint256", name: "available", diff --git a/packages/target-ethers-v5-test/types/v0.8.9/CustomError.ts b/packages/target-ethers-v5-test/types/v0.8.9/CustomError.ts index 709d86fda..16782e6cd 100644 --- a/packages/target-ethers-v5-test/types/v0.8.9/CustomError.ts +++ b/packages/target-ethers-v5-test/types/v0.8.9/CustomError.ts @@ -43,14 +43,13 @@ export interface CustomErrorInterface extends utils.Interface { events: {}; errors: { - "InsufficientBalance(uint256,uint256,uint256)": ErrorFragment; + "InsufficientBalance(uint256,uint256)": ErrorFragment; }; getError(nameOrSignature: "InsufficientBalance"): ErrorFragment; } export interface InsufficientBalanceErrorObject { - magic: BigNumber; available: BigNumber; required: BigNumber; } diff --git a/packages/target-ethers-v5/src/codegen/errors.ts b/packages/target-ethers-v5/src/codegen/errors.ts index ea2717bd8..546feee0f 100644 --- a/packages/target-ethers-v5/src/codegen/errors.ts +++ b/packages/target-ethers-v5/src/codegen/errors.ts @@ -1,64 +1,58 @@ -import { - createPositionalIdentifier, - CustomErrorDeclaration, - getSignatureForFn, - } from 'typechain' - - import { generateOutputComplexTypeAsArray, generateOutputComplexTypesAsObject } from './types' - - - - export function generateErrorTypeExports(errors: CustomErrorDeclaration[]) { - if (errors.length === 1) { - return generateErrorTypeExport(errors[0], false) - } else { - return errors.map((e) => generateErrorTypeExport(e, true)).join('\n') - } +import { createPositionalIdentifier, CustomErrorDeclaration, getSignatureForFn } from 'typechain' + +import { generateOutputComplexTypesAsObject } from './types' + +export function generateErrorTypeExports(errors: CustomErrorDeclaration[]) { + if (errors.length === 1) { + return generateErrorTypeExport(errors[0], false) + } else { + return errors.map((e) => generateErrorTypeExport(e, true)).join('\n') } - - - function generateErrorTypeExport(error: CustomErrorDeclaration, includeArgTypes: boolean) { - const components = error.inputs.map((input, i) => ({ name: input.name ?? `arg${i.toString()}`, type: input.type })) - const objectOutput = generateOutputComplexTypesAsObject(components, { useStructs: true }) || '{}' - - const identifier = generateErrorIdentifier(error, { includeArgTypes }) - - return ` +} + +function generateErrorTypeExport(error: CustomErrorDeclaration, includeArgTypes: boolean) { + const components = error.inputs.map((input) => ({ name: input.name, type: input.type })) + const objectOutput = generateOutputComplexTypesAsObject(components, { useStructs: true }) || '{}' + + const identifier = generateErrorIdentifier(error, { includeArgTypes }) + + return ` export interface ${identifier}Object ${objectOutput}; ` +} + +export function generateInterfaceErrorDescription(error: CustomErrorDeclaration): string { + return `'${generateErrorSignature(error)}': ErrorFragment;` +} + +export function generateErrorSignature(error: CustomErrorDeclaration): string { + return `${error.name}(${error.inputs.map((input: any) => input.type.originalType).join(',')})` +} + +export function generateErrorInputs(errorArgs: CustomErrorDeclaration['inputs']) { + if (errorArgs.length === 0) { + return '' } - - export function generateInterfaceErrorDescription(error: CustomErrorDeclaration): string { - return `'${generateErrorSignature(error)}': ErrorFragment;` - } - - export function generateErrorSignature(error: CustomErrorDeclaration): string { - return `${error.name}(${error.inputs.map((input: any) => input.type.originalType).join(',')})` - } - - export function generateErrorInputs(errorArgs: CustomErrorDeclaration["inputs"]) { - if (errorArgs.length === 0) { - return '' - } - return ( - errorArgs - .map((arg, index) => { - return createPositionalIdentifier(arg.name) - }) - .join(', ') + ', ' - ) - } - - export function generateGetError(error: CustomErrorDeclaration, useSignature: boolean): string { - return `getError(nameOrSignature: '${ - useSignature ? generateErrorSignature(error) : error.name - }'): ErrorFragment;` - } - - function generateErrorIdentifier(error: CustomErrorDeclaration, { includeArgTypes }: { includeArgTypes?: boolean } = {}) { - if (includeArgTypes) { - return getSignatureForFn(error) + '_Error' - } else { - return error.name + 'Error' - } + return ( + errorArgs + .map((arg) => { + return createPositionalIdentifier(arg.name) + }) + .join(', ') + ', ' + ) +} + +export function generateGetError(error: CustomErrorDeclaration, useSignature: boolean): string { + return `getError(nameOrSignature: '${useSignature ? generateErrorSignature(error) : error.name}'): ErrorFragment;` +} + +function generateErrorIdentifier( + error: CustomErrorDeclaration, + { includeArgTypes }: { includeArgTypes?: boolean } = {}, +) { + if (includeArgTypes) { + return getSignatureForFn(error) + '_Error' + } else { + return error.name + 'Error' } +} diff --git a/packages/target-ethers-v5/src/codegen/index.ts b/packages/target-ethers-v5/src/codegen/index.ts index 0e3c5ff75..8499a1032 100644 --- a/packages/target-ethers-v5/src/codegen/index.ts +++ b/packages/target-ethers-v5/src/codegen/index.ts @@ -12,6 +12,7 @@ import { } from 'typechain' import { FACTORY_POSTFIX, STRUCT_INPUT_POSTFIX } from '../common' +import { generateErrorTypeExports, generateGetError, generateInterfaceErrorDescription } from './errors' import { EVENT_IMPORTS, EVENT_METHOD_OVERRIDES, @@ -20,11 +21,6 @@ import { generateGetEvent, generateInterfaceEventDescription, } from './events' -import { - generateInterfaceErrorDescription, - generateGetError, - generateErrorTypeExports -} from './errors' import { codegenFunctions, generateDecodeFunctionResultOverload, diff --git a/packages/target-ethers-v5/test/generation.test.ts b/packages/target-ethers-v5/test/generation.test.ts index 42d2f9154..d12559ae4 100644 --- a/packages/target-ethers-v5/test/generation.test.ts +++ b/packages/target-ethers-v5/test/generation.test.ts @@ -17,6 +17,7 @@ describe('Ethers generation edge cases', () => { rawName: 'TestContract', functions: {}, events: {}, + errors: {}, structs: {}, constructor: [{ name: 'constructor', inputs: [], outputs: [], stateMutability: 'nonpayable' }], path: [], diff --git a/packages/typechain/test/parser/abiParser.test.ts b/packages/typechain/test/parser/abiParser.test.ts index 43671f125..5b22d6cf5 100644 --- a/packages/typechain/test/parser/abiParser.test.ts +++ b/packages/typechain/test/parser/abiParser.test.ts @@ -374,18 +374,18 @@ describe('parseError', () => { const expectedError = { inputs: [ { - internalType: "uint256", - name: "magic", - type: "uint256" + internalType: 'uint256', + name: 'magic', + type: 'uint256', }, { - internalType: "address", - name: "target", - type: "address" + internalType: 'address', + name: 'target', + type: 'address', }, ], - "name": "InsufficientBalance", - "type": "error" + name: 'InsufficientBalance', + type: 'error', } as RawAbiDefinition const parsedError = parseCustomErrorDeclaration(expectedError, () => {}) @@ -395,7 +395,7 @@ describe('parseError', () => { { name: 'magic', type: { type: 'uinteger', bits: 256, originalType: 'uint256' } }, { name: 'target', type: { type: 'address', originalType: 'address' } }, ], - documentation: undefined + documentation: undefined, }) }) }) From fc4dfa7f2b467303227cde9e809c2be3d7b18847 Mon Sep 17 00:00:00 2001 From: fredlacs <32464905+fredlacs@users.noreply.github.com> Date: Sun, 1 May 2022 15:48:49 +0100 Subject: [PATCH 4/5] Fix copy pasta oversight --- packages/typechain/src/parser/abiParser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/typechain/src/parser/abiParser.ts b/packages/typechain/src/parser/abiParser.ts index bab958c16..6d916eaf9 100644 --- a/packages/typechain/src/parser/abiParser.ts +++ b/packages/typechain/src/parser/abiParser.ts @@ -318,7 +318,7 @@ export function parseCustomErrorDeclaration( registerStruct: (struct: StructType) => void, documentation?: DocumentationResult, ): CustomErrorDeclaration { - debug(`Parsing function declaration "${abiPiece.name}"`) + debug(`Parsing custom error declaration "${abiPiece.name}"`) return { name: abiPiece.name, inputs: abiPiece.inputs.map(parseRawAbiParameter.bind(null, registerStruct)), From c29ed324ac15d313380cb1beb50a5ee31a69ec5a Mon Sep 17 00:00:00 2001 From: fredlacs <32464905+fredlacs@users.noreply.github.com> Date: Sun, 1 May 2022 15:51:49 +0100 Subject: [PATCH 5/5] Remove old artifacts --- .../types/v0.8.9/CustomError/TestToken.ts | 40 ------------------- .../types/v0.8.9/CustomError/index.ts | 4 -- 2 files changed, 44 deletions(-) delete mode 100644 packages/target-web3-v1-test/types/v0.8.9/CustomError/TestToken.ts delete mode 100644 packages/target-web3-v1-test/types/v0.8.9/CustomError/index.ts diff --git a/packages/target-web3-v1-test/types/v0.8.9/CustomError/TestToken.ts b/packages/target-web3-v1-test/types/v0.8.9/CustomError/TestToken.ts deleted file mode 100644 index c925f1c15..000000000 --- a/packages/target-web3-v1-test/types/v0.8.9/CustomError/TestToken.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import type BN from "bn.js"; -import type { ContractOptions } from "web3-eth-contract"; -import type { EventLog } from "web3-core"; -import type { EventEmitter } from "events"; -import type { - Callback, - PayableTransactionObject, - NonPayableTransactionObject, - BlockType, - ContractEventLog, - BaseContract, -} from "../../types"; - -export interface EventOptions { - filter?: object; - fromBlock?: BlockType; - topics?: string[]; -} - -export interface TestToken extends BaseContract { - constructor( - jsonInterface: any[], - address?: string, - options?: ContractOptions - ): TestToken; - clone(): TestToken; - methods: { - transfer( - to: string, - amount: number | string | BN - ): NonPayableTransactionObject; - }; - events: { - allEvents(options?: EventOptions, cb?: Callback): EventEmitter; - }; -} diff --git a/packages/target-web3-v1-test/types/v0.8.9/CustomError/index.ts b/packages/target-web3-v1-test/types/v0.8.9/CustomError/index.ts deleted file mode 100644 index 14ee95744..000000000 --- a/packages/target-web3-v1-test/types/v0.8.9/CustomError/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -export type { TestToken } from "./TestToken";