abi-coder.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. "use strict";
  2. /**
  3. * When sending values to or receiving values from a [[Contract]], the
  4. * data is generally encoded using the [ABI standard](link-solc-abi).
  5. *
  6. * The AbiCoder provides a utility to encode values to ABI data and
  7. * decode values from ABI data.
  8. *
  9. * Most of the time, developers should favour the [[Contract]] class,
  10. * which further abstracts a lot of the finer details of ABI data.
  11. *
  12. * @_section api/abi/abi-coder:ABI Encoding
  13. */
  14. Object.defineProperty(exports, "__esModule", { value: true });
  15. exports.AbiCoder = void 0;
  16. // See: https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
  17. const index_js_1 = require("../utils/index.js");
  18. const abstract_coder_js_1 = require("./coders/abstract-coder.js");
  19. const address_js_1 = require("./coders/address.js");
  20. const array_js_1 = require("./coders/array.js");
  21. const boolean_js_1 = require("./coders/boolean.js");
  22. const bytes_js_1 = require("./coders/bytes.js");
  23. const fixed_bytes_js_1 = require("./coders/fixed-bytes.js");
  24. const null_js_1 = require("./coders/null.js");
  25. const number_js_1 = require("./coders/number.js");
  26. const string_js_1 = require("./coders/string.js");
  27. const tuple_js_1 = require("./coders/tuple.js");
  28. const fragments_js_1 = require("./fragments.js");
  29. const index_js_2 = require("../address/index.js");
  30. const index_js_3 = require("../utils/index.js");
  31. // https://docs.soliditylang.org/en/v0.8.17/control-structures.html
  32. const PanicReasons = new Map();
  33. PanicReasons.set(0x00, "GENERIC_PANIC");
  34. PanicReasons.set(0x01, "ASSERT_FALSE");
  35. PanicReasons.set(0x11, "OVERFLOW");
  36. PanicReasons.set(0x12, "DIVIDE_BY_ZERO");
  37. PanicReasons.set(0x21, "ENUM_RANGE_ERROR");
  38. PanicReasons.set(0x22, "BAD_STORAGE_DATA");
  39. PanicReasons.set(0x31, "STACK_UNDERFLOW");
  40. PanicReasons.set(0x32, "ARRAY_RANGE_ERROR");
  41. PanicReasons.set(0x41, "OUT_OF_MEMORY");
  42. PanicReasons.set(0x51, "UNINITIALIZED_FUNCTION_CALL");
  43. const paramTypeBytes = new RegExp(/^bytes([0-9]*)$/);
  44. const paramTypeNumber = new RegExp(/^(u?int)([0-9]*)$/);
  45. let defaultCoder = null;
  46. let defaultMaxInflation = 1024;
  47. function getBuiltinCallException(action, tx, data, abiCoder) {
  48. let message = "missing revert data";
  49. let reason = null;
  50. const invocation = null;
  51. let revert = null;
  52. if (data) {
  53. message = "execution reverted";
  54. const bytes = (0, index_js_3.getBytes)(data);
  55. data = (0, index_js_3.hexlify)(data);
  56. if (bytes.length === 0) {
  57. message += " (no data present; likely require(false) occurred";
  58. reason = "require(false)";
  59. }
  60. else if (bytes.length % 32 !== 4) {
  61. message += " (could not decode reason; invalid data length)";
  62. }
  63. else if ((0, index_js_3.hexlify)(bytes.slice(0, 4)) === "0x08c379a0") {
  64. // Error(string)
  65. try {
  66. reason = abiCoder.decode(["string"], bytes.slice(4))[0];
  67. revert = {
  68. signature: "Error(string)",
  69. name: "Error",
  70. args: [reason]
  71. };
  72. message += `: ${JSON.stringify(reason)}`;
  73. }
  74. catch (error) {
  75. message += " (could not decode reason; invalid string data)";
  76. }
  77. }
  78. else if ((0, index_js_3.hexlify)(bytes.slice(0, 4)) === "0x4e487b71") {
  79. // Panic(uint256)
  80. try {
  81. const code = Number(abiCoder.decode(["uint256"], bytes.slice(4))[0]);
  82. revert = {
  83. signature: "Panic(uint256)",
  84. name: "Panic",
  85. args: [code]
  86. };
  87. reason = `Panic due to ${PanicReasons.get(code) || "UNKNOWN"}(${code})`;
  88. message += `: ${reason}`;
  89. }
  90. catch (error) {
  91. message += " (could not decode panic code)";
  92. }
  93. }
  94. else {
  95. message += " (unknown custom error)";
  96. }
  97. }
  98. const transaction = {
  99. to: (tx.to ? (0, index_js_2.getAddress)(tx.to) : null),
  100. data: (tx.data || "0x")
  101. };
  102. if (tx.from) {
  103. transaction.from = (0, index_js_2.getAddress)(tx.from);
  104. }
  105. return (0, index_js_3.makeError)(message, "CALL_EXCEPTION", {
  106. action, data, reason, transaction, invocation, revert
  107. });
  108. }
  109. /**
  110. * The **AbiCoder** is a low-level class responsible for encoding JavaScript
  111. * values into binary data and decoding binary data into JavaScript values.
  112. */
  113. class AbiCoder {
  114. #getCoder(param) {
  115. if (param.isArray()) {
  116. return new array_js_1.ArrayCoder(this.#getCoder(param.arrayChildren), param.arrayLength, param.name);
  117. }
  118. if (param.isTuple()) {
  119. return new tuple_js_1.TupleCoder(param.components.map((c) => this.#getCoder(c)), param.name);
  120. }
  121. switch (param.baseType) {
  122. case "address":
  123. return new address_js_1.AddressCoder(param.name);
  124. case "bool":
  125. return new boolean_js_1.BooleanCoder(param.name);
  126. case "string":
  127. return new string_js_1.StringCoder(param.name);
  128. case "bytes":
  129. return new bytes_js_1.BytesCoder(param.name);
  130. case "":
  131. return new null_js_1.NullCoder(param.name);
  132. }
  133. // u?int[0-9]*
  134. let match = param.type.match(paramTypeNumber);
  135. if (match) {
  136. let size = parseInt(match[2] || "256");
  137. (0, index_js_1.assertArgument)(size !== 0 && size <= 256 && (size % 8) === 0, "invalid " + match[1] + " bit length", "param", param);
  138. return new number_js_1.NumberCoder(size / 8, (match[1] === "int"), param.name);
  139. }
  140. // bytes[0-9]+
  141. match = param.type.match(paramTypeBytes);
  142. if (match) {
  143. let size = parseInt(match[1]);
  144. (0, index_js_1.assertArgument)(size !== 0 && size <= 32, "invalid bytes length", "param", param);
  145. return new fixed_bytes_js_1.FixedBytesCoder(size, param.name);
  146. }
  147. (0, index_js_1.assertArgument)(false, "invalid type", "type", param.type);
  148. }
  149. /**
  150. * Get the default values for the given %%types%%.
  151. *
  152. * For example, a ``uint`` is by default ``0`` and ``bool``
  153. * is by default ``false``.
  154. */
  155. getDefaultValue(types) {
  156. const coders = types.map((type) => this.#getCoder(fragments_js_1.ParamType.from(type)));
  157. const coder = new tuple_js_1.TupleCoder(coders, "_");
  158. return coder.defaultValue();
  159. }
  160. /**
  161. * Encode the %%values%% as the %%types%% into ABI data.
  162. *
  163. * @returns DataHexstring
  164. */
  165. encode(types, values) {
  166. (0, index_js_1.assertArgumentCount)(values.length, types.length, "types/values length mismatch");
  167. const coders = types.map((type) => this.#getCoder(fragments_js_1.ParamType.from(type)));
  168. const coder = (new tuple_js_1.TupleCoder(coders, "_"));
  169. const writer = new abstract_coder_js_1.Writer();
  170. coder.encode(writer, values);
  171. return writer.data;
  172. }
  173. /**
  174. * Decode the ABI %%data%% as the %%types%% into values.
  175. *
  176. * If %%loose%% decoding is enabled, then strict padding is
  177. * not enforced. Some older versions of Solidity incorrectly
  178. * padded event data emitted from ``external`` functions.
  179. */
  180. decode(types, data, loose) {
  181. const coders = types.map((type) => this.#getCoder(fragments_js_1.ParamType.from(type)));
  182. const coder = new tuple_js_1.TupleCoder(coders, "_");
  183. return coder.decode(new abstract_coder_js_1.Reader(data, loose, defaultMaxInflation));
  184. }
  185. static _setDefaultMaxInflation(value) {
  186. (0, index_js_1.assertArgument)(typeof (value) === "number" && Number.isInteger(value), "invalid defaultMaxInflation factor", "value", value);
  187. defaultMaxInflation = value;
  188. }
  189. /**
  190. * Returns the shared singleton instance of a default [[AbiCoder]].
  191. *
  192. * On the first call, the instance is created internally.
  193. */
  194. static defaultAbiCoder() {
  195. if (defaultCoder == null) {
  196. defaultCoder = new AbiCoder();
  197. }
  198. return defaultCoder;
  199. }
  200. /**
  201. * Returns an ethers-compatible [[CallExceptionError]] Error for the given
  202. * result %%data%% for the [[CallExceptionAction]] %%action%% against
  203. * the Transaction %%tx%%.
  204. */
  205. static getBuiltinCallException(action, tx, data) {
  206. return getBuiltinCallException(action, tx, data, AbiCoder.defaultAbiCoder());
  207. }
  208. }
  209. exports.AbiCoder = AbiCoder;
  210. //# sourceMappingURL=abi-coder.js.map