From cdd99e7756d610019073c30de7e8ddc5974a57fb Mon Sep 17 00:00:00 2001 From: Muhammad Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Tue, 5 Dec 2023 04:50:57 +0100 Subject: [PATCH] Use cause instead of innerError (#6631) * use cause and deprecate innerError * fix linting errors * update test snapshots * remove cyclic dependencies to same file * fix issue with collecting response errors * fix linting issues * rename and format some files * add unit test for BaseWeb3Error --- .../basics/sign_and_send_tx/promi_event.md | 2 +- packages/web3-core/src/web3_context.ts | 55 ++++++++------- .../test/unit/web3_request_manager.test.ts | 2 +- packages/web3-errors/src/error_codes.ts | 1 + .../web3-errors/src/errors/contract_errors.ts | 15 ++-- .../web3-errors/src/errors/response_errors.ts | 19 +++-- .../src/errors/transaction_errors.ts | 4 +- packages/web3-errors/src/web3_error_base.ts | 54 ++++++++++++-- .../unit/__snapshots__/errors.test.ts.snap | 70 ++++++++++++++----- .../__snapshots__/rpc-errors.test.ts.snap | 17 +++++ .../test/unit/base-web3-error.test.ts | 50 +++++++++++++ packages/web3-eth-abi/test/fixtures/data.ts | 4 +- .../test/unit/decodeContractErrorData.test.ts | 2 +- packages/web3-eth-contract/src/contract.ts | 15 ++-- .../test/integration/contract_methods.test.ts | 2 +- .../contract_methods_errors.test.ts | 8 +-- .../test/unit/contract.test.ts | 2 +- .../web3-eth/src/utils/get_revert_reason.ts | 33 ++++----- ...ing.ts => watch_transaction_by_polling.ts} | 0 .../watch_transaction_by_subscription.ts | 2 +- .../watch_transaction_for_confirmations.ts | 2 +- .../test/integration/defaults.test.ts | 2 +- .../web3_eth/send_signed_transaction.test.ts | 6 +- .../watch_transaction_by_polling.test.ts | 2 +- ...atch_transaction_for_confirmations.test.ts | 6 +- .../ganache_fault_tolerance.test.ts | 2 +- 26 files changed, 265 insertions(+), 112 deletions(-) create mode 100644 packages/web3-errors/test/unit/base-web3-error.test.ts rename packages/web3-eth/src/utils/{watch_transaction_by_pooling.ts => watch_transaction_by_polling.ts} (100%) diff --git a/docs/docs/guides/basics/sign_and_send_tx/promi_event.md b/docs/docs/guides/basics/sign_and_send_tx/promi_event.md index 3d2403b4c46..b1372bb315f 100644 --- a/docs/docs/guides/basics/sign_and_send_tx/promi_event.md +++ b/docs/docs/guides/basics/sign_and_send_tx/promi_event.md @@ -71,7 +71,7 @@ web3.eth.sendTransaction({...}) // at Generator.next () // at fulfilled (.../web3_request_manager.js:5:58) // at processTicksAndRejections (node:internal/process/task_queues:96:5) { - // innerError: { code: -32000, message: 'exceeds block gas limit' }, + // cause: { code: -32000, message: 'exceeds block gas limit' }, // code: 101, // data: undefined, // request: { diff --git a/packages/web3-core/src/web3_context.ts b/packages/web3-core/src/web3_context.ts index f4c72f9ab61..76afcaf8875 100644 --- a/packages/web3-core/src/web3_context.ts +++ b/packages/web3-core/src/web3_context.ts @@ -103,7 +103,7 @@ export class Web3Context< | Web3ContextInitOptions, ) { super(); - + // If "providerOrContext" is provided as "string" or an objects matching "SupportedProviders" interface if ( isNullish(providerOrContext) || @@ -364,7 +364,7 @@ export class Web3Context< /** * This method allows extending the web3 modules. - * Note: This method is only for backward compatibility, and It is recommended to use Web3 v4 Plugin feature for extending web3.js functionality if you are developing some thing new. + * Note: This method is only for backward compatibility, and It is recommended to use Web3 v4 Plugin feature for extending web3.js functionality if you are developing something new. */ public extend(extendObj: ExtensionObject) { // @ts-expect-error No index signature with a parameter of type 'string' was found on type 'Web3Context' @@ -407,36 +407,39 @@ export class Web3Context< * class CustomPlugin extends Web3PluginBase {...} * ``` */ - export abstract class Web3PluginBase< - API extends Web3APISpec = Web3APISpec, +export abstract class Web3PluginBase< + API extends Web3APISpec = Web3APISpec, > extends Web3Context { - public abstract pluginNamespace: string; + public abstract pluginNamespace: string; - // eslint-disable-next-line class-methods-use-this - protected registerNewTransactionType>(type: Numbers, txClass: NewTxTypeClass): void { - TransactionFactory.registerTransactionType(type, txClass); - } + // eslint-disable-next-line class-methods-use-this + protected registerNewTransactionType>( + type: Numbers, + txClass: NewTxTypeClass, + ): void { + TransactionFactory.registerTransactionType(type, txClass); + } } /** -* 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: -* -* @example -* ```ts -* type CustomRpcApi = { -* custom_rpc_method: () => string; -* custom_rpc_method_with_parameters: (parameter1: string, parameter2: number) => string; -* }; -* -* class CustomPlugin extends Web3PluginBase {...} -* ``` -*/ + * 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: + * + * @example + * ```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 + API & EthExecutionAPI > {} // To avoid cycle dependency declare this type in this file diff --git a/packages/web3-core/test/unit/web3_request_manager.test.ts b/packages/web3-core/test/unit/web3_request_manager.test.ts index 21483ef13a2..375e27a5d1e 100644 --- a/packages/web3-core/test/unit/web3_request_manager.test.ts +++ b/packages/web3-core/test/unit/web3_request_manager.test.ts @@ -688,7 +688,7 @@ describe('Web3RequestManager', () => { err = error; } finally { expect(err).toBeInstanceOf(ResponseError); - expect(err.innerError).toEqual(rpcErrorResponse.error); + expect(err.cause).toEqual(rpcErrorResponse.error); } }); }); diff --git a/packages/web3-errors/src/error_codes.ts b/packages/web3-errors/src/error_codes.ts index c9ff68f6d7f..1db99b30ea7 100644 --- a/packages/web3-errors/src/error_codes.ts +++ b/packages/web3-errors/src/error_codes.ts @@ -28,6 +28,7 @@ export const ERR_OPERATION_ABORT = 204; export const ERR_ABI_ENCODING = 205; export const ERR_EXISTING_PLUGIN_NAMESPACE = 206; export const ERR_INVALID_METHOD_PARAMS = 207; +export const ERR_MULTIPLE_ERRORS = 208; // Contract error codes export const ERR_CONTRACT = 300; diff --git a/packages/web3-errors/src/errors/contract_errors.ts b/packages/web3-errors/src/errors/contract_errors.ts index 98a38245da6..3710d9f60f0 100644 --- a/packages/web3-errors/src/errors/contract_errors.ts +++ b/packages/web3-errors/src/errors/contract_errors.ts @@ -132,7 +132,7 @@ export type ProviderErrorData = | { originalError: { data: HexString } }; /** - * This class is expected to be set as an `innerError` inside ContractExecutionError + * This class is expected to be set as an `cause` inside ContractExecutionError * The properties would be typically decoded from the `data` if it was encoded according to EIP-838 */ export class Eip838ExecutionError extends Web3ContractError { @@ -144,7 +144,7 @@ export class Eip838ExecutionError extends Web3ContractError { public errorArgs?: { [K in string]: unknown }; // eslint-disable-next-line no-use-before-define - public innerError: Eip838ExecutionError | undefined; + public cause: Eip838ExecutionError | undefined; public constructor(error: JsonRpcError | Eip838ExecutionError) { super(error.message || 'Error'); @@ -166,9 +166,7 @@ export class Eip838ExecutionError extends Web3ContractError { originalError = error.data; } this.data = originalError.data; - this.innerError = new Eip838ExecutionError( - originalError as JsonRpcError, - ); + this.cause = new Eip838ExecutionError(originalError as JsonRpcError); } else { this.data = error.data; } @@ -192,7 +190,8 @@ export class Eip838ExecutionError extends Web3ContractError { name: string; code: number; message: string; - innerError: Error | Error[] | undefined; + innerError: Eip838ExecutionError | undefined; + cause: Eip838ExecutionError | undefined; data: string; errorName?: string; errorSignature?: string; @@ -216,12 +215,12 @@ export class Eip838ExecutionError extends Web3ContractError { * The data is expected to be encoded according to EIP-848. */ export class ContractExecutionError extends Web3ContractError { - public innerError: Eip838ExecutionError; + public cause: Eip838ExecutionError; public constructor(rpcError: JsonRpcError) { super('Error happened while trying to execute a function inside a smart contract'); this.code = ERR_CONTRACT_EXECUTION_REVERTED; - this.innerError = new Eip838ExecutionError(rpcError as JsonRpcError); + this.cause = new Eip838ExecutionError(rpcError as JsonRpcError); } } diff --git a/packages/web3-errors/src/errors/response_errors.ts b/packages/web3-errors/src/errors/response_errors.ts index 95d08b91ad6..9fb1f09e172 100644 --- a/packages/web3-errors/src/errors/response_errors.ts +++ b/packages/web3-errors/src/errors/response_errors.ts @@ -22,7 +22,7 @@ import { JsonRpcResponse, JsonRpcResponseWithError, } from 'web3-types'; -import { BaseWeb3Error } from '../web3_error_base.js'; +import { BaseWeb3Error, MultipleErrors } from '../web3_error_base.js'; import { ERR_INVALID_RESPONSE, ERR_RESPONSE } from '../error_codes.js'; // To avoid circular package dependency, copied to code here. If you update this please update same function in `json_rpc.ts` @@ -71,10 +71,14 @@ export class ResponseError extends B if (`error` in response) { errorOrErrors = response.error as JsonRpcError; } else if (response instanceof Array) { - errorOrErrors = response.map(r => r.error) as JsonRpcError[]; + errorOrErrors = response.filter(r => r.error).map(r => r.error) as JsonRpcError[]; } - this.innerError = errorOrErrors as Error | Error[] | undefined; + if (Array.isArray(errorOrErrors) && errorOrErrors.length > 0) { + this.cause = new MultipleErrors(errorOrErrors as unknown as Error[]); + } else { + this.cause = errorOrErrors as Error | undefined; + } } public toJSON() { @@ -98,7 +102,10 @@ export class InvalidResponseError ex } else if (result instanceof Array) { errorOrErrors = result.map(r => r.error) as JsonRpcError[]; } - - this.innerError = errorOrErrors as Error | Error[] | undefined; + if (Array.isArray(errorOrErrors)) { + this.cause = new MultipleErrors(errorOrErrors as unknown as Error[]); + } else { + this.cause = errorOrErrors as Error | undefined; + } } -} +} \ No newline at end of file diff --git a/packages/web3-errors/src/errors/transaction_errors.ts b/packages/web3-errors/src/errors/transaction_errors.ts index 69c56378724..9a62750142f 100644 --- a/packages/web3-errors/src/errors/transaction_errors.ts +++ b/packages/web3-errors/src/errors/transaction_errors.ts @@ -341,7 +341,7 @@ export class MissingGasError extends InvalidValueError { }`, '"gas" is missing', ); - this.innerError = new MissingGasInnerError(); + this.cause = new MissingGasInnerError(); } } @@ -372,7 +372,7 @@ export class TransactionGasMismatchError extends InvalidValueError { }`, 'transaction must specify legacy or fee market gas properties, not both', ); - this.innerError = new TransactionGasMismatchInnerError(); + this.cause = new TransactionGasMismatchInnerError(); } } diff --git a/packages/web3-errors/src/web3_error_base.ts b/packages/web3-errors/src/web3_error_base.ts index 72a6fd01731..40e96c3eda6 100644 --- a/packages/web3-errors/src/web3_error_base.ts +++ b/packages/web3-errors/src/web3_error_base.ts @@ -18,16 +18,50 @@ along with web3.js. If not, see . /* eslint-disable max-classes-per-file */ import { Web3Error } from 'web3-types'; +import { ERR_MULTIPLE_ERRORS } from './error_codes.js'; +/** + * Base class for Web3 errors. + */ export abstract class BaseWeb3Error extends Error implements Web3Error { public readonly name: string; public abstract readonly code: number; public stack: string | undefined; - public innerError: Error | Error[] | undefined; - public constructor(msg?: string, innerError?: Error | Error[]) { + public cause: Error | undefined; + + /** + * @deprecated Use the `cause` property instead. + */ + public get innerError(): Error | Error[] | undefined { + // eslint-disable-next-line no-use-before-define + if (this.cause instanceof MultipleErrors) { + return this.cause.errors; + } + return this.cause; + } + /** + * @deprecated Use the `cause` property instead. + */ + public set innerError(cause: Error | Error[] | undefined) { + if (Array.isArray(cause)) { + // eslint-disable-next-line no-use-before-define + this.cause = new MultipleErrors(cause); + } else { + this.cause = cause; + } + } + + public constructor(msg?: string, cause?: Error | Error[]) { super(msg); - this.innerError = innerError; + + if (Array.isArray(cause)) { + // eslint-disable-next-line no-use-before-define + this.cause = new MultipleErrors(cause); + } else { + this.cause = cause; + } + this.name = this.constructor.name; if (typeof Error.captureStackTrace === 'function') { @@ -57,11 +91,23 @@ export abstract class BaseWeb3Error extends Error implements Web3Error { name: this.name, code: this.code, message: this.message, - innerError: this.innerError, + cause: this.cause, + // deprecated + innerError: this.cause, }; } } +export class MultipleErrors extends BaseWeb3Error { + public code = ERR_MULTIPLE_ERRORS; + public errors: Error[]; + + public constructor(errors: Error[]) { + super(`Multiple errors occurred: [${errors.map(e => e.message).join('], [')}]`); + this.errors = errors; + } +} + export abstract class InvalidValueError extends BaseWeb3Error { public readonly name: string; diff --git a/packages/web3-errors/test/unit/__snapshots__/errors.test.ts.snap b/packages/web3-errors/test/unit/__snapshots__/errors.test.ts.snap index ab49d6aaa0a..5cfb35158c7 100644 --- a/packages/web3-errors/test/unit/__snapshots__/errors.test.ts.snap +++ b/packages/web3-errors/test/unit/__snapshots__/errors.test.ts.snap @@ -2,6 +2,7 @@ exports[`errors ConnectionCloseError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 504, "errorCode": 10, "errorReason": "reason", @@ -13,6 +14,7 @@ Object { exports[`errors ConnectionError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 500, "errorCode": 10, "errorReason": "reason", @@ -24,6 +26,7 @@ Object { exports[`errors ConnectionNotOpenError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 503, "errorCode": 10, "errorReason": "reason", @@ -35,6 +38,7 @@ Object { exports[`errors ConnectionTimeoutError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 502, "duration": 5000, "errorCode": undefined, @@ -47,6 +51,7 @@ Object { exports[`errors ContractCodeNotStoredError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 404, "innerError": undefined, "message": "The contract code couldn't be stored, please check your gas limit.", @@ -59,6 +64,7 @@ Object { exports[`errors ContractEventDoesNotExistError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 304, "eventName": "eventName", "innerError": undefined, @@ -69,6 +75,7 @@ Object { exports[`errors ContractMissingABIError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 302, "innerError": undefined, "message": "You must provide the json interface of the contract when instantiating a contract object.", @@ -78,6 +85,7 @@ Object { exports[`errors ContractMissingDeployDataError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 306, "innerError": undefined, "message": "No \\"data\\" specified in neither the given options, nor the default options.", @@ -87,6 +95,7 @@ Object { exports[`errors ContractNoAddressDefinedError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 307, "innerError": undefined, "message": "This contract object doesn't have address set yet, please set an address first.", @@ -96,6 +105,7 @@ Object { exports[`errors ContractNoFromAddressDefinedError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 308, "innerError": undefined, "message": "No \\"from\\" address specified in neither the given options, nor the default options.", @@ -105,6 +115,7 @@ Object { exports[`errors ContractOnceRequiresCallbackError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 303, "innerError": undefined, "message": "Once requires a callback as the second parameter.", @@ -114,6 +125,7 @@ Object { exports[`errors ContractReservedEventError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 305, "innerError": undefined, "message": "Event \\"type\\" doesn't exist in this contract.", @@ -124,6 +136,7 @@ Object { exports[`errors Eip838ExecutionError should get the data from error.data.data 1`] = ` Object { + "cause": [Eip838ExecutionError: Error], "code": undefined, "data": "0x0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016300000000000000000000000000000000000000000000000000000000000000", "innerError": [Eip838ExecutionError: Error], @@ -134,6 +147,7 @@ Object { exports[`errors Eip838ExecutionError should get the data from error.data.originalError.data 1`] = ` Object { + "cause": [Eip838ExecutionError: Error], "code": undefined, "data": "0x0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016300000000000000000000000000000000000000000000000000000000000000", "innerError": [Eip838ExecutionError: Error], @@ -144,6 +158,7 @@ Object { exports[`errors InvalidConnectionError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 501, "errorCode": 10, "errorReason": "reason", @@ -156,6 +171,7 @@ Object { exports[`errors InvalidNumberOfParamsError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 200, "expected": 20, "got": 10, @@ -168,6 +184,7 @@ Object { exports[`errors InvalidPropertiesForTransactionTypeError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 439, "innerError": undefined, "message": "The following properties are invalid for the transaction type 0x0: property", @@ -177,6 +194,7 @@ Object { exports[`errors InvalidProviderError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 601, "innerError": undefined, "message": "Provider with url \\"my url\\" is not set or invalid", @@ -186,6 +204,14 @@ Object { exports[`errors InvalidResponseError should have valid json structure 1`] = ` Object { + "cause": Object { + "code": 123, + "data": Object { + "a": "10", + "b": "20", + }, + "message": "error message", + }, "code": 101, "data": Object { "a": "10", @@ -207,6 +233,7 @@ Object { exports[`errors MaxAttemptsReachedOnReconnectingError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 505, "errorCode": undefined, "errorReason": undefined, @@ -218,6 +245,7 @@ Object { exports[`errors NoContractAddressFoundError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 403, "innerError": undefined, "message": "The transaction receipt didn't contain a contract address.", @@ -230,6 +258,7 @@ Object { exports[`errors PendingRequestsOnReconnectingError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 506, "errorCode": undefined, "errorReason": undefined, @@ -242,6 +271,7 @@ Object { exports[`errors ResolverMethodMissingError should have valid json structure 1`] = ` Object { "address": "address", + "cause": undefined, "code": 301, "innerError": undefined, "message": "The resolver at address does not implement requested method: \\"name\\".", @@ -251,6 +281,14 @@ Object { exports[`errors ResponseError should have valid json structure with data 1`] = ` Object { + "cause": Object { + "code": 123, + "data": Object { + "a": "10", + "b": "20", + }, + "message": "error message", + }, "code": 100, "data": Object { "a": "10", @@ -272,6 +310,11 @@ Object { exports[`errors ResponseError should have valid json structure without data 1`] = ` Object { + "cause": Object { + "code": 123, + "data": undefined, + "message": "error message", + }, "code": 100, "data": undefined, "innerError": Object { @@ -287,6 +330,7 @@ Object { exports[`errors ResponseError should include the array of inner errors 1`] = ` Object { + "cause": [MultipleErrors: Multiple errors occurred: [error message], [error message]], "code": 100, "data": Array [ Object { @@ -298,24 +342,7 @@ Object { "d": "40", }, ], - "innerError": Array [ - Object { - "code": 123, - "data": Object { - "a": "10", - "b": "20", - }, - "message": "error message", - }, - Object { - "code": 124, - "data": Object { - "c": "30", - "d": "40", - }, - "message": "error message", - }, - ], + "innerError": [MultipleErrors: Multiple errors occurred: [error message], [error message]], "message": "Returned error: error message,error message", "name": "ResponseError", "request": undefined, @@ -324,6 +351,7 @@ Object { exports[`errors RevertInstructionError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 401, "innerError": undefined, "message": "Your request got reverted with the following reason string: message", @@ -335,6 +363,7 @@ Object { exports[`errors SchemaFormatError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 1200, "innerError": undefined, "message": "Format for the type unsupported is unsupported", @@ -345,6 +374,7 @@ Object { exports[`errors TransactionError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 400, "innerError": undefined, "message": "message", @@ -357,6 +387,7 @@ Object { exports[`errors TransactionOutOfGasError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 406, "innerError": undefined, "message": "Transaction ran out of gas. Please provide more gas: @@ -372,6 +403,7 @@ Object { exports[`errors TransactionRevertInstructionError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 402, "data": undefined, "innerError": undefined, @@ -388,6 +420,7 @@ Object { exports[`errors TransactionRevertWithCustomError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 438, "customErrorArguments": Object { "customErrorArgument": "customErrorArgument", @@ -408,6 +441,7 @@ Object { exports[`errors TransactionRevertedWithoutReasonError should have valid json structure 1`] = ` Object { + "cause": undefined, "code": 405, "innerError": undefined, "message": "Transaction has been reverted by the EVM: diff --git a/packages/web3-errors/test/unit/__snapshots__/rpc-errors.test.ts.snap b/packages/web3-errors/test/unit/__snapshots__/rpc-errors.test.ts.snap index 93330d6576f..687b9888aab 100644 --- a/packages/web3-errors/test/unit/__snapshots__/rpc-errors.test.ts.snap +++ b/packages/web3-errors/test/unit/__snapshots__/rpc-errors.test.ts.snap @@ -2,6 +2,7 @@ exports[`rpc errors EIP1193ProviderRpcError test constructor with a known code (1000) 1`] = ` Object { + "cause": undefined, "code": 1000, "innerError": undefined, "message": "The connection successfully completed the purpose for which it was created.", @@ -11,6 +12,7 @@ Object { exports[`rpc errors EIP1193ProviderRpcError test constructor with no code 1`] = ` Object { + "cause": undefined, "code": undefined, "innerError": undefined, "message": "", @@ -20,6 +22,7 @@ Object { exports[`rpc errors EIP1193ProviderRpcError test constructor with un registered code 1`] = ` Object { + "cause": undefined, "code": 99999, "innerError": undefined, "message": "An Rpc error has occured with a code of 99999", @@ -29,6 +32,7 @@ Object { exports[`rpc errors InternalError test constructor 1`] = ` Object { + "cause": undefined, "code": -32603, "error": Object { "code": 123, @@ -48,6 +52,7 @@ Object { exports[`rpc errors InvalidInputError test constructor 1`] = ` Object { + "cause": undefined, "code": -32000, "error": Object { "code": 123, @@ -67,6 +72,7 @@ Object { exports[`rpc errors InvalidParamsError test constructor 1`] = ` Object { + "cause": undefined, "code": -32602, "error": Object { "code": 123, @@ -86,6 +92,7 @@ Object { exports[`rpc errors InvalidRequestError test constructor 1`] = ` Object { + "cause": undefined, "code": -32600, "error": Object { "code": 123, @@ -105,6 +112,7 @@ Object { exports[`rpc errors LimitExceededError test constructor 1`] = ` Object { + "cause": undefined, "code": -32005, "error": Object { "code": 123, @@ -124,6 +132,7 @@ Object { exports[`rpc errors MethodNotFoundError test constructor 1`] = ` Object { + "cause": undefined, "code": -32601, "error": Object { "code": 123, @@ -143,6 +152,7 @@ Object { exports[`rpc errors MethodNotSupported test constructor 1`] = ` Object { + "cause": undefined, "code": -32004, "error": Object { "code": 123, @@ -162,6 +172,7 @@ Object { exports[`rpc errors ParseError test constructor 1`] = ` Object { + "cause": undefined, "code": -32700, "error": Object { "code": 123, @@ -181,6 +192,7 @@ Object { exports[`rpc errors ResourceUnavailableError test constructor 1`] = ` Object { + "cause": undefined, "code": -32002, "error": Object { "code": 123, @@ -200,6 +212,7 @@ Object { exports[`rpc errors ResourcesNotFoundError test constructor 1`] = ` Object { + "cause": undefined, "code": -32001, "error": Object { "code": 123, @@ -219,6 +232,7 @@ Object { exports[`rpc errors TransactionRejectedError test constructor 1`] = ` Object { + "cause": undefined, "code": -32003, "error": Object { "code": 123, @@ -238,6 +252,7 @@ Object { exports[`rpc errors VersionNotSupportedError test constructor 1`] = ` Object { + "cause": undefined, "code": -32006, "error": Object { "code": 123, @@ -257,6 +272,7 @@ Object { exports[`rpc errors rpcErrors.RpcError test constructor 1`] = ` Object { + "cause": undefined, "code": 123, "error": Object { "code": 123, @@ -276,6 +292,7 @@ Object { exports[`rpc errors rpcErrors.RpcError test constructor with custom message 1`] = ` Object { + "cause": undefined, "code": 123, "error": Object { "code": 123, diff --git a/packages/web3-errors/test/unit/base-web3-error.test.ts b/packages/web3-errors/test/unit/base-web3-error.test.ts new file mode 100644 index 00000000000..dad318f30df --- /dev/null +++ b/packages/web3-errors/test/unit/base-web3-error.test.ts @@ -0,0 +1,50 @@ +/* +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 { BaseWeb3Error, MultipleErrors } from '../../src/web3_error_base'; + +class CustomError extends BaseWeb3Error { + public code = 0; +} +describe('BaseWeb3Error', () => { + it('innerError is readable from cause', () => { + const error = new CustomError('outer error'); + const innerError = new Error('inner error'); + error.cause = innerError; + // eslint-disable-next-line deprecation/deprecation + expect(error.innerError).toBe(innerError); + }); + it('case is set from innerError', () => { + const error = new CustomError('outer error'); + const cause = new Error('inner error'); + // eslint-disable-next-line deprecation/deprecation + error.innerError = cause; + expect(error.cause).toBe(cause); + }); + it('case is set from innerError array', () => { + const error = new CustomError('outer error'); + const innerErrors = [new Error('inner error1'), new Error('inner error2')]; + // eslint-disable-next-line deprecation/deprecation + error.innerError = innerErrors; + expect((error.cause as MultipleErrors).errors).toBe(innerErrors); + }); + it('case is set from array of errors at the constructor', () => { + const innerErrors = [new Error('inner error1'), new Error('inner error2')]; + const error = new CustomError('outer error', innerErrors); + expect((error.cause as MultipleErrors).errors).toBe(innerErrors); + }); +}); diff --git a/packages/web3-eth-abi/test/fixtures/data.ts b/packages/web3-eth-abi/test/fixtures/data.ts index a2f3b32ba20..aeefb7a9541 100644 --- a/packages/web3-eth-abi/test/fixtures/data.ts +++ b/packages/web3-eth-abi/test/fixtures/data.ts @@ -1031,7 +1031,7 @@ export const validDecodeContractErrorData: { code: 42, message: 'This is an error with params', }, - innerError: { + cause: { code: -32000, }, }, @@ -1067,7 +1067,7 @@ export const validDecodeContractErrorData: { code: 42, message: 'This is an error with params', }, - innerError: { + cause: { code: 3, }, }, diff --git a/packages/web3-eth-abi/test/unit/decodeContractErrorData.test.ts b/packages/web3-eth-abi/test/unit/decodeContractErrorData.test.ts index c130af64c4a..3f6a624ef22 100644 --- a/packages/web3-eth-abi/test/unit/decodeContractErrorData.test.ts +++ b/packages/web3-eth-abi/test/unit/decodeContractErrorData.test.ts @@ -33,7 +33,7 @@ describe('decodeContractErrorData', () => { expect(err.errorSignature).toEqual(output.errorSignature); expect(err.errorArgs?.message).toEqual(output.errorArgs?.message); expect(Number(err.errorArgs?.code)).toEqual(output.errorArgs?.code); - expect(err.innerError?.code).toEqual(output.innerError?.code); + expect(err.cause?.code).toEqual(output.cause?.code); }, ); }); diff --git a/packages/web3-eth-contract/src/contract.ts b/packages/web3-eth-contract/src/contract.ts index 197762c3554..03166fec709 100644 --- a/packages/web3-eth-contract/src/contract.ts +++ b/packages/web3-eth-contract/src/contract.ts @@ -371,9 +371,8 @@ export class Contract : returnFormat ?? DEFAULT_RETURN_FORMAT; const address = typeof addressOrOptionsOrContext === 'string' ? addressOrOptionsOrContext : undefined; - this.config.contractDataInputFill = - (options as ContractInitOptions)?.dataInputFill ?? - this.config.contractDataInputFill; + this.config.contractDataInputFill = + (options as ContractInitOptions)?.dataInputFill ?? this.config.contractDataInputFill; this._parseAndSetJsonInterface(jsonInterface, returnDataFormat); if (!isNullish(address)) { @@ -1028,7 +1027,7 @@ export class Contract } catch (error: unknown) { if (error instanceof ContractExecutionError) { // this will parse the error data by trying to decode the ABI error inputs according to EIP-838 - decodeContractErrorData(errorsAbi, error.innerError); + decodeContractErrorData(errorsAbi, error.cause); } throw error; } @@ -1058,7 +1057,7 @@ export class Contract } catch (error: unknown) { if (error instanceof ContractExecutionError) { // this will parse the error data by trying to decode the ABI error inputs according to EIP-838 - decodeContractErrorData(errorsAbi, error.innerError); + decodeContractErrorData(errorsAbi, error.cause); } throw error; } @@ -1083,18 +1082,18 @@ export class Contract options: { ...options, dataInputFill: this.config.contractDataInputFill }, contractOptions: modifiedContractOptions, }); - + const transactionToSend = sendTransaction(this, tx, DEFAULT_RETURN_FORMAT, { // TODO Should make this configurable by the user checkRevertBeforeSending: false, contractAbi: this._jsonInterface, }); - + // eslint-disable-next-line no-void void transactionToSend.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 - decodeContractErrorData(errorsAbi, error.innerError); + decodeContractErrorData(errorsAbi, error.cause); } }); return transactionToSend; diff --git a/packages/web3-eth-contract/test/integration/contract_methods.test.ts b/packages/web3-eth-contract/test/integration/contract_methods.test.ts index dc28f17799a..a661854932a 100644 --- a/packages/web3-eth-contract/test/integration/contract_methods.test.ts +++ b/packages/web3-eth-contract/test/integration/contract_methods.test.ts @@ -81,7 +81,7 @@ describe('contract', () => { // eslint-disable-next-line jest/no-standalone-expect expect(error).toBeDefined(); // eslint-disable-next-line jest/no-standalone-expect - expect(error?.innerError.message).toContain('REVERTED WITH REVERT'); + expect(error?.cause.message).toContain('REVERTED WITH REVERT'); }); }); }); diff --git a/packages/web3-eth-contract/test/integration/contract_methods_errors.test.ts b/packages/web3-eth-contract/test/integration/contract_methods_errors.test.ts index e51accfa09b..ccd27987b27 100644 --- a/packages/web3-eth-contract/test/integration/contract_methods_errors.test.ts +++ b/packages/web3-eth-contract/test/integration/contract_methods_errors.test.ts @@ -68,7 +68,7 @@ describe('contract errors', () => { 'Error happened while trying to execute a function inside a smart contract', ), code: ERR_CONTRACT_EXECUTION_REVERTED, - innerError: { + cause: { errorName: 'Unauthorized', errorSignature: 'Unauthorized()', }, @@ -80,7 +80,7 @@ describe('contract errors', () => { name: 'ContractExecutionError', code: ERR_CONTRACT_EXECUTION_REVERTED, receipt: undefined, - innerError: { + cause: { code: 3, data: '0x82b42900', errorName: 'Unauthorized', @@ -113,7 +113,7 @@ describe('contract errors', () => { 'Error happened while trying to execute a function inside a smart contract', ), code: ERR_CONTRACT_EXECUTION_REVERTED, - innerError: { + cause: { errorName: 'CustomError', errorSignature: 'CustomError(string)', errorArgs: { @@ -128,7 +128,7 @@ describe('contract errors', () => { name: 'ContractExecutionError', code: ERR_CONTRACT_EXECUTION_REVERTED, receipt: undefined, - innerError: { + cause: { code: 3, data: '0x8d6ea8be0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001b7265766572746564207573696e6720637573746f6d204572726f720000000000', errorName: 'CustomError', diff --git a/packages/web3-eth-contract/test/unit/contract.test.ts b/packages/web3-eth-contract/test/unit/contract.test.ts index bee7d90999c..828666243a2 100644 --- a/packages/web3-eth-contract/test/unit/contract.test.ts +++ b/packages/web3-eth-contract/test/unit/contract.test.ts @@ -1146,7 +1146,7 @@ describe('Contract', () => { }), ).rejects.toThrow( expect.objectContaining({ - innerError: expect.any(Error), + cause: expect.any(Error), }), ); diff --git a/packages/web3-eth/src/utils/get_revert_reason.ts b/packages/web3-eth/src/utils/get_revert_reason.ts index 4b4b7d596a9..ae4fc32dbfa 100644 --- a/packages/web3-eth/src/utils/get_revert_reason.ts +++ b/packages/web3-eth/src/utils/get_revert_reason.ts @@ -16,7 +16,7 @@ along with web3.js. If not, see . */ import { Web3Context } from 'web3-core'; -import { ContractExecutionError, Eip838ExecutionError, InvalidResponseError } from 'web3-errors'; +import { ContractExecutionError, Eip838ExecutionError, InvalidResponseError , MultipleErrors } from 'web3-errors'; import { decodeContractErrorData, isAbiErrorFragment } from 'web3-eth-abi'; import { AbiErrorFragment, @@ -32,39 +32,36 @@ import { call } from '../rpc_method_wrappers.js'; import { RevertReason, RevertReasonWithCustomError } from '../types.js'; export const parseTransactionError = (error: unknown, contractAbi?: ContractAbi) => { - if ( - error instanceof ContractExecutionError && - error.innerError instanceof Eip838ExecutionError - ) { + if (error instanceof ContractExecutionError && error.cause instanceof Eip838ExecutionError) { if (contractAbi !== undefined) { const errorsAbi = contractAbi.filter(abi => isAbiErrorFragment(abi), ) as unknown as AbiErrorFragment[]; - decodeContractErrorData(errorsAbi, error.innerError); + decodeContractErrorData(errorsAbi, error.cause); return { - reason: error.innerError.message, - signature: error.innerError.data?.slice(0, 10), - data: error.innerError.data?.substring(10), - customErrorName: error.innerError.errorName, - customErrorDecodedSignature: error.innerError.errorSignature, - customErrorArguments: error.innerError.errorArgs, + reason: error.cause.message, + signature: error.cause.data?.slice(0, 10), + data: error.cause.data?.substring(10), + customErrorName: error.cause.errorName, + customErrorDecodedSignature: error.cause.errorSignature, + customErrorArguments: error.cause.errorArgs, } as RevertReasonWithCustomError; } return { - reason: error.innerError.message, - signature: error.innerError.data?.slice(0, 10), - data: error.innerError.data?.substring(10), + reason: error.cause.message, + signature: error.cause.data?.slice(0, 10), + data: error.cause.data?.substring(10), } as RevertReason; } if ( error instanceof InvalidResponseError && - !Array.isArray(error.innerError) && - error.innerError !== undefined + !Array.isArray((error.cause as MultipleErrors)?.errors) && + error.cause !== undefined ) { - return error.innerError.message; + return error.cause.message; } throw error; diff --git a/packages/web3-eth/src/utils/watch_transaction_by_pooling.ts b/packages/web3-eth/src/utils/watch_transaction_by_polling.ts similarity index 100% rename from packages/web3-eth/src/utils/watch_transaction_by_pooling.ts rename to packages/web3-eth/src/utils/watch_transaction_by_polling.ts diff --git a/packages/web3-eth/src/utils/watch_transaction_by_subscription.ts b/packages/web3-eth/src/utils/watch_transaction_by_subscription.ts index 8cdaa8fc214..d68c39da95d 100644 --- a/packages/web3-eth/src/utils/watch_transaction_by_subscription.ts +++ b/packages/web3-eth/src/utils/watch_transaction_by_subscription.ts @@ -20,7 +20,7 @@ import { format } from 'web3-utils'; import { DataFormat } from 'web3-types'; import { NewHeadsSubscription } from '../web3_subscriptions.js'; import { transactionReceiptSchema } from '../schemas.js'; -import { WaitProps, watchTransactionByPolling } from './watch_transaction_by_pooling.js'; +import { WaitProps, watchTransactionByPolling } from './watch_transaction_by_polling.js'; /** * This function watches a Transaction by subscribing to new heads. diff --git a/packages/web3-eth/src/utils/watch_transaction_for_confirmations.ts b/packages/web3-eth/src/utils/watch_transaction_for_confirmations.ts index e6826fea10d..60489d59c50 100644 --- a/packages/web3-eth/src/utils/watch_transaction_for_confirmations.ts +++ b/packages/web3-eth/src/utils/watch_transaction_for_confirmations.ts @@ -28,7 +28,7 @@ import { transactionReceiptSchema } from '../schemas.js'; import { watchTransactionByPolling, Web3PromiEventEventTypeBase, -} from './watch_transaction_by_pooling.js'; +} from './watch_transaction_by_polling.js'; import { watchTransactionBySubscription } from './watch_transaction_by_subscription.js'; export function watchTransactionForConfirmations< diff --git a/packages/web3-eth/test/integration/defaults.test.ts b/packages/web3-eth/test/integration/defaults.test.ts index 3a17d9449de..a9651fa20e3 100644 --- a/packages/web3-eth/test/integration/defaults.test.ts +++ b/packages/web3-eth/test/integration/defaults.test.ts @@ -584,7 +584,7 @@ describe('defaults', () => { confirmations: bigint; receipt: { status: bigint }; }) => { - // Being able to get 2 confirmations means the pooling for new blocks works + // Being able to get 2 confirmations means the polling for new blocks works if (confirmations >= 2) { sentTx.removeAllListeners(); resolve(status); 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 1fe6c4f4c63..66587e6e90f 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 @@ -409,7 +409,7 @@ describe('Web3Eth.sendSignedTransaction', () => { const expectedThrownError = { name: 'TransactionRevertInstructionError', - innerError: undefined, + cause: undefined, reason: getSystemTestBackend() === 'geth' ? expect.stringContaining( @@ -445,7 +445,7 @@ describe('Web3Eth.sendSignedTransaction', () => { const expectedThrownError = { name: 'TransactionRevertInstructionError', message: 'Transaction has been reverted by the EVM', - innerError: undefined, + cause: undefined, reason: getSystemTestBackend() === 'geth' ? expect.stringContaining( @@ -600,7 +600,7 @@ describe('Web3Eth.sendSignedTransaction', () => { const expectedThrownError = { name: 'TransactionRevertInstructionError', - innerError: undefined, + cause: undefined, reason: getSystemTestBackend() === 'geth' ? 'execution reverted: This is a send revert' diff --git a/packages/web3-eth/test/unit/utils/watch_transaction_by_polling.test.ts b/packages/web3-eth/test/unit/utils/watch_transaction_by_polling.test.ts index 5a44db457e8..125a6432274 100644 --- a/packages/web3-eth/test/unit/utils/watch_transaction_by_polling.test.ts +++ b/packages/web3-eth/test/unit/utils/watch_transaction_by_polling.test.ts @@ -21,7 +21,7 @@ import { ethRpcMethods } from 'web3-rpc-methods'; import * as rpcMethodWrappers from '../../../src/rpc_method_wrappers'; import * as WaitForTransactionReceipt from '../../../src/utils/wait_for_transaction_receipt'; -import * as WatchTransactionByPolling from '../../../src/utils/watch_transaction_by_pooling'; +import * as WatchTransactionByPolling from '../../../src/utils/watch_transaction_by_polling'; import { expectedTransactionReceipt, expectedTransactionHash, diff --git a/packages/web3-eth/test/unit/utils/watch_transaction_for_confirmations.test.ts b/packages/web3-eth/test/unit/utils/watch_transaction_for_confirmations.test.ts index 21f630c8bd1..d4a73939b05 100644 --- a/packages/web3-eth/test/unit/utils/watch_transaction_for_confirmations.test.ts +++ b/packages/web3-eth/test/unit/utils/watch_transaction_for_confirmations.test.ts @@ -26,7 +26,7 @@ import * as rpcMethodWrappers from '../../../src/rpc_method_wrappers'; import * as WaitForTransactionReceipt from '../../../src/utils/wait_for_transaction_receipt'; import * as WatchTransactionForConfirmations from '../../../src/utils/watch_transaction_for_confirmations'; -import * as WatchTransactionByPolling from '../../../src/utils/watch_transaction_by_pooling'; +import * as WatchTransactionByPolling from '../../../src/utils/watch_transaction_by_polling'; import * as WatchTransactionBySubscription from '../../../src/utils/watch_transaction_by_subscription'; import { expectedTransactionReceipt, @@ -38,7 +38,7 @@ import { SendSignedTransactionEvents } from '../../../src/types'; jest.mock('web3-rpc-methods'); jest.mock('../../../src/utils/wait_for_transaction_receipt'); -jest.mock('../../../src/utils/watch_transaction_by_pooling'); +jest.mock('../../../src/utils/watch_transaction_by_polling'); jest.mock('../../../src/utils/watch_transaction_by_subscription'); const testMessage = @@ -164,7 +164,7 @@ describe('watchTransactionForConfirmations', () => { ); }); - describe('should call watchTransactionByPooling when the provider does not support subscription', () => { + describe('should call watchTransactionByPolling when the provider does not support subscription', () => { let web3Context: Web3Context; beforeAll(() => { diff --git a/packages/web3-providers-ws/test/integration/ganache_fault_tolerance.test.ts b/packages/web3-providers-ws/test/integration/ganache_fault_tolerance.test.ts index 4a0ba982996..30078527ca2 100644 --- a/packages/web3-providers-ws/test/integration/ganache_fault_tolerance.test.ts +++ b/packages/web3-providers-ws/test/integration/ganache_fault_tolerance.test.ts @@ -267,7 +267,7 @@ describeIf(getSystemTestBackend() === 'ganache' && isWs)('ganache tests', () => const errorPromise = new Promise(resolve => { webSocketProvider.on('error', (err: any) => { expect(err).toBeInstanceOf(InvalidResponseError); - if (err.innerError.message === 'Chunk timeout') { + if (err.cause.message === 'Chunk timeout') { resolve(true); } });