errors.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. "use strict";
  2. /**
  3. * All errors in ethers include properties to ensure they are both
  4. * human-readable (i.e. ``.message``) and machine-readable (i.e. ``.code``).
  5. *
  6. * The [[isError]] function can be used to check the error ``code`` and
  7. * provide a type guard for the properties present on that error interface.
  8. *
  9. * @_section: api/utils/errors:Errors [about-errors]
  10. */
  11. Object.defineProperty(exports, "__esModule", { value: true });
  12. exports.assertPrivate = exports.assertNormalize = exports.assertArgumentCount = exports.assertArgument = exports.assert = exports.makeError = exports.isCallException = exports.isError = void 0;
  13. const _version_js_1 = require("../_version.js");
  14. const properties_js_1 = require("./properties.js");
  15. function stringify(value) {
  16. if (value == null) {
  17. return "null";
  18. }
  19. if (Array.isArray(value)) {
  20. return "[ " + (value.map(stringify)).join(", ") + " ]";
  21. }
  22. if (value instanceof Uint8Array) {
  23. const HEX = "0123456789abcdef";
  24. let result = "0x";
  25. for (let i = 0; i < value.length; i++) {
  26. result += HEX[value[i] >> 4];
  27. result += HEX[value[i] & 0xf];
  28. }
  29. return result;
  30. }
  31. if (typeof (value) === "object" && typeof (value.toJSON) === "function") {
  32. return stringify(value.toJSON());
  33. }
  34. switch (typeof (value)) {
  35. case "boolean":
  36. case "symbol":
  37. return value.toString();
  38. case "bigint":
  39. return BigInt(value).toString();
  40. case "number":
  41. return (value).toString();
  42. case "string":
  43. return JSON.stringify(value);
  44. case "object": {
  45. const keys = Object.keys(value);
  46. keys.sort();
  47. return "{ " + keys.map((k) => `${stringify(k)}: ${stringify(value[k])}`).join(", ") + " }";
  48. }
  49. }
  50. return `[ COULD NOT SERIALIZE ]`;
  51. }
  52. /**
  53. * Returns true if the %%error%% matches an error thrown by ethers
  54. * that matches the error %%code%%.
  55. *
  56. * In TypeScript environments, this can be used to check that %%error%%
  57. * matches an EthersError type, which means the expected properties will
  58. * be set.
  59. *
  60. * @See [ErrorCodes](api:ErrorCode)
  61. * @example
  62. * try {
  63. * // code....
  64. * } catch (e) {
  65. * if (isError(e, "CALL_EXCEPTION")) {
  66. * // The Type Guard has validated this object
  67. * console.log(e.data);
  68. * }
  69. * }
  70. */
  71. function isError(error, code) {
  72. return (error && error.code === code);
  73. }
  74. exports.isError = isError;
  75. /**
  76. * Returns true if %%error%% is a [[CallExceptionError].
  77. */
  78. function isCallException(error) {
  79. return isError(error, "CALL_EXCEPTION");
  80. }
  81. exports.isCallException = isCallException;
  82. /**
  83. * Returns a new Error configured to the format ethers emits errors, with
  84. * the %%message%%, [[api:ErrorCode]] %%code%% and additional properties
  85. * for the corresponding EthersError.
  86. *
  87. * Each error in ethers includes the version of ethers, a
  88. * machine-readable [[ErrorCode]], and depending on %%code%%, additional
  89. * required properties. The error message will also include the %%message%%,
  90. * ethers version, %%code%% and all additional properties, serialized.
  91. */
  92. function makeError(message, code, info) {
  93. let shortMessage = message;
  94. {
  95. const details = [];
  96. if (info) {
  97. if ("message" in info || "code" in info || "name" in info) {
  98. throw new Error(`value will overwrite populated values: ${stringify(info)}`);
  99. }
  100. for (const key in info) {
  101. if (key === "shortMessage") {
  102. continue;
  103. }
  104. const value = (info[key]);
  105. // try {
  106. details.push(key + "=" + stringify(value));
  107. // } catch (error: any) {
  108. // console.log("MMM", error.message);
  109. // details.push(key + "=[could not serialize object]");
  110. // }
  111. }
  112. }
  113. details.push(`code=${code}`);
  114. details.push(`version=${_version_js_1.version}`);
  115. if (details.length) {
  116. message += " (" + details.join(", ") + ")";
  117. }
  118. }
  119. let error;
  120. switch (code) {
  121. case "INVALID_ARGUMENT":
  122. error = new TypeError(message);
  123. break;
  124. case "NUMERIC_FAULT":
  125. case "BUFFER_OVERRUN":
  126. error = new RangeError(message);
  127. break;
  128. default:
  129. error = new Error(message);
  130. }
  131. (0, properties_js_1.defineProperties)(error, { code });
  132. if (info) {
  133. Object.assign(error, info);
  134. }
  135. if (error.shortMessage == null) {
  136. (0, properties_js_1.defineProperties)(error, { shortMessage });
  137. }
  138. return error;
  139. }
  140. exports.makeError = makeError;
  141. /**
  142. * Throws an EthersError with %%message%%, %%code%% and additional error
  143. * %%info%% when %%check%% is falsish..
  144. *
  145. * @see [[api:makeError]]
  146. */
  147. function assert(check, message, code, info) {
  148. if (!check) {
  149. throw makeError(message, code, info);
  150. }
  151. }
  152. exports.assert = assert;
  153. /**
  154. * A simple helper to simply ensuring provided arguments match expected
  155. * constraints, throwing if not.
  156. *
  157. * In TypeScript environments, the %%check%% has been asserted true, so
  158. * any further code does not need additional compile-time checks.
  159. */
  160. function assertArgument(check, message, name, value) {
  161. assert(check, message, "INVALID_ARGUMENT", { argument: name, value: value });
  162. }
  163. exports.assertArgument = assertArgument;
  164. function assertArgumentCount(count, expectedCount, message) {
  165. if (message == null) {
  166. message = "";
  167. }
  168. if (message) {
  169. message = ": " + message;
  170. }
  171. assert(count >= expectedCount, "missing argument" + message, "MISSING_ARGUMENT", {
  172. count: count,
  173. expectedCount: expectedCount
  174. });
  175. assert(count <= expectedCount, "too many arguments" + message, "UNEXPECTED_ARGUMENT", {
  176. count: count,
  177. expectedCount: expectedCount
  178. });
  179. }
  180. exports.assertArgumentCount = assertArgumentCount;
  181. const _normalizeForms = ["NFD", "NFC", "NFKD", "NFKC"].reduce((accum, form) => {
  182. try {
  183. // General test for normalize
  184. /* c8 ignore start */
  185. if ("test".normalize(form) !== "test") {
  186. throw new Error("bad");
  187. }
  188. ;
  189. /* c8 ignore stop */
  190. if (form === "NFD") {
  191. const check = String.fromCharCode(0xe9).normalize("NFD");
  192. const expected = String.fromCharCode(0x65, 0x0301);
  193. /* c8 ignore start */
  194. if (check !== expected) {
  195. throw new Error("broken");
  196. }
  197. /* c8 ignore stop */
  198. }
  199. accum.push(form);
  200. }
  201. catch (error) { }
  202. return accum;
  203. }, []);
  204. /**
  205. * Throws if the normalization %%form%% is not supported.
  206. */
  207. function assertNormalize(form) {
  208. assert(_normalizeForms.indexOf(form) >= 0, "platform missing String.prototype.normalize", "UNSUPPORTED_OPERATION", {
  209. operation: "String.prototype.normalize", info: { form }
  210. });
  211. }
  212. exports.assertNormalize = assertNormalize;
  213. /**
  214. * Many classes use file-scoped values to guard the constructor,
  215. * making it effectively private. This facilitates that pattern
  216. * by ensuring the %%givenGaurd%% matches the file-scoped %%guard%%,
  217. * throwing if not, indicating the %%className%% if provided.
  218. */
  219. function assertPrivate(givenGuard, guard, className) {
  220. if (className == null) {
  221. className = "";
  222. }
  223. if (givenGuard !== guard) {
  224. let method = className, operation = "new";
  225. if (className) {
  226. method += ".";
  227. operation += " " + className;
  228. }
  229. assert(false, `private constructor; use ${method}from* methods`, "UNSUPPORTED_OPERATION", {
  230. operation
  231. });
  232. }
  233. }
  234. exports.assertPrivate = assertPrivate;
  235. //# sourceMappingURL=errors.js.map