/* 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 . */ /* eslint-disable max-classes-per-file */ import { JsonRpcError, TransactionReceipt, HexString } from '@theqrl/web3-types'; import { ERR_CONTRACT, ERR_CONTRACT_ABI_MISSING, ERR_CONTRACT_EXECUTION_REVERTED, ERR_CONTRACT_EVENT_NOT_EXISTS, ERR_CONTRACT_INSTANTIATION, ERR_CONTRACT_MISSING_ADDRESS, ERR_CONTRACT_MISSING_DEPLOY_DATA, ERR_CONTRACT_MISSING_FROM_ADDRESS, ERR_CONTRACT_REQUIRED_CALLBACK, ERR_CONTRACT_RESERVED_EVENT, ERR_CONTRACT_RESOLVER_MISSING, ERR_CONTRACT_TX_DATA_AND_INPUT, } from '../error_codes.js'; import { BaseWeb3Error, InvalidValueError } from '../web3_error_base.js'; export class Web3ContractError extends BaseWeb3Error { public code = ERR_CONTRACT; public receipt?: TransactionReceipt; public constructor(message: string, receipt?: TransactionReceipt) { super(message); this.receipt = receipt; } } export class ResolverMethodMissingError extends BaseWeb3Error { public code = ERR_CONTRACT_RESOLVER_MISSING; public constructor(public address: string, public name: string) { super(`The resolver at ${address} does not implement requested method: "${name}".`); } public toJSON() { return { ...super.toJSON(), address: this.address, name: this.name }; } } export class ContractMissingABIError extends BaseWeb3Error { public code = ERR_CONTRACT_ABI_MISSING; public constructor() { super( 'You must provide the json interface of the contract when instantiating a contract object.', ); } } export class ContractOnceRequiresCallbackError extends BaseWeb3Error { public code = ERR_CONTRACT_REQUIRED_CALLBACK; public constructor() { super('Once requires a callback as the second parameter.'); } } export class ContractEventDoesNotExistError extends BaseWeb3Error { public code = ERR_CONTRACT_EVENT_NOT_EXISTS; public constructor(public eventName: string) { super(`Event "${eventName}" doesn't exist in this contract.`); } public toJSON() { return { ...super.toJSON(), eventName: this.eventName }; } } export class ContractReservedEventError extends BaseWeb3Error { public code = ERR_CONTRACT_RESERVED_EVENT; public constructor(public type: string) { super(`Event "${type}" doesn't exist in this contract.`); } public toJSON() { return { ...super.toJSON(), type: this.type }; } } export class ContractMissingDeployDataError extends BaseWeb3Error { public code = ERR_CONTRACT_MISSING_DEPLOY_DATA; public constructor() { super(`No "data" specified in neither the given options, nor the default options.`); } } export class ContractNoAddressDefinedError extends BaseWeb3Error { public code = ERR_CONTRACT_MISSING_ADDRESS; public constructor() { super("This contract object doesn't have address set yet, please set an address first."); } } export class ContractNoFromAddressDefinedError extends BaseWeb3Error { public code = ERR_CONTRACT_MISSING_FROM_ADDRESS; public constructor() { super('No "from" address specified in neither the given options, nor the default options.'); } } export class ContractInstantiationError extends BaseWeb3Error { public code = ERR_CONTRACT_INSTANTIATION; } export type ProviderErrorData = | HexString | { data: HexString } | { originalError: { data: HexString } }; /** * This class is expected to be set as an `innerError` inside ContractExecutionError * The properties would be typically decoded from the `data` if it was encoded according to EIP-838 */ export class Eip838ExecutionError extends Web3ContractError { public readonly name: string; public code: number; public data?: HexString; public errorName?: string; public errorSignature?: string; public errorArgs?: { [K in string]: unknown }; // eslint-disable-next-line no-use-before-define public innerError: Eip838ExecutionError | undefined; public constructor(error: JsonRpcError | Eip838ExecutionError) { super(error.message || 'Error'); this.name = ('name' in error && error.name) || this.constructor.name; this.stack = ('stack' in error && error.stack) || undefined; this.code = error.code; // get embedded error details got from some providers like MetaMask // and set this.data from the inner error data for easier read. // note: the data is a hex string inside either: // error.data, error.data.data or error.data.originalError.data (https://github.com/web3/web3.js/issues/4454#issuecomment-1485953455) if (typeof error.data === 'object') { let originalError: { data: string }; if ('originalError' in error.data) { originalError = error.data.originalError; } else { // Some provider implementations have no `originalError` sub-object. originalError = error.data; } this.data = originalError.data; this.innerError = new Eip838ExecutionError( originalError as JsonRpcError, ); } else { this.data = error.data; } } public setDecodedProperties( errorName: string, errorSignature?: string, errorArgs?: { [K in string]: unknown }, ) { this.errorName = errorName; this.errorSignature = errorSignature; this.errorArgs = errorArgs; } public toJSON() { let json = { ...super.toJSON(), data: this.data, } as { name: string; code: number; message: string; innerError: Error | Error[] | undefined; data: string; errorName?: string; errorSignature?: string; errorArgs?: { [K in string]: unknown }; }; if (this.errorName) { json = { ...json, errorName: this.errorName, errorSignature: this.errorSignature, errorArgs: this.errorArgs, }; } return json; } } /** * Used when an error is raised while executing a function inside a smart contract. * The data is expected to be encoded according to EIP-848. */ export class ContractExecutionError extends Web3ContractError { public innerError: 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); } } export class ContractTransactionDataAndInputError extends InvalidValueError { public code = ERR_CONTRACT_TX_DATA_AND_INPUT; public constructor(value: { data: HexString | undefined; input: HexString | undefined }) { super( `data: ${value.data ?? 'undefined'}, input: ${value.input ?? 'undefined'}`, 'You can\'t have "data" and "input" as properties of a contract at the same time, please use either "data" or "input" instead.', ); } }