ethers.js/lib.esm/utils/errors.js

224 lines
7.2 KiB
JavaScript
Raw Permalink Normal View History

2022-11-30 15:44:23 -05:00
/**
2023-06-01 17:52:58 -04:00
* 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.
2022-11-30 15:44:23 -05:00
*
2022-12-02 21:27:06 -05:00
* @_section: api/utils/errors:Errors [about-errors]
2022-11-30 15:44:23 -05:00
*/
2022-09-15 22:58:45 -04:00
import { version } from "../_version.js";
2022-11-30 15:44:23 -05:00
import { defineProperties } from "./properties.js";
function stringify(value) {
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 ]`;
}
2022-09-05 16:57:11 -04:00
/**
2022-09-15 22:58:45 -04:00
* Returns true if the %%error%% matches an error thrown by ethers
* that matches the error %%code%%.
*
2023-11-11 18:00:39 -05:00
* In TypeScript environments, this can be used to check that %%error%%
2022-09-15 22:58:45 -04:00
* matches an EthersError type, which means the expected properties will
* be set.
*
* @See [ErrorCodes](api:ErrorCode)
* @example
2022-12-09 18:24:58 -05:00
* try {
* // code....
* } catch (e) {
2022-09-15 22:58:45 -04:00
* if (isError(e, "CALL_EXCEPTION")) {
2022-12-09 18:24:58 -05:00
* // The Type Guard has validated this object
2022-09-05 16:57:11 -04:00
* console.log(e.data);
* }
2022-12-09 18:24:58 -05:00
* }
2022-09-05 16:57:11 -04:00
*/
export function isError(error, code) {
return (error && error.code === code);
}
2022-09-15 22:58:45 -04:00
/**
2023-02-04 03:26:34 -05:00
* Returns true if %%error%% is a [[CallExceptionError].
2022-09-15 22:58:45 -04:00
*/
2022-09-05 16:57:11 -04:00
export function isCallException(error) {
return isError(error, "CALL_EXCEPTION");
}
2022-09-15 22:58:45 -04:00
/**
* Returns a new Error configured to the format ethers emits errors, with
2023-11-11 18:00:39 -05:00
* the %%message%%, [[api:ErrorCode]] %%code%% and additional properties
2022-09-15 22:58:45 -04:00
* for the corresponding EthersError.
*
* Each error in ethers includes the version of ethers, a
2023-11-11 18:00:39 -05:00
* 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.
2022-09-15 22:58:45 -04:00
*/
export function makeError(message, code, info) {
2023-10-09 20:27:56 -04:00
let shortMessage = message;
2022-09-15 22:58:45 -04:00
{
const details = [];
if (info) {
if ("message" in info || "code" in info || "name" in info) {
2022-11-30 15:44:23 -05:00
throw new Error(`value will overwrite populated values: ${stringify(info)}`);
2022-09-15 22:58:45 -04:00
}
for (const key in info) {
2023-10-09 20:27:56 -04:00
if (key === "shortMessage") {
continue;
}
2022-09-15 22:58:45 -04:00
const value = (info[key]);
2022-11-30 15:44:23 -05:00
// try {
details.push(key + "=" + stringify(value));
// } catch (error: any) {
// console.log("MMM", error.message);
// details.push(key + "=[could not serialize object]");
// }
2022-09-15 22:58:45 -04:00
}
}
details.push(`code=${code}`);
details.push(`version=${version}`);
if (details.length) {
message += " (" + details.join(", ") + ")";
}
}
2022-11-30 15:44:23 -05:00
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(error, { code });
2022-09-15 22:58:45 -04:00
if (info) {
2023-02-16 08:19:59 -05:00
Object.assign(error, info);
2022-09-15 22:58:45 -04:00
}
2023-10-09 20:27:56 -04:00
if (error.shortMessage == null) {
defineProperties(error, { shortMessage });
}
2022-09-15 22:58:45 -04:00
return error;
}
/**
* Throws an EthersError with %%message%%, %%code%% and additional error
2022-11-09 02:57:02 -05:00
* %%info%% when %%check%% is falsish..
2022-09-15 22:58:45 -04:00
*
* @see [[api:makeError]]
*/
2022-11-09 02:57:02 -05:00
export function assert(check, message, code, info) {
if (!check) {
throw makeError(message, code, info);
}
2022-09-15 22:58:45 -04:00
}
/**
* 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, message, name, value) {
2022-11-09 02:57:02 -05:00
assert(check, message, "INVALID_ARGUMENT", { argument: name, value: value });
2022-09-15 22:58:45 -04:00
}
2022-11-30 15:44:23 -05:00
export function assertArgumentCount(count, expectedCount, message) {
if (message == null) {
message = "";
}
2022-09-15 22:58:45 -04:00
if (message) {
message = ": " + message;
}
2022-11-09 02:57:02 -05:00
assert(count >= expectedCount, "missing arguemnt" + message, "MISSING_ARGUMENT", {
count: count,
expectedCount: expectedCount
});
2024-01-15 23:07:48 -05:00
assert(count <= expectedCount, "too many arguments" + message, "UNEXPECTED_ARGUMENT", {
2022-11-09 02:57:02 -05:00
count: count,
expectedCount: expectedCount
});
2022-09-15 22:58:45 -04:00
}
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;
}, []);
/**
* Throws if the normalization %%form%% is not supported.
*/
export function assertNormalize(form) {
2022-11-09 02:57:02 -05:00
assert(_normalizeForms.indexOf(form) >= 0, "platform missing String.prototype.normalize", "UNSUPPORTED_OPERATION", {
operation: "String.prototype.normalize", info: { form }
});
2022-09-15 22:58:45 -04:00
}
/**
* 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.
*/
2022-11-30 15:44:23 -05:00
export function assertPrivate(givenGuard, guard, className) {
if (className == null) {
className = "";
}
2022-09-15 22:58:45 -04:00
if (givenGuard !== guard) {
let method = className, operation = "new";
if (className) {
method += ".";
operation += " " + className;
}
2022-11-09 02:57:02 -05:00
assert(false, `private constructor; use ${method}from* methods`, "UNSUPPORTED_OPERATION", {
2022-09-15 22:58:45 -04:00
operation
});
}
}
2022-09-05 16:57:11 -04:00
//# sourceMappingURL=errors.js.map