interface.js 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101
  1. /**
  2. * The Interface class is a low-level class that accepts an
  3. * ABI and provides all the necessary functionality to encode
  4. * and decode paramaters to and results from methods, events
  5. * and errors.
  6. *
  7. * It also provides several convenience methods to automatically
  8. * search and find matching transactions and events to parse them.
  9. *
  10. * @_subsection api/abi:Interfaces [interfaces]
  11. */
  12. import { keccak256 } from "../crypto/index.js";
  13. import { id } from "../hash/index.js";
  14. import { concat, dataSlice, getBigInt, getBytes, getBytesCopy, hexlify, zeroPadBytes, zeroPadValue, isHexString, defineProperties, assertArgument, toBeHex, assert } from "../utils/index.js";
  15. import { AbiCoder } from "./abi-coder.js";
  16. import { checkResultErrors, Result } from "./coders/abstract-coder.js";
  17. import { ConstructorFragment, ErrorFragment, EventFragment, Fragment, FunctionFragment, ParamType } from "./fragments.js";
  18. import { Typed } from "./typed.js";
  19. export { checkResultErrors, Result };
  20. /**
  21. * When using the [[Interface-parseLog]] to automatically match a Log to its event
  22. * for parsing, a **LogDescription** is returned.
  23. */
  24. export class LogDescription {
  25. /**
  26. * The matching fragment for the ``topic0``.
  27. */
  28. fragment;
  29. /**
  30. * The name of the Event.
  31. */
  32. name;
  33. /**
  34. * The full Event signature.
  35. */
  36. signature;
  37. /**
  38. * The topic hash for the Event.
  39. */
  40. topic;
  41. /**
  42. * The arguments passed into the Event with ``emit``.
  43. */
  44. args;
  45. /**
  46. * @_ignore:
  47. */
  48. constructor(fragment, topic, args) {
  49. const name = fragment.name, signature = fragment.format();
  50. defineProperties(this, {
  51. fragment, name, signature, topic, args
  52. });
  53. }
  54. }
  55. /**
  56. * When using the [[Interface-parseTransaction]] to automatically match
  57. * a transaction data to its function for parsing,
  58. * a **TransactionDescription** is returned.
  59. */
  60. export class TransactionDescription {
  61. /**
  62. * The matching fragment from the transaction ``data``.
  63. */
  64. fragment;
  65. /**
  66. * The name of the Function from the transaction ``data``.
  67. */
  68. name;
  69. /**
  70. * The arguments passed to the Function from the transaction ``data``.
  71. */
  72. args;
  73. /**
  74. * The full Function signature from the transaction ``data``.
  75. */
  76. signature;
  77. /**
  78. * The selector for the Function from the transaction ``data``.
  79. */
  80. selector;
  81. /**
  82. * The ``value`` (in wei) from the transaction.
  83. */
  84. value;
  85. /**
  86. * @_ignore:
  87. */
  88. constructor(fragment, selector, args, value) {
  89. const name = fragment.name, signature = fragment.format();
  90. defineProperties(this, {
  91. fragment, name, args, signature, selector, value
  92. });
  93. }
  94. }
  95. /**
  96. * When using the [[Interface-parseError]] to automatically match an
  97. * error for a call result for parsing, an **ErrorDescription** is returned.
  98. */
  99. export class ErrorDescription {
  100. /**
  101. * The matching fragment.
  102. */
  103. fragment;
  104. /**
  105. * The name of the Error.
  106. */
  107. name;
  108. /**
  109. * The arguments passed to the Error with ``revert``.
  110. */
  111. args;
  112. /**
  113. * The full Error signature.
  114. */
  115. signature;
  116. /**
  117. * The selector for the Error.
  118. */
  119. selector;
  120. /**
  121. * @_ignore:
  122. */
  123. constructor(fragment, selector, args) {
  124. const name = fragment.name, signature = fragment.format();
  125. defineProperties(this, {
  126. fragment, name, args, signature, selector
  127. });
  128. }
  129. }
  130. /**
  131. * An **Indexed** is used as a value when a value that does not
  132. * fit within a topic (i.e. not a fixed-length, 32-byte type). It
  133. * is the ``keccak256`` of the value, and used for types such as
  134. * arrays, tuples, bytes and strings.
  135. */
  136. export class Indexed {
  137. /**
  138. * The ``keccak256`` of the value logged.
  139. */
  140. hash;
  141. /**
  142. * @_ignore:
  143. */
  144. _isIndexed;
  145. /**
  146. * Returns ``true`` if %%value%% is an **Indexed**.
  147. *
  148. * This provides a Type Guard for property access.
  149. */
  150. static isIndexed(value) {
  151. return !!(value && value._isIndexed);
  152. }
  153. /**
  154. * @_ignore:
  155. */
  156. constructor(hash) {
  157. defineProperties(this, { hash, _isIndexed: true });
  158. }
  159. }
  160. // https://docs.soliditylang.org/en/v0.8.13/control-structures.html?highlight=panic#panic-via-assert-and-error-via-require
  161. const PanicReasons = {
  162. "0": "generic panic",
  163. "1": "assert(false)",
  164. "17": "arithmetic overflow",
  165. "18": "division or modulo by zero",
  166. "33": "enum overflow",
  167. "34": "invalid encoded storage byte array accessed",
  168. "49": "out-of-bounds array access; popping on an empty array",
  169. "50": "out-of-bounds access of an array or bytesN",
  170. "65": "out of memory",
  171. "81": "uninitialized function",
  172. };
  173. const BuiltinErrors = {
  174. "0x08c379a0": {
  175. signature: "Error(string)",
  176. name: "Error",
  177. inputs: ["string"],
  178. reason: (message) => {
  179. return `reverted with reason string ${JSON.stringify(message)}`;
  180. }
  181. },
  182. "0x4e487b71": {
  183. signature: "Panic(uint256)",
  184. name: "Panic",
  185. inputs: ["uint256"],
  186. reason: (code) => {
  187. let reason = "unknown panic code";
  188. if (code >= 0 && code <= 0xff && PanicReasons[code.toString()]) {
  189. reason = PanicReasons[code.toString()];
  190. }
  191. return `reverted with panic code 0x${code.toString(16)} (${reason})`;
  192. }
  193. }
  194. };
  195. /**
  196. * An Interface abstracts many of the low-level details for
  197. * encoding and decoding the data on the blockchain.
  198. *
  199. * An ABI provides information on how to encode data to send to
  200. * a Contract, how to decode the results and events and how to
  201. * interpret revert errors.
  202. *
  203. * The ABI can be specified by [any supported format](InterfaceAbi).
  204. */
  205. export class Interface {
  206. /**
  207. * All the Contract ABI members (i.e. methods, events, errors, etc).
  208. */
  209. fragments;
  210. /**
  211. * The Contract constructor.
  212. */
  213. deploy;
  214. /**
  215. * The Fallback method, if any.
  216. */
  217. fallback;
  218. /**
  219. * If receiving ether is supported.
  220. */
  221. receive;
  222. #errors;
  223. #events;
  224. #functions;
  225. // #structs: Map<string, StructFragment>;
  226. #abiCoder;
  227. /**
  228. * Create a new Interface for the %%fragments%%.
  229. */
  230. constructor(fragments) {
  231. let abi = [];
  232. if (typeof (fragments) === "string") {
  233. abi = JSON.parse(fragments);
  234. }
  235. else {
  236. abi = fragments;
  237. }
  238. this.#functions = new Map();
  239. this.#errors = new Map();
  240. this.#events = new Map();
  241. // this.#structs = new Map();
  242. const frags = [];
  243. for (const a of abi) {
  244. try {
  245. frags.push(Fragment.from(a));
  246. }
  247. catch (error) {
  248. console.log(`[Warning] Invalid Fragment ${JSON.stringify(a)}:`, error.message);
  249. }
  250. }
  251. defineProperties(this, {
  252. fragments: Object.freeze(frags)
  253. });
  254. let fallback = null;
  255. let receive = false;
  256. this.#abiCoder = this.getAbiCoder();
  257. // Add all fragments by their signature
  258. this.fragments.forEach((fragment, index) => {
  259. let bucket;
  260. switch (fragment.type) {
  261. case "constructor":
  262. if (this.deploy) {
  263. console.log("duplicate definition - constructor");
  264. return;
  265. }
  266. //checkNames(fragment, "input", fragment.inputs);
  267. defineProperties(this, { deploy: fragment });
  268. return;
  269. case "fallback":
  270. if (fragment.inputs.length === 0) {
  271. receive = true;
  272. }
  273. else {
  274. assertArgument(!fallback || fragment.payable !== fallback.payable, "conflicting fallback fragments", `fragments[${index}]`, fragment);
  275. fallback = fragment;
  276. receive = fallback.payable;
  277. }
  278. return;
  279. case "function":
  280. //checkNames(fragment, "input", fragment.inputs);
  281. //checkNames(fragment, "output", (<FunctionFragment>fragment).outputs);
  282. bucket = this.#functions;
  283. break;
  284. case "event":
  285. //checkNames(fragment, "input", fragment.inputs);
  286. bucket = this.#events;
  287. break;
  288. case "error":
  289. bucket = this.#errors;
  290. break;
  291. default:
  292. return;
  293. }
  294. // Two identical entries; ignore it
  295. const signature = fragment.format();
  296. if (bucket.has(signature)) {
  297. return;
  298. }
  299. bucket.set(signature, fragment);
  300. });
  301. // If we do not have a constructor add a default
  302. if (!this.deploy) {
  303. defineProperties(this, {
  304. deploy: ConstructorFragment.from("constructor()")
  305. });
  306. }
  307. defineProperties(this, { fallback, receive });
  308. }
  309. /**
  310. * Returns the entire Human-Readable ABI, as an array of
  311. * signatures, optionally as %%minimal%% strings, which
  312. * removes parameter names and unneceesary spaces.
  313. */
  314. format(minimal) {
  315. const format = (minimal ? "minimal" : "full");
  316. const abi = this.fragments.map((f) => f.format(format));
  317. return abi;
  318. }
  319. /**
  320. * Return the JSON-encoded ABI. This is the format Solidiy
  321. * returns.
  322. */
  323. formatJson() {
  324. const abi = this.fragments.map((f) => f.format("json"));
  325. // We need to re-bundle the JSON fragments a bit
  326. return JSON.stringify(abi.map((j) => JSON.parse(j)));
  327. }
  328. /**
  329. * The ABI coder that will be used to encode and decode binary
  330. * data.
  331. */
  332. getAbiCoder() {
  333. return AbiCoder.defaultAbiCoder();
  334. }
  335. // Find a function definition by any means necessary (unless it is ambiguous)
  336. #getFunction(key, values, forceUnique) {
  337. // Selector
  338. if (isHexString(key)) {
  339. const selector = key.toLowerCase();
  340. for (const fragment of this.#functions.values()) {
  341. if (selector === fragment.selector) {
  342. return fragment;
  343. }
  344. }
  345. return null;
  346. }
  347. // It is a bare name, look up the function (will return null if ambiguous)
  348. if (key.indexOf("(") === -1) {
  349. const matching = [];
  350. for (const [name, fragment] of this.#functions) {
  351. if (name.split("(" /* fix:) */)[0] === key) {
  352. matching.push(fragment);
  353. }
  354. }
  355. if (values) {
  356. const lastValue = (values.length > 0) ? values[values.length - 1] : null;
  357. let valueLength = values.length;
  358. let allowOptions = true;
  359. if (Typed.isTyped(lastValue) && lastValue.type === "overrides") {
  360. allowOptions = false;
  361. valueLength--;
  362. }
  363. // Remove all matches that don't have a compatible length. The args
  364. // may contain an overrides, so the match may have n or n - 1 parameters
  365. for (let i = matching.length - 1; i >= 0; i--) {
  366. const inputs = matching[i].inputs.length;
  367. if (inputs !== valueLength && (!allowOptions || inputs !== valueLength - 1)) {
  368. matching.splice(i, 1);
  369. }
  370. }
  371. // Remove all matches that don't match the Typed signature
  372. for (let i = matching.length - 1; i >= 0; i--) {
  373. const inputs = matching[i].inputs;
  374. for (let j = 0; j < values.length; j++) {
  375. // Not a typed value
  376. if (!Typed.isTyped(values[j])) {
  377. continue;
  378. }
  379. // We are past the inputs
  380. if (j >= inputs.length) {
  381. if (values[j].type === "overrides") {
  382. continue;
  383. }
  384. matching.splice(i, 1);
  385. break;
  386. }
  387. // Make sure the value type matches the input type
  388. if (values[j].type !== inputs[j].baseType) {
  389. matching.splice(i, 1);
  390. break;
  391. }
  392. }
  393. }
  394. }
  395. // We found a single matching signature with an overrides, but the
  396. // last value is something that cannot possibly be an options
  397. if (matching.length === 1 && values && values.length !== matching[0].inputs.length) {
  398. const lastArg = values[values.length - 1];
  399. if (lastArg == null || Array.isArray(lastArg) || typeof (lastArg) !== "object") {
  400. matching.splice(0, 1);
  401. }
  402. }
  403. if (matching.length === 0) {
  404. return null;
  405. }
  406. if (matching.length > 1 && forceUnique) {
  407. const matchStr = matching.map((m) => JSON.stringify(m.format())).join(", ");
  408. assertArgument(false, `ambiguous function description (i.e. matches ${matchStr})`, "key", key);
  409. }
  410. return matching[0];
  411. }
  412. // Normalize the signature and lookup the function
  413. const result = this.#functions.get(FunctionFragment.from(key).format());
  414. if (result) {
  415. return result;
  416. }
  417. return null;
  418. }
  419. /**
  420. * Get the function name for %%key%%, which may be a function selector,
  421. * function name or function signature that belongs to the ABI.
  422. */
  423. getFunctionName(key) {
  424. const fragment = this.#getFunction(key, null, false);
  425. assertArgument(fragment, "no matching function", "key", key);
  426. return fragment.name;
  427. }
  428. /**
  429. * Returns true if %%key%% (a function selector, function name or
  430. * function signature) is present in the ABI.
  431. *
  432. * In the case of a function name, the name may be ambiguous, so
  433. * accessing the [[FunctionFragment]] may require refinement.
  434. */
  435. hasFunction(key) {
  436. return !!this.#getFunction(key, null, false);
  437. }
  438. /**
  439. * Get the [[FunctionFragment]] for %%key%%, which may be a function
  440. * selector, function name or function signature that belongs to the ABI.
  441. *
  442. * If %%values%% is provided, it will use the Typed API to handle
  443. * ambiguous cases where multiple functions match by name.
  444. *
  445. * If the %%key%% and %%values%% do not refine to a single function in
  446. * the ABI, this will throw.
  447. */
  448. getFunction(key, values) {
  449. return this.#getFunction(key, values || null, true);
  450. }
  451. /**
  452. * Iterate over all functions, calling %%callback%%, sorted by their name.
  453. */
  454. forEachFunction(callback) {
  455. const names = Array.from(this.#functions.keys());
  456. names.sort((a, b) => a.localeCompare(b));
  457. for (let i = 0; i < names.length; i++) {
  458. const name = names[i];
  459. callback((this.#functions.get(name)), i);
  460. }
  461. }
  462. // Find an event definition by any means necessary (unless it is ambiguous)
  463. #getEvent(key, values, forceUnique) {
  464. // EventTopic
  465. if (isHexString(key)) {
  466. const eventTopic = key.toLowerCase();
  467. for (const fragment of this.#events.values()) {
  468. if (eventTopic === fragment.topicHash) {
  469. return fragment;
  470. }
  471. }
  472. return null;
  473. }
  474. // It is a bare name, look up the function (will return null if ambiguous)
  475. if (key.indexOf("(") === -1) {
  476. const matching = [];
  477. for (const [name, fragment] of this.#events) {
  478. if (name.split("(" /* fix:) */)[0] === key) {
  479. matching.push(fragment);
  480. }
  481. }
  482. if (values) {
  483. // Remove all matches that don't have a compatible length.
  484. for (let i = matching.length - 1; i >= 0; i--) {
  485. if (matching[i].inputs.length < values.length) {
  486. matching.splice(i, 1);
  487. }
  488. }
  489. // Remove all matches that don't match the Typed signature
  490. for (let i = matching.length - 1; i >= 0; i--) {
  491. const inputs = matching[i].inputs;
  492. for (let j = 0; j < values.length; j++) {
  493. // Not a typed value
  494. if (!Typed.isTyped(values[j])) {
  495. continue;
  496. }
  497. // Make sure the value type matches the input type
  498. if (values[j].type !== inputs[j].baseType) {
  499. matching.splice(i, 1);
  500. break;
  501. }
  502. }
  503. }
  504. }
  505. if (matching.length === 0) {
  506. return null;
  507. }
  508. if (matching.length > 1 && forceUnique) {
  509. const matchStr = matching.map((m) => JSON.stringify(m.format())).join(", ");
  510. assertArgument(false, `ambiguous event description (i.e. matches ${matchStr})`, "key", key);
  511. }
  512. return matching[0];
  513. }
  514. // Normalize the signature and lookup the function
  515. const result = this.#events.get(EventFragment.from(key).format());
  516. if (result) {
  517. return result;
  518. }
  519. return null;
  520. }
  521. /**
  522. * Get the event name for %%key%%, which may be a topic hash,
  523. * event name or event signature that belongs to the ABI.
  524. */
  525. getEventName(key) {
  526. const fragment = this.#getEvent(key, null, false);
  527. assertArgument(fragment, "no matching event", "key", key);
  528. return fragment.name;
  529. }
  530. /**
  531. * Returns true if %%key%% (an event topic hash, event name or
  532. * event signature) is present in the ABI.
  533. *
  534. * In the case of an event name, the name may be ambiguous, so
  535. * accessing the [[EventFragment]] may require refinement.
  536. */
  537. hasEvent(key) {
  538. return !!this.#getEvent(key, null, false);
  539. }
  540. /**
  541. * Get the [[EventFragment]] for %%key%%, which may be a topic hash,
  542. * event name or event signature that belongs to the ABI.
  543. *
  544. * If %%values%% is provided, it will use the Typed API to handle
  545. * ambiguous cases where multiple events match by name.
  546. *
  547. * If the %%key%% and %%values%% do not refine to a single event in
  548. * the ABI, this will throw.
  549. */
  550. getEvent(key, values) {
  551. return this.#getEvent(key, values || null, true);
  552. }
  553. /**
  554. * Iterate over all events, calling %%callback%%, sorted by their name.
  555. */
  556. forEachEvent(callback) {
  557. const names = Array.from(this.#events.keys());
  558. names.sort((a, b) => a.localeCompare(b));
  559. for (let i = 0; i < names.length; i++) {
  560. const name = names[i];
  561. callback((this.#events.get(name)), i);
  562. }
  563. }
  564. /**
  565. * Get the [[ErrorFragment]] for %%key%%, which may be an error
  566. * selector, error name or error signature that belongs to the ABI.
  567. *
  568. * If %%values%% is provided, it will use the Typed API to handle
  569. * ambiguous cases where multiple errors match by name.
  570. *
  571. * If the %%key%% and %%values%% do not refine to a single error in
  572. * the ABI, this will throw.
  573. */
  574. getError(key, values) {
  575. if (isHexString(key)) {
  576. const selector = key.toLowerCase();
  577. if (BuiltinErrors[selector]) {
  578. return ErrorFragment.from(BuiltinErrors[selector].signature);
  579. }
  580. for (const fragment of this.#errors.values()) {
  581. if (selector === fragment.selector) {
  582. return fragment;
  583. }
  584. }
  585. return null;
  586. }
  587. // It is a bare name, look up the function (will return null if ambiguous)
  588. if (key.indexOf("(") === -1) {
  589. const matching = [];
  590. for (const [name, fragment] of this.#errors) {
  591. if (name.split("(" /* fix:) */)[0] === key) {
  592. matching.push(fragment);
  593. }
  594. }
  595. if (matching.length === 0) {
  596. if (key === "Error") {
  597. return ErrorFragment.from("error Error(string)");
  598. }
  599. if (key === "Panic") {
  600. return ErrorFragment.from("error Panic(uint256)");
  601. }
  602. return null;
  603. }
  604. else if (matching.length > 1) {
  605. const matchStr = matching.map((m) => JSON.stringify(m.format())).join(", ");
  606. assertArgument(false, `ambiguous error description (i.e. ${matchStr})`, "name", key);
  607. }
  608. return matching[0];
  609. }
  610. // Normalize the signature and lookup the function
  611. key = ErrorFragment.from(key).format();
  612. if (key === "Error(string)") {
  613. return ErrorFragment.from("error Error(string)");
  614. }
  615. if (key === "Panic(uint256)") {
  616. return ErrorFragment.from("error Panic(uint256)");
  617. }
  618. const result = this.#errors.get(key);
  619. if (result) {
  620. return result;
  621. }
  622. return null;
  623. }
  624. /**
  625. * Iterate over all errors, calling %%callback%%, sorted by their name.
  626. */
  627. forEachError(callback) {
  628. const names = Array.from(this.#errors.keys());
  629. names.sort((a, b) => a.localeCompare(b));
  630. for (let i = 0; i < names.length; i++) {
  631. const name = names[i];
  632. callback((this.#errors.get(name)), i);
  633. }
  634. }
  635. // Get the 4-byte selector used by Solidity to identify a function
  636. /*
  637. getSelector(fragment: ErrorFragment | FunctionFragment): string {
  638. if (typeof(fragment) === "string") {
  639. const matches: Array<Fragment> = [ ];
  640. try { matches.push(this.getFunction(fragment)); } catch (error) { }
  641. try { matches.push(this.getError(<string>fragment)); } catch (_) { }
  642. if (matches.length === 0) {
  643. logger.throwArgumentError("unknown fragment", "key", fragment);
  644. } else if (matches.length > 1) {
  645. logger.throwArgumentError("ambiguous fragment matches function and error", "key", fragment);
  646. }
  647. fragment = matches[0];
  648. }
  649. return dataSlice(id(fragment.format()), 0, 4);
  650. }
  651. */
  652. // Get the 32-byte topic hash used by Solidity to identify an event
  653. /*
  654. getEventTopic(fragment: EventFragment): string {
  655. //if (typeof(fragment) === "string") { fragment = this.getEvent(eventFragment); }
  656. return id(fragment.format());
  657. }
  658. */
  659. _decodeParams(params, data) {
  660. return this.#abiCoder.decode(params, data);
  661. }
  662. _encodeParams(params, values) {
  663. return this.#abiCoder.encode(params, values);
  664. }
  665. /**
  666. * Encodes a ``tx.data`` object for deploying the Contract with
  667. * the %%values%% as the constructor arguments.
  668. */
  669. encodeDeploy(values) {
  670. return this._encodeParams(this.deploy.inputs, values || []);
  671. }
  672. /**
  673. * Decodes the result %%data%% (e.g. from an ``eth_call``) for the
  674. * specified error (see [[getError]] for valid values for
  675. * %%key%%).
  676. *
  677. * Most developers should prefer the [[parseCallResult]] method instead,
  678. * which will automatically detect a ``CALL_EXCEPTION`` and throw the
  679. * corresponding error.
  680. */
  681. decodeErrorResult(fragment, data) {
  682. if (typeof (fragment) === "string") {
  683. const f = this.getError(fragment);
  684. assertArgument(f, "unknown error", "fragment", fragment);
  685. fragment = f;
  686. }
  687. assertArgument(dataSlice(data, 0, 4) === fragment.selector, `data signature does not match error ${fragment.name}.`, "data", data);
  688. return this._decodeParams(fragment.inputs, dataSlice(data, 4));
  689. }
  690. /**
  691. * Encodes the transaction revert data for a call result that
  692. * reverted from the the Contract with the sepcified %%error%%
  693. * (see [[getError]] for valid values for %%fragment%%) with the %%values%%.
  694. *
  695. * This is generally not used by most developers, unless trying to mock
  696. * a result from a Contract.
  697. */
  698. encodeErrorResult(fragment, values) {
  699. if (typeof (fragment) === "string") {
  700. const f = this.getError(fragment);
  701. assertArgument(f, "unknown error", "fragment", fragment);
  702. fragment = f;
  703. }
  704. return concat([
  705. fragment.selector,
  706. this._encodeParams(fragment.inputs, values || [])
  707. ]);
  708. }
  709. /**
  710. * Decodes the %%data%% from a transaction ``tx.data`` for
  711. * the function specified (see [[getFunction]] for valid values
  712. * for %%fragment%%).
  713. *
  714. * Most developers should prefer the [[parseTransaction]] method
  715. * instead, which will automatically detect the fragment.
  716. */
  717. decodeFunctionData(fragment, data) {
  718. if (typeof (fragment) === "string") {
  719. const f = this.getFunction(fragment);
  720. assertArgument(f, "unknown function", "fragment", fragment);
  721. fragment = f;
  722. }
  723. assertArgument(dataSlice(data, 0, 4) === fragment.selector, `data signature does not match function ${fragment.name}.`, "data", data);
  724. return this._decodeParams(fragment.inputs, dataSlice(data, 4));
  725. }
  726. /**
  727. * Encodes the ``tx.data`` for a transaction that calls the function
  728. * specified (see [[getFunction]] for valid values for %%fragment%%) with
  729. * the %%values%%.
  730. */
  731. encodeFunctionData(fragment, values) {
  732. if (typeof (fragment) === "string") {
  733. const f = this.getFunction(fragment);
  734. assertArgument(f, "unknown function", "fragment", fragment);
  735. fragment = f;
  736. }
  737. return concat([
  738. fragment.selector,
  739. this._encodeParams(fragment.inputs, values || [])
  740. ]);
  741. }
  742. /**
  743. * Decodes the result %%data%% (e.g. from an ``eth_call``) for the
  744. * specified function (see [[getFunction]] for valid values for
  745. * %%key%%).
  746. *
  747. * Most developers should prefer the [[parseCallResult]] method instead,
  748. * which will automatically detect a ``CALL_EXCEPTION`` and throw the
  749. * corresponding error.
  750. */
  751. decodeFunctionResult(fragment, data) {
  752. if (typeof (fragment) === "string") {
  753. const f = this.getFunction(fragment);
  754. assertArgument(f, "unknown function", "fragment", fragment);
  755. fragment = f;
  756. }
  757. let message = "invalid length for result data";
  758. const bytes = getBytesCopy(data);
  759. if ((bytes.length % 32) === 0) {
  760. try {
  761. return this.#abiCoder.decode(fragment.outputs, bytes);
  762. }
  763. catch (error) {
  764. message = "could not decode result data";
  765. }
  766. }
  767. // Call returned data with no error, but the data is junk
  768. assert(false, message, "BAD_DATA", {
  769. value: hexlify(bytes),
  770. info: { method: fragment.name, signature: fragment.format() }
  771. });
  772. }
  773. makeError(_data, tx) {
  774. const data = getBytes(_data, "data");
  775. const error = AbiCoder.getBuiltinCallException("call", tx, data);
  776. // Not a built-in error; try finding a custom error
  777. const customPrefix = "execution reverted (unknown custom error)";
  778. if (error.message.startsWith(customPrefix)) {
  779. const selector = hexlify(data.slice(0, 4));
  780. const ef = this.getError(selector);
  781. if (ef) {
  782. try {
  783. const args = this.#abiCoder.decode(ef.inputs, data.slice(4));
  784. error.revert = {
  785. name: ef.name, signature: ef.format(), args
  786. };
  787. error.reason = error.revert.signature;
  788. error.message = `execution reverted: ${error.reason}`;
  789. }
  790. catch (e) {
  791. error.message = `execution reverted (coult not decode custom error)`;
  792. }
  793. }
  794. }
  795. // Add the invocation, if available
  796. const parsed = this.parseTransaction(tx);
  797. if (parsed) {
  798. error.invocation = {
  799. method: parsed.name,
  800. signature: parsed.signature,
  801. args: parsed.args
  802. };
  803. }
  804. return error;
  805. }
  806. /**
  807. * Encodes the result data (e.g. from an ``eth_call``) for the
  808. * specified function (see [[getFunction]] for valid values
  809. * for %%fragment%%) with %%values%%.
  810. *
  811. * This is generally not used by most developers, unless trying to mock
  812. * a result from a Contract.
  813. */
  814. encodeFunctionResult(fragment, values) {
  815. if (typeof (fragment) === "string") {
  816. const f = this.getFunction(fragment);
  817. assertArgument(f, "unknown function", "fragment", fragment);
  818. fragment = f;
  819. }
  820. return hexlify(this.#abiCoder.encode(fragment.outputs, values || []));
  821. }
  822. /*
  823. spelunk(inputs: Array<ParamType>, values: ReadonlyArray<any>, processfunc: (type: string, value: any) => Promise<any>): Promise<Array<any>> {
  824. const promises: Array<Promise<>> = [ ];
  825. const process = function(type: ParamType, value: any): any {
  826. if (type.baseType === "array") {
  827. return descend(type.child
  828. }
  829. if (type. === "address") {
  830. }
  831. };
  832. const descend = function (inputs: Array<ParamType>, values: ReadonlyArray<any>) {
  833. if (inputs.length !== values.length) { throw new Error("length mismatch"); }
  834. };
  835. const result: Array<any> = [ ];
  836. values.forEach((value, index) => {
  837. if (value == null) {
  838. topics.push(null);
  839. } else if (param.baseType === "array" || param.baseType === "tuple") {
  840. logger.throwArgumentError("filtering with tuples or arrays not supported", ("contract." + param.name), value);
  841. } else if (Array.isArray(value)) {
  842. topics.push(value.map((value) => encodeTopic(param, value)));
  843. } else {
  844. topics.push(encodeTopic(param, value));
  845. }
  846. });
  847. }
  848. */
  849. // Create the filter for the event with search criteria (e.g. for eth_filterLog)
  850. encodeFilterTopics(fragment, values) {
  851. if (typeof (fragment) === "string") {
  852. const f = this.getEvent(fragment);
  853. assertArgument(f, "unknown event", "eventFragment", fragment);
  854. fragment = f;
  855. }
  856. assert(values.length <= fragment.inputs.length, `too many arguments for ${fragment.format()}`, "UNEXPECTED_ARGUMENT", { count: values.length, expectedCount: fragment.inputs.length });
  857. const topics = [];
  858. if (!fragment.anonymous) {
  859. topics.push(fragment.topicHash);
  860. }
  861. // @TODO: Use the coders for this; to properly support tuples, etc.
  862. const encodeTopic = (param, value) => {
  863. if (param.type === "string") {
  864. return id(value);
  865. }
  866. else if (param.type === "bytes") {
  867. return keccak256(hexlify(value));
  868. }
  869. if (param.type === "bool" && typeof (value) === "boolean") {
  870. value = (value ? "0x01" : "0x00");
  871. }
  872. else if (param.type.match(/^u?int/)) {
  873. value = toBeHex(value); // @TODO: Should this toTwos??
  874. }
  875. else if (param.type.match(/^bytes/)) {
  876. value = zeroPadBytes(value, 32);
  877. }
  878. else if (param.type === "address") {
  879. // Check addresses are valid
  880. this.#abiCoder.encode(["address"], [value]);
  881. }
  882. return zeroPadValue(hexlify(value), 32);
  883. };
  884. values.forEach((value, index) => {
  885. const param = fragment.inputs[index];
  886. if (!param.indexed) {
  887. assertArgument(value == null, "cannot filter non-indexed parameters; must be null", ("contract." + param.name), value);
  888. return;
  889. }
  890. if (value == null) {
  891. topics.push(null);
  892. }
  893. else if (param.baseType === "array" || param.baseType === "tuple") {
  894. assertArgument(false, "filtering with tuples or arrays not supported", ("contract." + param.name), value);
  895. }
  896. else if (Array.isArray(value)) {
  897. topics.push(value.map((value) => encodeTopic(param, value)));
  898. }
  899. else {
  900. topics.push(encodeTopic(param, value));
  901. }
  902. });
  903. // Trim off trailing nulls
  904. while (topics.length && topics[topics.length - 1] === null) {
  905. topics.pop();
  906. }
  907. return topics;
  908. }
  909. encodeEventLog(fragment, values) {
  910. if (typeof (fragment) === "string") {
  911. const f = this.getEvent(fragment);
  912. assertArgument(f, "unknown event", "eventFragment", fragment);
  913. fragment = f;
  914. }
  915. const topics = [];
  916. const dataTypes = [];
  917. const dataValues = [];
  918. if (!fragment.anonymous) {
  919. topics.push(fragment.topicHash);
  920. }
  921. assertArgument(values.length === fragment.inputs.length, "event arguments/values mismatch", "values", values);
  922. fragment.inputs.forEach((param, index) => {
  923. const value = values[index];
  924. if (param.indexed) {
  925. if (param.type === "string") {
  926. topics.push(id(value));
  927. }
  928. else if (param.type === "bytes") {
  929. topics.push(keccak256(value));
  930. }
  931. else if (param.baseType === "tuple" || param.baseType === "array") {
  932. // @TODO
  933. throw new Error("not implemented");
  934. }
  935. else {
  936. topics.push(this.#abiCoder.encode([param.type], [value]));
  937. }
  938. }
  939. else {
  940. dataTypes.push(param);
  941. dataValues.push(value);
  942. }
  943. });
  944. return {
  945. data: this.#abiCoder.encode(dataTypes, dataValues),
  946. topics: topics
  947. };
  948. }
  949. // Decode a filter for the event and the search criteria
  950. decodeEventLog(fragment, data, topics) {
  951. if (typeof (fragment) === "string") {
  952. const f = this.getEvent(fragment);
  953. assertArgument(f, "unknown event", "eventFragment", fragment);
  954. fragment = f;
  955. }
  956. if (topics != null && !fragment.anonymous) {
  957. const eventTopic = fragment.topicHash;
  958. assertArgument(isHexString(topics[0], 32) && topics[0].toLowerCase() === eventTopic, "fragment/topic mismatch", "topics[0]", topics[0]);
  959. topics = topics.slice(1);
  960. }
  961. const indexed = [];
  962. const nonIndexed = [];
  963. const dynamic = [];
  964. fragment.inputs.forEach((param, index) => {
  965. if (param.indexed) {
  966. if (param.type === "string" || param.type === "bytes" || param.baseType === "tuple" || param.baseType === "array") {
  967. indexed.push(ParamType.from({ type: "bytes32", name: param.name }));
  968. dynamic.push(true);
  969. }
  970. else {
  971. indexed.push(param);
  972. dynamic.push(false);
  973. }
  974. }
  975. else {
  976. nonIndexed.push(param);
  977. dynamic.push(false);
  978. }
  979. });
  980. const resultIndexed = (topics != null) ? this.#abiCoder.decode(indexed, concat(topics)) : null;
  981. const resultNonIndexed = this.#abiCoder.decode(nonIndexed, data, true);
  982. //const result: (Array<any> & { [ key: string ]: any }) = [ ];
  983. const values = [];
  984. const keys = [];
  985. let nonIndexedIndex = 0, indexedIndex = 0;
  986. fragment.inputs.forEach((param, index) => {
  987. let value = null;
  988. if (param.indexed) {
  989. if (resultIndexed == null) {
  990. value = new Indexed(null);
  991. }
  992. else if (dynamic[index]) {
  993. value = new Indexed(resultIndexed[indexedIndex++]);
  994. }
  995. else {
  996. try {
  997. value = resultIndexed[indexedIndex++];
  998. }
  999. catch (error) {
  1000. value = error;
  1001. }
  1002. }
  1003. }
  1004. else {
  1005. try {
  1006. value = resultNonIndexed[nonIndexedIndex++];
  1007. }
  1008. catch (error) {
  1009. value = error;
  1010. }
  1011. }
  1012. values.push(value);
  1013. keys.push(param.name || null);
  1014. });
  1015. return Result.fromItems(values, keys);
  1016. }
  1017. /**
  1018. * Parses a transaction, finding the matching function and extracts
  1019. * the parameter values along with other useful function details.
  1020. *
  1021. * If the matching function cannot be found, return null.
  1022. */
  1023. parseTransaction(tx) {
  1024. const data = getBytes(tx.data, "tx.data");
  1025. const value = getBigInt((tx.value != null) ? tx.value : 0, "tx.value");
  1026. const fragment = this.getFunction(hexlify(data.slice(0, 4)));
  1027. if (!fragment) {
  1028. return null;
  1029. }
  1030. const args = this.#abiCoder.decode(fragment.inputs, data.slice(4));
  1031. return new TransactionDescription(fragment, fragment.selector, args, value);
  1032. }
  1033. parseCallResult(data) {
  1034. throw new Error("@TODO");
  1035. }
  1036. /**
  1037. * Parses a receipt log, finding the matching event and extracts
  1038. * the parameter values along with other useful event details.
  1039. *
  1040. * If the matching event cannot be found, returns null.
  1041. */
  1042. parseLog(log) {
  1043. const fragment = this.getEvent(log.topics[0]);
  1044. if (!fragment || fragment.anonymous) {
  1045. return null;
  1046. }
  1047. // @TODO: If anonymous, and the only method, and the input count matches, should we parse?
  1048. // Probably not, because just because it is the only event in the ABI does
  1049. // not mean we have the full ABI; maybe just a fragment?
  1050. return new LogDescription(fragment, fragment.topicHash, this.decodeEventLog(fragment, log.data, log.topics));
  1051. }
  1052. /**
  1053. * Parses a revert data, finding the matching error and extracts
  1054. * the parameter values along with other useful error details.
  1055. *
  1056. * If the matching error cannot be found, returns null.
  1057. */
  1058. parseError(data) {
  1059. const hexData = hexlify(data);
  1060. const fragment = this.getError(dataSlice(hexData, 0, 4));
  1061. if (!fragment) {
  1062. return null;
  1063. }
  1064. const args = this.#abiCoder.decode(fragment.inputs, dataSlice(hexData, 4));
  1065. return new ErrorDescription(fragment, fragment.selector, args);
  1066. }
  1067. /**
  1068. * Creates a new [[Interface]] from the ABI %%value%%.
  1069. *
  1070. * The %%value%% may be provided as an existing [[Interface]] object,
  1071. * a JSON-encoded ABI or any Human-Readable ABI format.
  1072. */
  1073. static from(value) {
  1074. // Already an Interface, which is immutable
  1075. if (value instanceof Interface) {
  1076. return value;
  1077. }
  1078. // JSON
  1079. if (typeof (value) === "string") {
  1080. return new Interface(JSON.parse(value));
  1081. }
  1082. // An Interface; possibly from another v6 instance
  1083. if (typeof (value.formatJson) === "function") {
  1084. return new Interface(value.formatJson());
  1085. }
  1086. // A legacy Interface; from an older version
  1087. if (typeof (value.format) === "function") {
  1088. return new Interface(value.format("json"));
  1089. }
  1090. // Array of fragments
  1091. return new Interface(value);
  1092. }
  1093. }
  1094. //# sourceMappingURL=interface.js.map