interface.ts 45 KB

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