123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793 |
- /**
- * All errors in ethers include properties to ensure they are both
- * human-readable (i.e. ``.message``) and machine-readable (i.e. ``.code``).
- *
- * The [[isError]] function can be used to check the error ``code`` and
- * provide a type guard for the properties present on that error interface.
- *
- * @_section: api/utils/errors:Errors [about-errors]
- */
- import { version } from "../_version.js";
- import { defineProperties } from "./properties.js";
- import type {
- TransactionRequest, TransactionReceipt, TransactionResponse
- } from "../providers/index.js";
- import type { FetchRequest, FetchResponse } from "./fetch.js";
- /**
- * An error may contain additional properties, but those must not
- * conflict with any implicit properties.
- */
- export type ErrorInfo<T> = Omit<T, "code" | "name" | "message" | "shortMessage"> & { shortMessage?: string };
- function stringify(value: any): any {
- if (value == null) { return "null"; }
- if (Array.isArray(value)) {
- return "[ " + (value.map(stringify)).join(", ") + " ]";
- }
- if (value instanceof Uint8Array) {
- const HEX = "0123456789abcdef";
- let result = "0x";
- for (let i = 0; i < value.length; i++) {
- result += HEX[value[i] >> 4];
- result += HEX[value[i] & 0xf];
- }
- return result;
- }
- if (typeof(value) === "object" && typeof(value.toJSON) === "function") {
- return stringify(value.toJSON());
- }
- switch (typeof(value)) {
- case "boolean": case "symbol":
- return value.toString();
- case "bigint":
- return BigInt(value).toString();
- case "number":
- return (value).toString();
- case "string":
- return JSON.stringify(value);
- case "object": {
- const keys = Object.keys(value);
- keys.sort();
- return "{ " + keys.map((k) => `${ stringify(k) }: ${ stringify(value[k]) }`).join(", ") + " }";
- }
- }
- return `[ COULD NOT SERIALIZE ]`;
- }
- /**
- * All errors emitted by ethers have an **ErrorCode** to help
- * identify and coalesce errors to simplify programmatic analysis.
- *
- * Each **ErrorCode** is the %%code%% proerty of a coresponding
- * [[EthersError]].
- *
- * **Generic Errors**
- *
- * **``"UNKNOWN_ERROR"``** - see [[UnknownError]]
- *
- * **``"NOT_IMPLEMENTED"``** - see [[NotImplementedError]]
- *
- * **``"UNSUPPORTED_OPERATION"``** - see [[UnsupportedOperationError]]
- *
- * **``"NETWORK_ERROR"``** - see [[NetworkError]]
- *
- * **``"SERVER_ERROR"``** - see [[ServerError]]
- *
- * **``"TIMEOUT"``** - see [[TimeoutError]]
- *
- * **``"BAD_DATA"``** - see [[BadDataError]]
- *
- * **``"CANCELLED"``** - see [[CancelledError]]
- *
- * **Operational Errors**
- *
- * **``"BUFFER_OVERRUN"``** - see [[BufferOverrunError]]
- *
- * **``"NUMERIC_FAULT"``** - see [[NumericFaultError]]
- *
- * **Argument Errors**
- *
- * **``"INVALID_ARGUMENT"``** - see [[InvalidArgumentError]]
- *
- * **``"MISSING_ARGUMENT"``** - see [[MissingArgumentError]]
- *
- * **``"UNEXPECTED_ARGUMENT"``** - see [[UnexpectedArgumentError]]
- *
- * **``"VALUE_MISMATCH"``** - //unused//
- *
- * **Blockchain Errors**
- *
- * **``"CALL_EXCEPTION"``** - see [[CallExceptionError]]
- *
- * **``"INSUFFICIENT_FUNDS"``** - see [[InsufficientFundsError]]
- *
- * **``"NONCE_EXPIRED"``** - see [[NonceExpiredError]]
- *
- * **``"REPLACEMENT_UNDERPRICED"``** - see [[ReplacementUnderpricedError]]
- *
- * **``"TRANSACTION_REPLACED"``** - see [[TransactionReplacedError]]
- *
- * **``"UNCONFIGURED_NAME"``** - see [[UnconfiguredNameError]]
- *
- * **``"OFFCHAIN_FAULT"``** - see [[OffchainFaultError]]
- *
- * **User Interaction Errors**
- *
- * **``"ACTION_REJECTED"``** - see [[ActionRejectedError]]
- */
- export type ErrorCode =
- // Generic Errors
- "UNKNOWN_ERROR" | "NOT_IMPLEMENTED" | "UNSUPPORTED_OPERATION" |
- "NETWORK_ERROR" | "SERVER_ERROR" | "TIMEOUT" | "BAD_DATA" |
- "CANCELLED" |
- // Operational Errors
- "BUFFER_OVERRUN" | "NUMERIC_FAULT" |
- // Argument Errors
- "INVALID_ARGUMENT" | "MISSING_ARGUMENT" | "UNEXPECTED_ARGUMENT" |
- "VALUE_MISMATCH" |
- // Blockchain Errors
- "CALL_EXCEPTION" | "INSUFFICIENT_FUNDS" | "NONCE_EXPIRED" |
- "REPLACEMENT_UNDERPRICED" | "TRANSACTION_REPLACED" |
- "UNCONFIGURED_NAME" | "OFFCHAIN_FAULT" |
- // User Interaction
- "ACTION_REJECTED"
- ;
- /**
- * All errors in Ethers include properties to assist in
- * machine-readable errors.
- */
- export interface EthersError<T extends ErrorCode = ErrorCode> extends Error {
- /**
- * The string error code.
- */
- code: ErrorCode;
- /**
- * A short message describing the error, with minimal additional
- * details.
- */
- shortMessage: string;
- /**
- * Additional info regarding the error that may be useful.
- *
- * This is generally helpful mostly for human-based debugging.
- */
- info?: Record<string, any>;
- /**
- * Any related error.
- */
- error?: Error;
- }
- // Generic Errors
- /**
- * This Error is a catch-all for when there is no way for Ethers to
- * know what the underlying problem is.
- */
- export interface UnknownError extends EthersError<"UNKNOWN_ERROR"> {
- [ key: string ]: any;
- }
- /**
- * This Error is mostly used as a stub for functionality that is
- * intended for the future, but is currently not implemented.
- */
- export interface NotImplementedError extends EthersError<"NOT_IMPLEMENTED"> {
- /**
- * The attempted operation.
- */
- operation: string;
- }
- /**
- * This Error indicates that the attempted operation is not supported.
- *
- * This could range from a specific JSON-RPC end-point not supporting
- * a feature to a specific configuration of an object prohibiting the
- * operation.
- *
- * For example, a [[Wallet]] with no connected [[Provider]] is unable
- * to send a transaction.
- */
- export interface UnsupportedOperationError extends EthersError<"UNSUPPORTED_OPERATION"> {
- /**
- * The attempted operation.
- */
- operation: string;
- }
- /**
- * This Error indicates a problem connecting to a network.
- */
- export interface NetworkError extends EthersError<"NETWORK_ERROR"> {
- /**
- * The network event.
- */
- event: string;
- }
- /**
- * This Error indicates there was a problem fetching a resource from
- * a server.
- */
- export interface ServerError extends EthersError<"SERVER_ERROR"> {
- /**
- * The requested resource.
- */
- request: FetchRequest | string;
- /**
- * The response received from the server, if available.
- */
- response?: FetchResponse;
- }
- /**
- * This Error indicates that the timeout duration has expired and
- * that the operation has been implicitly cancelled.
- *
- * The side-effect of the operation may still occur, as this
- * generally means a request has been sent and there has simply
- * been no response to indicate whether it was processed or not.
- */
- export interface TimeoutError extends EthersError<"TIMEOUT"> {
- /**
- * The attempted operation.
- */
- operation: string;
- /**
- * The reason.
- */
- reason: string;
- /**
- * The resource request, if available.
- */
- request?: FetchRequest;
- }
- /**
- * This Error indicates that a provided set of data cannot
- * be correctly interpreted.
- */
- export interface BadDataError extends EthersError<"BAD_DATA"> {
- /**
- * The data.
- */
- value: any;
- }
- /**
- * This Error indicates that the operation was cancelled by a
- * programmatic call, for example to ``cancel()``.
- */
- export interface CancelledError extends EthersError<"CANCELLED"> {
- }
- // Operational Errors
- /**
- * This Error indicates an attempt was made to read outside the bounds
- * of protected data.
- *
- * Most operations in Ethers are protected by bounds checks, to mitigate
- * exploits when parsing data.
- */
- export interface BufferOverrunError extends EthersError<"BUFFER_OVERRUN"> {
- /**
- * The buffer that was overrun.
- */
- buffer: Uint8Array;
- /**
- * The length of the buffer.
- */
- length: number;
- /**
- * The offset that was requested.
- */
- offset: number;
- }
- /**
- * This Error indicates an operation which would result in incorrect
- * arithmetic output has occurred.
- *
- * For example, trying to divide by zero or using a ``uint8`` to store
- * a negative value.
- */
- export interface NumericFaultError extends EthersError<"NUMERIC_FAULT"> {
- /**
- * The attempted operation.
- */
- operation: string;
- /**
- * The fault reported.
- */
- fault: string;
- /**
- * The value the operation was attempted against.
- */
- value: any;
- }
- // Argument Errors
- /**
- * This Error indicates an incorrect type or value was passed to
- * a function or method.
- */
- export interface InvalidArgumentError extends EthersError<"INVALID_ARGUMENT"> {
- /**
- * The name of the argument.
- */
- argument: string;
- /**
- * The value that was provided.
- */
- value: any;
- info?: Record<string, any>
- }
- /**
- * This Error indicates there were too few arguments were provided.
- */
- export interface MissingArgumentError extends EthersError<"MISSING_ARGUMENT"> {
- /**
- * The number of arguments received.
- */
- count: number;
- /**
- * The number of arguments expected.
- */
- expectedCount: number;
- }
- /**
- * This Error indicates too many arguments were provided.
- */
- export interface UnexpectedArgumentError extends EthersError<"UNEXPECTED_ARGUMENT"> {
- /**
- * The number of arguments received.
- */
- count: number;
- /**
- * The number of arguments expected.
- */
- expectedCount: number;
- }
- // Blockchain Errors
- /**
- * The action that resulted in the call exception.
- */
- export type CallExceptionAction = "call" | "estimateGas" | "getTransactionResult" | "sendTransaction" | "unknown";
- /**
- * The related transaction that caused the error.
- */
- export type CallExceptionTransaction = {
- to: null | string;
- from?: string;
- data: string;
- };
- /**
- * This **Error** indicates a transaction reverted.
- */
- export interface CallExceptionError extends EthersError<"CALL_EXCEPTION"> {
- /**
- * The action being performed when the revert was encountered.
- */
- action: CallExceptionAction;
- /**
- * The revert data returned.
- */
- data: null | string;
- /**
- * A human-readable representation of data, if possible.
- */
- reason: null | string;
- /**
- * The transaction that triggered the exception.
- */
- transaction: CallExceptionTransaction,
- /**
- * The contract invocation details, if available.
- */
- invocation: null | {
- method: string;
- signature: string;
- args: Array<any>;
- }
- /**
- * The built-in or custom revert error, if available
- */
- revert: null | {
- signature: string;
- name: string;
- args: Array<any>;
- }
- /**
- * If the error occurred in a transaction that was mined
- * (with a status of ``0``), this is the receipt.
- */
- receipt?: TransactionReceipt; // @TODO: in v7, make this `null | TransactionReceipt`
- }
- /**
- * The sending account has insufficient funds to cover the
- * entire transaction cost.
- */
- export interface InsufficientFundsError extends EthersError<"INSUFFICIENT_FUNDS"> {
- /**
- * The transaction.
- */
- transaction: TransactionRequest;
- }
- /**
- * The sending account has already used this nonce in a
- * transaction that has been included.
- */
- export interface NonceExpiredError extends EthersError<"NONCE_EXPIRED"> {
- /**
- * The transaction.
- */
- transaction: TransactionRequest;
- }
- /**
- * A CCIP-read exception, which cannot be recovered from or
- * be further processed.
- */
- export interface OffchainFaultError extends EthersError<"OFFCHAIN_FAULT"> {
- /**
- * The transaction.
- */
- transaction?: TransactionRequest;
- /**
- * The reason the CCIP-read failed.
- */
- reason: string;
- }
- /**
- * An attempt was made to replace a transaction, but with an
- * insufficient additional fee to afford evicting the old
- * transaction from the memory pool.
- */
- export interface ReplacementUnderpricedError extends EthersError<"REPLACEMENT_UNDERPRICED"> {
- /**
- * The transaction.
- */
- transaction: TransactionRequest;
- }
- /**
- * A pending transaction was replaced by another.
- */
- export interface TransactionReplacedError extends EthersError<"TRANSACTION_REPLACED"> {
- /**
- * If the transaction was cancelled, such that the original
- * effects of the transaction cannot be assured.
- */
- cancelled: boolean;
- /**
- * The reason the transaction was replaced.
- */
- reason: "repriced" | "cancelled" | "replaced";
- /**
- * The hash of the replaced transaction.
- */
- hash: string;
- /**
- * The transaction that replaced the transaction.
- */
- replacement: TransactionResponse;
- /**
- * The receipt of the transaction that replace the transaction.
- */
- receipt: TransactionReceipt;
- }
- /**
- * This Error indicates an ENS name was used, but the name has not
- * been configured.
- *
- * This could indicate an ENS name is unowned or that the current
- * address being pointed to is the [[ZeroAddress]].
- */
- export interface UnconfiguredNameError extends EthersError<"UNCONFIGURED_NAME"> {
- /**
- * The ENS name that was requested
- */
- value: string;
- }
- /**
- * This Error indicates a request was rejected by the user.
- *
- * In most clients (such as MetaMask), when an operation requires user
- * authorization (such as ``signer.sendTransaction``), the client
- * presents a dialog box to the user. If the user denies the request
- * this error is thrown.
- */
- export interface ActionRejectedError extends EthersError<"ACTION_REJECTED"> {
- /**
- * The requested action.
- */
- action: "requestAccess" | "sendTransaction" | "signMessage" | "signTransaction" | "signTypedData" | "unknown",
- /**
- * The reason the action was rejected.
- *
- * If there is already a pending request, some clients may indicate
- * there is already a ``"pending"`` action. This prevents an app
- * from spamming the user.
- */
- reason: "expired" | "rejected" | "pending"
- }
- // Coding; converts an ErrorCode its Typed Error
- /**
- * A conditional type that transforms the [[ErrorCode]] T into
- * its EthersError type.
- *
- * @flatworm-skip-docs
- */
- export type CodedEthersError<T> =
- T extends "UNKNOWN_ERROR" ? UnknownError:
- T extends "NOT_IMPLEMENTED" ? NotImplementedError:
- T extends "UNSUPPORTED_OPERATION" ? UnsupportedOperationError:
- T extends "NETWORK_ERROR" ? NetworkError:
- T extends "SERVER_ERROR" ? ServerError:
- T extends "TIMEOUT" ? TimeoutError:
- T extends "BAD_DATA" ? BadDataError:
- T extends "CANCELLED" ? CancelledError:
- T extends "BUFFER_OVERRUN" ? BufferOverrunError:
- T extends "NUMERIC_FAULT" ? NumericFaultError:
- T extends "INVALID_ARGUMENT" ? InvalidArgumentError:
- T extends "MISSING_ARGUMENT" ? MissingArgumentError:
- T extends "UNEXPECTED_ARGUMENT" ? UnexpectedArgumentError:
- T extends "CALL_EXCEPTION" ? CallExceptionError:
- T extends "INSUFFICIENT_FUNDS" ? InsufficientFundsError:
- T extends "NONCE_EXPIRED" ? NonceExpiredError:
- T extends "OFFCHAIN_FAULT" ? OffchainFaultError:
- T extends "REPLACEMENT_UNDERPRICED" ? ReplacementUnderpricedError:
- T extends "TRANSACTION_REPLACED" ? TransactionReplacedError:
- T extends "UNCONFIGURED_NAME" ? UnconfiguredNameError:
- T extends "ACTION_REJECTED" ? ActionRejectedError:
- never;
- /**
- * Returns true if the %%error%% matches an error thrown by ethers
- * that matches the error %%code%%.
- *
- * In TypeScript environments, this can be used to check that %%error%%
- * matches an EthersError type, which means the expected properties will
- * be set.
- *
- * @See [ErrorCodes](api:ErrorCode)
- * @example
- * try {
- * // code....
- * } catch (e) {
- * if (isError(e, "CALL_EXCEPTION")) {
- * // The Type Guard has validated this object
- * console.log(e.data);
- * }
- * }
- */
- export function isError<K extends ErrorCode, T extends CodedEthersError<K>>(error: any, code: K): error is T {
- return (error && (<EthersError>error).code === code);
- }
- /**
- * Returns true if %%error%% is a [[CallExceptionError].
- */
- export function isCallException(error: any): error is CallExceptionError {
- return isError(error, "CALL_EXCEPTION");
- }
- /**
- * Returns a new Error configured to the format ethers emits errors, with
- * the %%message%%, [[api:ErrorCode]] %%code%% and additional properties
- * for the corresponding EthersError.
- *
- * Each error in ethers includes the version of ethers, a
- * machine-readable [[ErrorCode]], and depending on %%code%%, additional
- * required properties. The error message will also include the %%message%%,
- * ethers version, %%code%% and all additional properties, serialized.
- */
- export function makeError<K extends ErrorCode, T extends CodedEthersError<K>>(message: string, code: K, info?: ErrorInfo<T>): T {
- let shortMessage = message;
- {
- const details: Array<string> = [];
- if (info) {
- if ("message" in info || "code" in info || "name" in info) {
- throw new Error(`value will overwrite populated values: ${ stringify(info) }`);
- }
- for (const key in info) {
- if (key === "shortMessage") { continue; }
- const value = <any>(info[<keyof ErrorInfo<T>>key]);
- // try {
- details.push(key + "=" + stringify(value));
- // } catch (error: any) {
- // console.log("MMM", error.message);
- // details.push(key + "=[could not serialize object]");
- // }
- }
- }
- details.push(`code=${ code }`);
- details.push(`version=${ version }`);
- if (details.length) {
- message += " (" + details.join(", ") + ")";
- }
- }
- let error;
- switch (code) {
- case "INVALID_ARGUMENT":
- error = new TypeError(message);
- break;
- case "NUMERIC_FAULT":
- case "BUFFER_OVERRUN":
- error = new RangeError(message);
- break;
- default:
- error = new Error(message);
- }
- defineProperties<EthersError>(<EthersError>error, { code });
- if (info) { Object.assign(error, info); }
- if ((<any>error).shortMessage == null) {
- defineProperties<EthersError>(<EthersError>error, { shortMessage });
- }
- return <T>error;
- }
- /**
- * Throws an EthersError with %%message%%, %%code%% and additional error
- * %%info%% when %%check%% is falsish..
- *
- * @see [[api:makeError]]
- */
- export function assert<K extends ErrorCode, T extends CodedEthersError<K>>(check: unknown, message: string, code: K, info?: ErrorInfo<T>): asserts check {
- if (!check) { throw makeError(message, code, info); }
- }
- /**
- * A simple helper to simply ensuring provided arguments match expected
- * constraints, throwing if not.
- *
- * In TypeScript environments, the %%check%% has been asserted true, so
- * any further code does not need additional compile-time checks.
- */
- export function assertArgument(check: unknown, message: string, name: string, value: unknown): asserts check {
- assert(check, message, "INVALID_ARGUMENT", { argument: name, value: value });
- }
- export function assertArgumentCount(count: number, expectedCount: number, message?: string): void {
- if (message == null) { message = ""; }
- if (message) { message = ": " + message; }
- assert(count >= expectedCount, "missing argument" + message, "MISSING_ARGUMENT", {
- count: count,
- expectedCount: expectedCount
- });
- assert(count <= expectedCount, "too many arguments" + message, "UNEXPECTED_ARGUMENT", {
- count: count,
- expectedCount: expectedCount
- });
- }
- const _normalizeForms = ["NFD", "NFC", "NFKD", "NFKC"].reduce((accum, form) => {
- try {
- // General test for normalize
- /* c8 ignore start */
- if ("test".normalize(form) !== "test") { throw new Error("bad"); };
- /* c8 ignore stop */
- if (form === "NFD") {
- const check = String.fromCharCode(0xe9).normalize("NFD");
- const expected = String.fromCharCode(0x65, 0x0301)
- /* c8 ignore start */
- if (check !== expected) { throw new Error("broken") }
- /* c8 ignore stop */
- }
- accum.push(form);
- } catch(error) { }
- return accum;
- }, <Array<string>>[]);
- /**
- * Throws if the normalization %%form%% is not supported.
- */
- export function assertNormalize(form: string): void {
- assert(_normalizeForms.indexOf(form) >= 0, "platform missing String.prototype.normalize", "UNSUPPORTED_OPERATION", {
- operation: "String.prototype.normalize", info: { form }
- });
- }
- /**
- * Many classes use file-scoped values to guard the constructor,
- * making it effectively private. This facilitates that pattern
- * by ensuring the %%givenGaurd%% matches the file-scoped %%guard%%,
- * throwing if not, indicating the %%className%% if provided.
- */
- export function assertPrivate(givenGuard: any, guard: any, className?: string): void {
- if (className == null) { className = ""; }
- if (givenGuard !== guard) {
- let method = className, operation = "new";
- if (className) {
- method += ".";
- operation += " " + className;
- }
- assert(false, `private constructor; use ${ method }from* methods`, "UNSUPPORTED_OPERATION", {
- operation
- });
- }
- }
|