diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b2f21f1784..cee2db1aa8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -895,6 +895,10 @@ should use 4.0.1-alpha.0 for testing. ### Added +#### web3-eth-contract + +- Decoding error data, using Error ABI if available, if error was returned from a smart contract function call (#5662). + #### web3-types - These types were moved from `web3-eth-accounts` to `web3-types` package: Cipher, CipherOptions, ScryptParams, PBKDF2SHA256Params, KeyStore (#5581 ) diff --git a/packages/web3-eth-contract/CHANGELOG.md b/packages/web3-eth-contract/CHANGELOG.md index 3062d11eb5b..7602457ec36 100644 --- a/packages/web3-eth-contract/CHANGELOG.md +++ b/packages/web3-eth-contract/CHANGELOG.md @@ -186,6 +186,10 @@ const transactionHash = receipt.transactionHash; ## [Unreleased] +### Added + +- Decoding error data, using Error ABI if available, if error was returned from a smart contract function call (#5662). + ### Fixed - Emit past contract events based on `fromBlock` when passed to `contract.events.someEventName` (#5201) diff --git a/packages/web3-eth-contract/src/contract.ts b/packages/web3-eth-contract/src/contract.ts index 6f27d4fb4cc..3ef46996978 100644 --- a/packages/web3-eth-contract/src/contract.ts +++ b/packages/web3-eth-contract/src/contract.ts @@ -1038,7 +1038,7 @@ export class Contract call: async (options?: PayableCallOptions, block?: BlockNumberOrTag) => this._contractMethodCall(methodAbi, abiParams, errorsAbis, options, block), send: (options?: PayableTxOptions) => - this._contractMethodSend(methodAbi, abiParams, options), // TODO: refactor to parse errorsAbi #5587 + this._contractMethodSend(methodAbi, abiParams, errorsAbis, options), estimateGas: async < ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT, >( @@ -1062,7 +1062,7 @@ export class Contract call: async (options?: NonPayableCallOptions, block?: BlockNumberOrTag) => this._contractMethodCall(methodAbi, abiParams, errorsAbis, options, block), send: (options?: NonPayableTxOptions) => - this._contractMethodSend(methodAbi, abiParams, options), // TODO: refactor to parse errorsAbi #5587 + this._contractMethodSend(methodAbi, abiParams, errorsAbis, options), estimateGas: async ( options?: NonPayableCallOptions, returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, @@ -1081,13 +1081,10 @@ export class Contract }; } - private async _contractMethodCall< - E extends AbiErrorFragment, - Options extends PayableCallOptions | NonPayableCallOptions, - >( + private async _contractMethodCall( abi: AbiFunctionFragment, params: unknown[], - errorsAbi: E[], + errorsAbi: AbiErrorFragment[], options?: Options, block?: BlockNumberOrTag, ) { @@ -1115,6 +1112,7 @@ export class Contract private _contractMethodSend( abi: AbiFunctionFragment, params: unknown[], + errorsAbi: AbiErrorFragment[], options?: Options, contractOptions?: ContractOptions, ) { @@ -1132,7 +1130,17 @@ export class Contract contractOptions: modifiedContractOptions, }); - return sendTransaction(this, tx, DEFAULT_RETURN_FORMAT); + const promiEvent = sendTransaction(this, tx, DEFAULT_RETURN_FORMAT); + + // eslint-disable-next-line @typescript-eslint/no-floating-promises + promiEvent.on('error', (error: unknown) => { + if (error instanceof ContractExecutionError) { + // this will parse the error data by trying to decode the ABI error inputs according to EIP-838 + decodeErrorData(errorsAbi, error.innerError); + } + }); + + return promiEvent; } private _contractMethodDeploySend( diff --git a/packages/web3-eth-contract/test/unit/contract.test.ts b/packages/web3-eth-contract/test/unit/contract.test.ts index f1ce2cd11ec..19d5f633fb3 100644 --- a/packages/web3-eth-contract/test/unit/contract.test.ts +++ b/packages/web3-eth-contract/test/unit/contract.test.ts @@ -144,11 +144,13 @@ describe('Contract', () => { ) { // 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, @typescript-eslint/no-empty-function + return { status: '0x1', on: () => {} } as any; } - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return Promise.resolve(newContract) as any; + + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-empty-function + return Promise.resolve(Object.assign(newContract, { on: () => {} })) as any; }); const deployedContract = await contract