interface.js 40 KB

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