fragments.js 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319
  1. /**
  2. * A fragment is a single item from an ABI, which may represent any of:
  3. *
  4. * - [Functions](FunctionFragment)
  5. * - [Events](EventFragment)
  6. * - [Constructors](ConstructorFragment)
  7. * - Custom [Errors](ErrorFragment)
  8. * - [Fallback or Receive](FallbackFragment) functions
  9. *
  10. * @_subsection api/abi/abi-coder:Fragments [about-fragments]
  11. */
  12. import { defineProperties, getBigInt, getNumber, assert, assertPrivate, assertArgument } from "../utils/index.js";
  13. import { id } from "../hash/index.js";
  14. ;
  15. // [ "a", "b" ] => { "a": 1, "b": 1 }
  16. function setify(items) {
  17. const result = new Set();
  18. items.forEach((k) => result.add(k));
  19. return Object.freeze(result);
  20. }
  21. const _kwVisibDeploy = "external public payable override";
  22. const KwVisibDeploy = setify(_kwVisibDeploy.split(" "));
  23. // Visibility Keywords
  24. const _kwVisib = "constant external internal payable private public pure view override";
  25. const KwVisib = setify(_kwVisib.split(" "));
  26. const _kwTypes = "constructor error event fallback function receive struct";
  27. const KwTypes = setify(_kwTypes.split(" "));
  28. const _kwModifiers = "calldata memory storage payable indexed";
  29. const KwModifiers = setify(_kwModifiers.split(" "));
  30. const _kwOther = "tuple returns";
  31. // All Keywords
  32. const _keywords = [_kwTypes, _kwModifiers, _kwOther, _kwVisib].join(" ");
  33. const Keywords = setify(_keywords.split(" "));
  34. // Single character tokens
  35. const SimpleTokens = {
  36. "(": "OPEN_PAREN", ")": "CLOSE_PAREN",
  37. "[": "OPEN_BRACKET", "]": "CLOSE_BRACKET",
  38. ",": "COMMA", "@": "AT"
  39. };
  40. // Parser regexes to consume the next token
  41. const regexWhitespacePrefix = new RegExp("^(\\s*)");
  42. const regexNumberPrefix = new RegExp("^([0-9]+)");
  43. const regexIdPrefix = new RegExp("^([a-zA-Z$_][a-zA-Z0-9$_]*)");
  44. // Parser regexs to check validity
  45. const regexId = new RegExp("^([a-zA-Z$_][a-zA-Z0-9$_]*)$");
  46. const regexType = new RegExp("^(address|bool|bytes([0-9]*)|string|u?int([0-9]*))$");
  47. class TokenString {
  48. #offset;
  49. #tokens;
  50. get offset() { return this.#offset; }
  51. get length() { return this.#tokens.length - this.#offset; }
  52. constructor(tokens) {
  53. this.#offset = 0;
  54. this.#tokens = tokens.slice();
  55. }
  56. clone() { return new TokenString(this.#tokens); }
  57. reset() { this.#offset = 0; }
  58. #subTokenString(from = 0, to = 0) {
  59. return new TokenString(this.#tokens.slice(from, to).map((t) => {
  60. return Object.freeze(Object.assign({}, t, {
  61. match: (t.match - from),
  62. linkBack: (t.linkBack - from),
  63. linkNext: (t.linkNext - from),
  64. }));
  65. }));
  66. }
  67. // Pops and returns the value of the next token, if it is a keyword in allowed; throws if out of tokens
  68. popKeyword(allowed) {
  69. const top = this.peek();
  70. if (top.type !== "KEYWORD" || !allowed.has(top.text)) {
  71. throw new Error(`expected keyword ${top.text}`);
  72. }
  73. return this.pop().text;
  74. }
  75. // Pops and returns the value of the next token if it is `type`; throws if out of tokens
  76. popType(type) {
  77. if (this.peek().type !== type) {
  78. const top = this.peek();
  79. throw new Error(`expected ${type}; got ${top.type} ${JSON.stringify(top.text)}`);
  80. }
  81. return this.pop().text;
  82. }
  83. // Pops and returns a "(" TOKENS ")"
  84. popParen() {
  85. const top = this.peek();
  86. if (top.type !== "OPEN_PAREN") {
  87. throw new Error("bad start");
  88. }
  89. const result = this.#subTokenString(this.#offset + 1, top.match + 1);
  90. this.#offset = top.match + 1;
  91. return result;
  92. }
  93. // Pops and returns the items within "(" ITEM1 "," ITEM2 "," ... ")"
  94. popParams() {
  95. const top = this.peek();
  96. if (top.type !== "OPEN_PAREN") {
  97. throw new Error("bad start");
  98. }
  99. const result = [];
  100. while (this.#offset < top.match - 1) {
  101. const link = this.peek().linkNext;
  102. result.push(this.#subTokenString(this.#offset + 1, link));
  103. this.#offset = link;
  104. }
  105. this.#offset = top.match + 1;
  106. return result;
  107. }
  108. // Returns the top Token, throwing if out of tokens
  109. peek() {
  110. if (this.#offset >= this.#tokens.length) {
  111. throw new Error("out-of-bounds");
  112. }
  113. return this.#tokens[this.#offset];
  114. }
  115. // Returns the next value, if it is a keyword in `allowed`
  116. peekKeyword(allowed) {
  117. const top = this.peekType("KEYWORD");
  118. return (top != null && allowed.has(top)) ? top : null;
  119. }
  120. // Returns the value of the next token if it is `type`
  121. peekType(type) {
  122. if (this.length === 0) {
  123. return null;
  124. }
  125. const top = this.peek();
  126. return (top.type === type) ? top.text : null;
  127. }
  128. // Returns the next token; throws if out of tokens
  129. pop() {
  130. const result = this.peek();
  131. this.#offset++;
  132. return result;
  133. }
  134. toString() {
  135. const tokens = [];
  136. for (let i = this.#offset; i < this.#tokens.length; i++) {
  137. const token = this.#tokens[i];
  138. tokens.push(`${token.type}:${token.text}`);
  139. }
  140. return `<TokenString ${tokens.join(" ")}>`;
  141. }
  142. }
  143. function lex(text) {
  144. const tokens = [];
  145. const throwError = (message) => {
  146. const token = (offset < text.length) ? JSON.stringify(text[offset]) : "$EOI";
  147. throw new Error(`invalid token ${token} at ${offset}: ${message}`);
  148. };
  149. let brackets = [];
  150. let commas = [];
  151. let offset = 0;
  152. while (offset < text.length) {
  153. // Strip off any leading whitespace
  154. let cur = text.substring(offset);
  155. let match = cur.match(regexWhitespacePrefix);
  156. if (match) {
  157. offset += match[1].length;
  158. cur = text.substring(offset);
  159. }
  160. const token = { depth: brackets.length, linkBack: -1, linkNext: -1, match: -1, type: "", text: "", offset, value: -1 };
  161. tokens.push(token);
  162. let type = (SimpleTokens[cur[0]] || "");
  163. if (type) {
  164. token.type = type;
  165. token.text = cur[0];
  166. offset++;
  167. if (type === "OPEN_PAREN") {
  168. brackets.push(tokens.length - 1);
  169. commas.push(tokens.length - 1);
  170. }
  171. else if (type == "CLOSE_PAREN") {
  172. if (brackets.length === 0) {
  173. throwError("no matching open bracket");
  174. }
  175. token.match = brackets.pop();
  176. (tokens[token.match]).match = tokens.length - 1;
  177. token.depth--;
  178. token.linkBack = commas.pop();
  179. (tokens[token.linkBack]).linkNext = tokens.length - 1;
  180. }
  181. else if (type === "COMMA") {
  182. token.linkBack = commas.pop();
  183. (tokens[token.linkBack]).linkNext = tokens.length - 1;
  184. commas.push(tokens.length - 1);
  185. }
  186. else if (type === "OPEN_BRACKET") {
  187. token.type = "BRACKET";
  188. }
  189. else if (type === "CLOSE_BRACKET") {
  190. // Remove the CLOSE_BRACKET
  191. let suffix = tokens.pop().text;
  192. if (tokens.length > 0 && tokens[tokens.length - 1].type === "NUMBER") {
  193. const value = tokens.pop().text;
  194. suffix = value + suffix;
  195. (tokens[tokens.length - 1]).value = getNumber(value);
  196. }
  197. if (tokens.length === 0 || tokens[tokens.length - 1].type !== "BRACKET") {
  198. throw new Error("missing opening bracket");
  199. }
  200. (tokens[tokens.length - 1]).text += suffix;
  201. }
  202. continue;
  203. }
  204. match = cur.match(regexIdPrefix);
  205. if (match) {
  206. token.text = match[1];
  207. offset += token.text.length;
  208. if (Keywords.has(token.text)) {
  209. token.type = "KEYWORD";
  210. continue;
  211. }
  212. if (token.text.match(regexType)) {
  213. token.type = "TYPE";
  214. continue;
  215. }
  216. token.type = "ID";
  217. continue;
  218. }
  219. match = cur.match(regexNumberPrefix);
  220. if (match) {
  221. token.text = match[1];
  222. token.type = "NUMBER";
  223. offset += token.text.length;
  224. continue;
  225. }
  226. throw new Error(`unexpected token ${JSON.stringify(cur[0])} at position ${offset}`);
  227. }
  228. return new TokenString(tokens.map((t) => Object.freeze(t)));
  229. }
  230. // Check only one of `allowed` is in `set`
  231. function allowSingle(set, allowed) {
  232. let included = [];
  233. for (const key in allowed.keys()) {
  234. if (set.has(key)) {
  235. included.push(key);
  236. }
  237. }
  238. if (included.length > 1) {
  239. throw new Error(`conflicting types: ${included.join(", ")}`);
  240. }
  241. }
  242. // Functions to process a Solidity Signature TokenString from left-to-right for...
  243. // ...the name with an optional type, returning the name
  244. function consumeName(type, tokens) {
  245. if (tokens.peekKeyword(KwTypes)) {
  246. const keyword = tokens.pop().text;
  247. if (keyword !== type) {
  248. throw new Error(`expected ${type}, got ${keyword}`);
  249. }
  250. }
  251. return tokens.popType("ID");
  252. }
  253. // ...all keywords matching allowed, returning the keywords
  254. function consumeKeywords(tokens, allowed) {
  255. const keywords = new Set();
  256. while (true) {
  257. const keyword = tokens.peekType("KEYWORD");
  258. if (keyword == null || (allowed && !allowed.has(keyword))) {
  259. break;
  260. }
  261. tokens.pop();
  262. if (keywords.has(keyword)) {
  263. throw new Error(`duplicate keywords: ${JSON.stringify(keyword)}`);
  264. }
  265. keywords.add(keyword);
  266. }
  267. return Object.freeze(keywords);
  268. }
  269. // ...all visibility keywords, returning the coalesced mutability
  270. function consumeMutability(tokens) {
  271. let modifiers = consumeKeywords(tokens, KwVisib);
  272. // Detect conflicting modifiers
  273. allowSingle(modifiers, setify("constant payable nonpayable".split(" ")));
  274. allowSingle(modifiers, setify("pure view payable nonpayable".split(" ")));
  275. // Process mutability states
  276. if (modifiers.has("view")) {
  277. return "view";
  278. }
  279. if (modifiers.has("pure")) {
  280. return "pure";
  281. }
  282. if (modifiers.has("payable")) {
  283. return "payable";
  284. }
  285. if (modifiers.has("nonpayable")) {
  286. return "nonpayable";
  287. }
  288. // Process legacy `constant` last
  289. if (modifiers.has("constant")) {
  290. return "view";
  291. }
  292. return "nonpayable";
  293. }
  294. // ...a parameter list, returning the ParamType list
  295. function consumeParams(tokens, allowIndexed) {
  296. return tokens.popParams().map((t) => ParamType.from(t, allowIndexed));
  297. }
  298. // ...a gas limit, returning a BigNumber or null if none
  299. function consumeGas(tokens) {
  300. if (tokens.peekType("AT")) {
  301. tokens.pop();
  302. if (tokens.peekType("NUMBER")) {
  303. return getBigInt(tokens.pop().text);
  304. }
  305. throw new Error("invalid gas");
  306. }
  307. return null;
  308. }
  309. function consumeEoi(tokens) {
  310. if (tokens.length) {
  311. throw new Error(`unexpected tokens at offset ${tokens.offset}: ${tokens.toString()}`);
  312. }
  313. }
  314. const regexArrayType = new RegExp(/^(.*)\[([0-9]*)\]$/);
  315. function verifyBasicType(type) {
  316. const match = type.match(regexType);
  317. assertArgument(match, "invalid type", "type", type);
  318. if (type === "uint") {
  319. return "uint256";
  320. }
  321. if (type === "int") {
  322. return "int256";
  323. }
  324. if (match[2]) {
  325. // bytesXX
  326. const length = parseInt(match[2]);
  327. assertArgument(length !== 0 && length <= 32, "invalid bytes length", "type", type);
  328. }
  329. else if (match[3]) {
  330. // intXX or uintXX
  331. const size = parseInt(match[3]);
  332. assertArgument(size !== 0 && size <= 256 && (size % 8) === 0, "invalid numeric width", "type", type);
  333. }
  334. return type;
  335. }
  336. // Make the Fragment constructors effectively private
  337. const _guard = {};
  338. const internal = Symbol.for("_ethers_internal");
  339. const ParamTypeInternal = "_ParamTypeInternal";
  340. const ErrorFragmentInternal = "_ErrorInternal";
  341. const EventFragmentInternal = "_EventInternal";
  342. const ConstructorFragmentInternal = "_ConstructorInternal";
  343. const FallbackFragmentInternal = "_FallbackInternal";
  344. const FunctionFragmentInternal = "_FunctionInternal";
  345. const StructFragmentInternal = "_StructInternal";
  346. /**
  347. * Each input and output of a [[Fragment]] is an Array of **ParamType**.
  348. */
  349. export class ParamType {
  350. /**
  351. * The local name of the parameter (or ``""`` if unbound)
  352. */
  353. name;
  354. /**
  355. * The fully qualified type (e.g. ``"address"``, ``"tuple(address)"``,
  356. * ``"uint256[3][]"``)
  357. */
  358. type;
  359. /**
  360. * The base type (e.g. ``"address"``, ``"tuple"``, ``"array"``)
  361. */
  362. baseType;
  363. /**
  364. * True if the parameters is indexed.
  365. *
  366. * For non-indexable types this is ``null``.
  367. */
  368. indexed;
  369. /**
  370. * The components for the tuple.
  371. *
  372. * For non-tuple types this is ``null``.
  373. */
  374. components;
  375. /**
  376. * The array length, or ``-1`` for dynamic-lengthed arrays.
  377. *
  378. * For non-array types this is ``null``.
  379. */
  380. arrayLength;
  381. /**
  382. * The type of each child in the array.
  383. *
  384. * For non-array types this is ``null``.
  385. */
  386. arrayChildren;
  387. /**
  388. * @private
  389. */
  390. constructor(guard, name, type, baseType, indexed, components, arrayLength, arrayChildren) {
  391. assertPrivate(guard, _guard, "ParamType");
  392. Object.defineProperty(this, internal, { value: ParamTypeInternal });
  393. if (components) {
  394. components = Object.freeze(components.slice());
  395. }
  396. if (baseType === "array") {
  397. if (arrayLength == null || arrayChildren == null) {
  398. throw new Error("");
  399. }
  400. }
  401. else if (arrayLength != null || arrayChildren != null) {
  402. throw new Error("");
  403. }
  404. if (baseType === "tuple") {
  405. if (components == null) {
  406. throw new Error("");
  407. }
  408. }
  409. else if (components != null) {
  410. throw new Error("");
  411. }
  412. defineProperties(this, {
  413. name, type, baseType, indexed, components, arrayLength, arrayChildren
  414. });
  415. }
  416. /**
  417. * Return a string representation of this type.
  418. *
  419. * For example,
  420. *
  421. * ``sighash" => "(uint256,address)"``
  422. *
  423. * ``"minimal" => "tuple(uint256,address) indexed"``
  424. *
  425. * ``"full" => "tuple(uint256 foo, address bar) indexed baz"``
  426. */
  427. format(format) {
  428. if (format == null) {
  429. format = "sighash";
  430. }
  431. if (format === "json") {
  432. const name = this.name || "";
  433. if (this.isArray()) {
  434. const result = JSON.parse(this.arrayChildren.format("json"));
  435. result.name = name;
  436. result.type += `[${(this.arrayLength < 0 ? "" : String(this.arrayLength))}]`;
  437. return JSON.stringify(result);
  438. }
  439. const result = {
  440. type: ((this.baseType === "tuple") ? "tuple" : this.type),
  441. name
  442. };
  443. if (typeof (this.indexed) === "boolean") {
  444. result.indexed = this.indexed;
  445. }
  446. if (this.isTuple()) {
  447. result.components = this.components.map((c) => JSON.parse(c.format(format)));
  448. }
  449. return JSON.stringify(result);
  450. }
  451. let result = "";
  452. // Array
  453. if (this.isArray()) {
  454. result += this.arrayChildren.format(format);
  455. result += `[${(this.arrayLength < 0 ? "" : String(this.arrayLength))}]`;
  456. }
  457. else {
  458. if (this.isTuple()) {
  459. result += "(" + this.components.map((comp) => comp.format(format)).join((format === "full") ? ", " : ",") + ")";
  460. }
  461. else {
  462. result += this.type;
  463. }
  464. }
  465. if (format !== "sighash") {
  466. if (this.indexed === true) {
  467. result += " indexed";
  468. }
  469. if (format === "full" && this.name) {
  470. result += " " + this.name;
  471. }
  472. }
  473. return result;
  474. }
  475. /**
  476. * Returns true if %%this%% is an Array type.
  477. *
  478. * This provides a type gaurd ensuring that [[arrayChildren]]
  479. * and [[arrayLength]] are non-null.
  480. */
  481. isArray() {
  482. return (this.baseType === "array");
  483. }
  484. /**
  485. * Returns true if %%this%% is a Tuple type.
  486. *
  487. * This provides a type gaurd ensuring that [[components]]
  488. * is non-null.
  489. */
  490. isTuple() {
  491. return (this.baseType === "tuple");
  492. }
  493. /**
  494. * Returns true if %%this%% is an Indexable type.
  495. *
  496. * This provides a type gaurd ensuring that [[indexed]]
  497. * is non-null.
  498. */
  499. isIndexable() {
  500. return (this.indexed != null);
  501. }
  502. /**
  503. * Walks the **ParamType** with %%value%%, calling %%process%%
  504. * on each type, destructing the %%value%% recursively.
  505. */
  506. walk(value, process) {
  507. if (this.isArray()) {
  508. if (!Array.isArray(value)) {
  509. throw new Error("invalid array value");
  510. }
  511. if (this.arrayLength !== -1 && value.length !== this.arrayLength) {
  512. throw new Error("array is wrong length");
  513. }
  514. const _this = this;
  515. return value.map((v) => (_this.arrayChildren.walk(v, process)));
  516. }
  517. if (this.isTuple()) {
  518. if (!Array.isArray(value)) {
  519. throw new Error("invalid tuple value");
  520. }
  521. if (value.length !== this.components.length) {
  522. throw new Error("array is wrong length");
  523. }
  524. const _this = this;
  525. return value.map((v, i) => (_this.components[i].walk(v, process)));
  526. }
  527. return process(this.type, value);
  528. }
  529. #walkAsync(promises, value, process, setValue) {
  530. if (this.isArray()) {
  531. if (!Array.isArray(value)) {
  532. throw new Error("invalid array value");
  533. }
  534. if (this.arrayLength !== -1 && value.length !== this.arrayLength) {
  535. throw new Error("array is wrong length");
  536. }
  537. const childType = this.arrayChildren;
  538. const result = value.slice();
  539. result.forEach((value, index) => {
  540. childType.#walkAsync(promises, value, process, (value) => {
  541. result[index] = value;
  542. });
  543. });
  544. setValue(result);
  545. return;
  546. }
  547. if (this.isTuple()) {
  548. const components = this.components;
  549. // Convert the object into an array
  550. let result;
  551. if (Array.isArray(value)) {
  552. result = value.slice();
  553. }
  554. else {
  555. if (value == null || typeof (value) !== "object") {
  556. throw new Error("invalid tuple value");
  557. }
  558. result = components.map((param) => {
  559. if (!param.name) {
  560. throw new Error("cannot use object value with unnamed components");
  561. }
  562. if (!(param.name in value)) {
  563. throw new Error(`missing value for component ${param.name}`);
  564. }
  565. return value[param.name];
  566. });
  567. }
  568. if (result.length !== this.components.length) {
  569. throw new Error("array is wrong length");
  570. }
  571. result.forEach((value, index) => {
  572. components[index].#walkAsync(promises, value, process, (value) => {
  573. result[index] = value;
  574. });
  575. });
  576. setValue(result);
  577. return;
  578. }
  579. const result = process(this.type, value);
  580. if (result.then) {
  581. promises.push((async function () { setValue(await result); })());
  582. }
  583. else {
  584. setValue(result);
  585. }
  586. }
  587. /**
  588. * Walks the **ParamType** with %%value%%, asynchronously calling
  589. * %%process%% on each type, destructing the %%value%% recursively.
  590. *
  591. * This can be used to resolve ENS names by walking and resolving each
  592. * ``"address"`` type.
  593. */
  594. async walkAsync(value, process) {
  595. const promises = [];
  596. const result = [value];
  597. this.#walkAsync(promises, value, process, (value) => {
  598. result[0] = value;
  599. });
  600. if (promises.length) {
  601. await Promise.all(promises);
  602. }
  603. return result[0];
  604. }
  605. /**
  606. * Creates a new **ParamType** for %%obj%%.
  607. *
  608. * If %%allowIndexed%% then the ``indexed`` keyword is permitted,
  609. * otherwise the ``indexed`` keyword will throw an error.
  610. */
  611. static from(obj, allowIndexed) {
  612. if (ParamType.isParamType(obj)) {
  613. return obj;
  614. }
  615. if (typeof (obj) === "string") {
  616. try {
  617. return ParamType.from(lex(obj), allowIndexed);
  618. }
  619. catch (error) {
  620. assertArgument(false, "invalid param type", "obj", obj);
  621. }
  622. }
  623. else if (obj instanceof TokenString) {
  624. let type = "", baseType = "";
  625. let comps = null;
  626. if (consumeKeywords(obj, setify(["tuple"])).has("tuple") || obj.peekType("OPEN_PAREN")) {
  627. // Tuple
  628. baseType = "tuple";
  629. comps = obj.popParams().map((t) => ParamType.from(t));
  630. type = `tuple(${comps.map((c) => c.format()).join(",")})`;
  631. }
  632. else {
  633. // Normal
  634. type = verifyBasicType(obj.popType("TYPE"));
  635. baseType = type;
  636. }
  637. // Check for Array
  638. let arrayChildren = null;
  639. let arrayLength = null;
  640. while (obj.length && obj.peekType("BRACKET")) {
  641. const bracket = obj.pop(); //arrays[i];
  642. arrayChildren = new ParamType(_guard, "", type, baseType, null, comps, arrayLength, arrayChildren);
  643. arrayLength = bracket.value;
  644. type += bracket.text;
  645. baseType = "array";
  646. comps = null;
  647. }
  648. let indexed = null;
  649. const keywords = consumeKeywords(obj, KwModifiers);
  650. if (keywords.has("indexed")) {
  651. if (!allowIndexed) {
  652. throw new Error("");
  653. }
  654. indexed = true;
  655. }
  656. const name = (obj.peekType("ID") ? obj.pop().text : "");
  657. if (obj.length) {
  658. throw new Error("leftover tokens");
  659. }
  660. return new ParamType(_guard, name, type, baseType, indexed, comps, arrayLength, arrayChildren);
  661. }
  662. const name = obj.name;
  663. assertArgument(!name || (typeof (name) === "string" && name.match(regexId)), "invalid name", "obj.name", name);
  664. let indexed = obj.indexed;
  665. if (indexed != null) {
  666. assertArgument(allowIndexed, "parameter cannot be indexed", "obj.indexed", obj.indexed);
  667. indexed = !!indexed;
  668. }
  669. let type = obj.type;
  670. let arrayMatch = type.match(regexArrayType);
  671. if (arrayMatch) {
  672. const arrayLength = parseInt(arrayMatch[2] || "-1");
  673. const arrayChildren = ParamType.from({
  674. type: arrayMatch[1],
  675. components: obj.components
  676. });
  677. return new ParamType(_guard, name || "", type, "array", indexed, null, arrayLength, arrayChildren);
  678. }
  679. if (type === "tuple" || type.startsWith("tuple(" /* fix: ) */) || type.startsWith("(" /* fix: ) */)) {
  680. const comps = (obj.components != null) ? obj.components.map((c) => ParamType.from(c)) : null;
  681. const tuple = new ParamType(_guard, name || "", type, "tuple", indexed, comps, null, null);
  682. // @TODO: use lexer to validate and normalize type
  683. return tuple;
  684. }
  685. type = verifyBasicType(obj.type);
  686. return new ParamType(_guard, name || "", type, type, indexed, null, null, null);
  687. }
  688. /**
  689. * Returns true if %%value%% is a **ParamType**.
  690. */
  691. static isParamType(value) {
  692. return (value && value[internal] === ParamTypeInternal);
  693. }
  694. }
  695. /**
  696. * An abstract class to represent An individual fragment from a parse ABI.
  697. */
  698. export class Fragment {
  699. /**
  700. * The type of the fragment.
  701. */
  702. type;
  703. /**
  704. * The inputs for the fragment.
  705. */
  706. inputs;
  707. /**
  708. * @private
  709. */
  710. constructor(guard, type, inputs) {
  711. assertPrivate(guard, _guard, "Fragment");
  712. inputs = Object.freeze(inputs.slice());
  713. defineProperties(this, { type, inputs });
  714. }
  715. /**
  716. * Creates a new **Fragment** for %%obj%%, wich can be any supported
  717. * ABI frgament type.
  718. */
  719. static from(obj) {
  720. if (typeof (obj) === "string") {
  721. // Try parsing JSON...
  722. try {
  723. Fragment.from(JSON.parse(obj));
  724. }
  725. catch (e) { }
  726. // ...otherwise, use the human-readable lexer
  727. return Fragment.from(lex(obj));
  728. }
  729. if (obj instanceof TokenString) {
  730. // Human-readable ABI (already lexed)
  731. const type = obj.peekKeyword(KwTypes);
  732. switch (type) {
  733. case "constructor": return ConstructorFragment.from(obj);
  734. case "error": return ErrorFragment.from(obj);
  735. case "event": return EventFragment.from(obj);
  736. case "fallback":
  737. case "receive":
  738. return FallbackFragment.from(obj);
  739. case "function": return FunctionFragment.from(obj);
  740. case "struct": return StructFragment.from(obj);
  741. }
  742. }
  743. else if (typeof (obj) === "object") {
  744. // JSON ABI
  745. switch (obj.type) {
  746. case "constructor": return ConstructorFragment.from(obj);
  747. case "error": return ErrorFragment.from(obj);
  748. case "event": return EventFragment.from(obj);
  749. case "fallback":
  750. case "receive":
  751. return FallbackFragment.from(obj);
  752. case "function": return FunctionFragment.from(obj);
  753. case "struct": return StructFragment.from(obj);
  754. }
  755. assert(false, `unsupported type: ${obj.type}`, "UNSUPPORTED_OPERATION", {
  756. operation: "Fragment.from"
  757. });
  758. }
  759. assertArgument(false, "unsupported frgament object", "obj", obj);
  760. }
  761. /**
  762. * Returns true if %%value%% is a [[ConstructorFragment]].
  763. */
  764. static isConstructor(value) {
  765. return ConstructorFragment.isFragment(value);
  766. }
  767. /**
  768. * Returns true if %%value%% is an [[ErrorFragment]].
  769. */
  770. static isError(value) {
  771. return ErrorFragment.isFragment(value);
  772. }
  773. /**
  774. * Returns true if %%value%% is an [[EventFragment]].
  775. */
  776. static isEvent(value) {
  777. return EventFragment.isFragment(value);
  778. }
  779. /**
  780. * Returns true if %%value%% is a [[FunctionFragment]].
  781. */
  782. static isFunction(value) {
  783. return FunctionFragment.isFragment(value);
  784. }
  785. /**
  786. * Returns true if %%value%% is a [[StructFragment]].
  787. */
  788. static isStruct(value) {
  789. return StructFragment.isFragment(value);
  790. }
  791. }
  792. /**
  793. * An abstract class to represent An individual fragment
  794. * which has a name from a parse ABI.
  795. */
  796. export class NamedFragment extends Fragment {
  797. /**
  798. * The name of the fragment.
  799. */
  800. name;
  801. /**
  802. * @private
  803. */
  804. constructor(guard, type, name, inputs) {
  805. super(guard, type, inputs);
  806. assertArgument(typeof (name) === "string" && name.match(regexId), "invalid identifier", "name", name);
  807. inputs = Object.freeze(inputs.slice());
  808. defineProperties(this, { name });
  809. }
  810. }
  811. function joinParams(format, params) {
  812. return "(" + params.map((p) => p.format(format)).join((format === "full") ? ", " : ",") + ")";
  813. }
  814. /**
  815. * A Fragment which represents a //Custom Error//.
  816. */
  817. export class ErrorFragment extends NamedFragment {
  818. /**
  819. * @private
  820. */
  821. constructor(guard, name, inputs) {
  822. super(guard, "error", name, inputs);
  823. Object.defineProperty(this, internal, { value: ErrorFragmentInternal });
  824. }
  825. /**
  826. * The Custom Error selector.
  827. */
  828. get selector() {
  829. return id(this.format("sighash")).substring(0, 10);
  830. }
  831. /**
  832. * Returns a string representation of this fragment as %%format%%.
  833. */
  834. format(format) {
  835. if (format == null) {
  836. format = "sighash";
  837. }
  838. if (format === "json") {
  839. return JSON.stringify({
  840. type: "error",
  841. name: this.name,
  842. inputs: this.inputs.map((input) => JSON.parse(input.format(format))),
  843. });
  844. }
  845. const result = [];
  846. if (format !== "sighash") {
  847. result.push("error");
  848. }
  849. result.push(this.name + joinParams(format, this.inputs));
  850. return result.join(" ");
  851. }
  852. /**
  853. * Returns a new **ErrorFragment** for %%obj%%.
  854. */
  855. static from(obj) {
  856. if (ErrorFragment.isFragment(obj)) {
  857. return obj;
  858. }
  859. if (typeof (obj) === "string") {
  860. return ErrorFragment.from(lex(obj));
  861. }
  862. else if (obj instanceof TokenString) {
  863. const name = consumeName("error", obj);
  864. const inputs = consumeParams(obj);
  865. consumeEoi(obj);
  866. return new ErrorFragment(_guard, name, inputs);
  867. }
  868. return new ErrorFragment(_guard, obj.name, obj.inputs ? obj.inputs.map(ParamType.from) : []);
  869. }
  870. /**
  871. * Returns ``true`` and provides a type guard if %%value%% is an
  872. * **ErrorFragment**.
  873. */
  874. static isFragment(value) {
  875. return (value && value[internal] === ErrorFragmentInternal);
  876. }
  877. }
  878. /**
  879. * A Fragment which represents an Event.
  880. */
  881. export class EventFragment extends NamedFragment {
  882. /**
  883. * Whether this event is anonymous.
  884. */
  885. anonymous;
  886. /**
  887. * @private
  888. */
  889. constructor(guard, name, inputs, anonymous) {
  890. super(guard, "event", name, inputs);
  891. Object.defineProperty(this, internal, { value: EventFragmentInternal });
  892. defineProperties(this, { anonymous });
  893. }
  894. /**
  895. * The Event topic hash.
  896. */
  897. get topicHash() {
  898. return id(this.format("sighash"));
  899. }
  900. /**
  901. * Returns a string representation of this event as %%format%%.
  902. */
  903. format(format) {
  904. if (format == null) {
  905. format = "sighash";
  906. }
  907. if (format === "json") {
  908. return JSON.stringify({
  909. type: "event",
  910. anonymous: this.anonymous,
  911. name: this.name,
  912. inputs: this.inputs.map((i) => JSON.parse(i.format(format)))
  913. });
  914. }
  915. const result = [];
  916. if (format !== "sighash") {
  917. result.push("event");
  918. }
  919. result.push(this.name + joinParams(format, this.inputs));
  920. if (format !== "sighash" && this.anonymous) {
  921. result.push("anonymous");
  922. }
  923. return result.join(" ");
  924. }
  925. /**
  926. * Return the topic hash for an event with %%name%% and %%params%%.
  927. */
  928. static getTopicHash(name, params) {
  929. params = (params || []).map((p) => ParamType.from(p));
  930. const fragment = new EventFragment(_guard, name, params, false);
  931. return fragment.topicHash;
  932. }
  933. /**
  934. * Returns a new **EventFragment** for %%obj%%.
  935. */
  936. static from(obj) {
  937. if (EventFragment.isFragment(obj)) {
  938. return obj;
  939. }
  940. if (typeof (obj) === "string") {
  941. try {
  942. return EventFragment.from(lex(obj));
  943. }
  944. catch (error) {
  945. assertArgument(false, "invalid event fragment", "obj", obj);
  946. }
  947. }
  948. else if (obj instanceof TokenString) {
  949. const name = consumeName("event", obj);
  950. const inputs = consumeParams(obj, true);
  951. const anonymous = !!consumeKeywords(obj, setify(["anonymous"])).has("anonymous");
  952. consumeEoi(obj);
  953. return new EventFragment(_guard, name, inputs, anonymous);
  954. }
  955. return new EventFragment(_guard, obj.name, obj.inputs ? obj.inputs.map((p) => ParamType.from(p, true)) : [], !!obj.anonymous);
  956. }
  957. /**
  958. * Returns ``true`` and provides a type guard if %%value%% is an
  959. * **EventFragment**.
  960. */
  961. static isFragment(value) {
  962. return (value && value[internal] === EventFragmentInternal);
  963. }
  964. }
  965. /**
  966. * A Fragment which represents a constructor.
  967. */
  968. export class ConstructorFragment extends Fragment {
  969. /**
  970. * Whether the constructor can receive an endowment.
  971. */
  972. payable;
  973. /**
  974. * The recommended gas limit for deployment or ``null``.
  975. */
  976. gas;
  977. /**
  978. * @private
  979. */
  980. constructor(guard, type, inputs, payable, gas) {
  981. super(guard, type, inputs);
  982. Object.defineProperty(this, internal, { value: ConstructorFragmentInternal });
  983. defineProperties(this, { payable, gas });
  984. }
  985. /**
  986. * Returns a string representation of this constructor as %%format%%.
  987. */
  988. format(format) {
  989. assert(format != null && format !== "sighash", "cannot format a constructor for sighash", "UNSUPPORTED_OPERATION", { operation: "format(sighash)" });
  990. if (format === "json") {
  991. return JSON.stringify({
  992. type: "constructor",
  993. stateMutability: (this.payable ? "payable" : "undefined"),
  994. payable: this.payable,
  995. gas: ((this.gas != null) ? this.gas : undefined),
  996. inputs: this.inputs.map((i) => JSON.parse(i.format(format)))
  997. });
  998. }
  999. const result = [`constructor${joinParams(format, this.inputs)}`];
  1000. if (this.payable) {
  1001. result.push("payable");
  1002. }
  1003. if (this.gas != null) {
  1004. result.push(`@${this.gas.toString()}`);
  1005. }
  1006. return result.join(" ");
  1007. }
  1008. /**
  1009. * Returns a new **ConstructorFragment** for %%obj%%.
  1010. */
  1011. static from(obj) {
  1012. if (ConstructorFragment.isFragment(obj)) {
  1013. return obj;
  1014. }
  1015. if (typeof (obj) === "string") {
  1016. try {
  1017. return ConstructorFragment.from(lex(obj));
  1018. }
  1019. catch (error) {
  1020. assertArgument(false, "invalid constuctor fragment", "obj", obj);
  1021. }
  1022. }
  1023. else if (obj instanceof TokenString) {
  1024. consumeKeywords(obj, setify(["constructor"]));
  1025. const inputs = consumeParams(obj);
  1026. const payable = !!consumeKeywords(obj, KwVisibDeploy).has("payable");
  1027. const gas = consumeGas(obj);
  1028. consumeEoi(obj);
  1029. return new ConstructorFragment(_guard, "constructor", inputs, payable, gas);
  1030. }
  1031. return new ConstructorFragment(_guard, "constructor", obj.inputs ? obj.inputs.map(ParamType.from) : [], !!obj.payable, (obj.gas != null) ? obj.gas : null);
  1032. }
  1033. /**
  1034. * Returns ``true`` and provides a type guard if %%value%% is a
  1035. * **ConstructorFragment**.
  1036. */
  1037. static isFragment(value) {
  1038. return (value && value[internal] === ConstructorFragmentInternal);
  1039. }
  1040. }
  1041. /**
  1042. * A Fragment which represents a method.
  1043. */
  1044. export class FallbackFragment extends Fragment {
  1045. /**
  1046. * If the function can be sent value during invocation.
  1047. */
  1048. payable;
  1049. constructor(guard, inputs, payable) {
  1050. super(guard, "fallback", inputs);
  1051. Object.defineProperty(this, internal, { value: FallbackFragmentInternal });
  1052. defineProperties(this, { payable });
  1053. }
  1054. /**
  1055. * Returns a string representation of this fallback as %%format%%.
  1056. */
  1057. format(format) {
  1058. const type = ((this.inputs.length === 0) ? "receive" : "fallback");
  1059. if (format === "json") {
  1060. const stateMutability = (this.payable ? "payable" : "nonpayable");
  1061. return JSON.stringify({ type, stateMutability });
  1062. }
  1063. return `${type}()${this.payable ? " payable" : ""}`;
  1064. }
  1065. /**
  1066. * Returns a new **FallbackFragment** for %%obj%%.
  1067. */
  1068. static from(obj) {
  1069. if (FallbackFragment.isFragment(obj)) {
  1070. return obj;
  1071. }
  1072. if (typeof (obj) === "string") {
  1073. try {
  1074. return FallbackFragment.from(lex(obj));
  1075. }
  1076. catch (error) {
  1077. assertArgument(false, "invalid fallback fragment", "obj", obj);
  1078. }
  1079. }
  1080. else if (obj instanceof TokenString) {
  1081. const errorObj = obj.toString();
  1082. const topIsValid = obj.peekKeyword(setify(["fallback", "receive"]));
  1083. assertArgument(topIsValid, "type must be fallback or receive", "obj", errorObj);
  1084. const type = obj.popKeyword(setify(["fallback", "receive"]));
  1085. // receive()
  1086. if (type === "receive") {
  1087. const inputs = consumeParams(obj);
  1088. assertArgument(inputs.length === 0, `receive cannot have arguments`, "obj.inputs", inputs);
  1089. consumeKeywords(obj, setify(["payable"]));
  1090. consumeEoi(obj);
  1091. return new FallbackFragment(_guard, [], true);
  1092. }
  1093. // fallback() [payable]
  1094. // fallback(bytes) [payable] returns (bytes)
  1095. let inputs = consumeParams(obj);
  1096. if (inputs.length) {
  1097. assertArgument(inputs.length === 1 && inputs[0].type === "bytes", "invalid fallback inputs", "obj.inputs", inputs.map((i) => i.format("minimal")).join(", "));
  1098. }
  1099. else {
  1100. inputs = [ParamType.from("bytes")];
  1101. }
  1102. const mutability = consumeMutability(obj);
  1103. assertArgument(mutability === "nonpayable" || mutability === "payable", "fallback cannot be constants", "obj.stateMutability", mutability);
  1104. if (consumeKeywords(obj, setify(["returns"])).has("returns")) {
  1105. const outputs = consumeParams(obj);
  1106. assertArgument(outputs.length === 1 && outputs[0].type === "bytes", "invalid fallback outputs", "obj.outputs", outputs.map((i) => i.format("minimal")).join(", "));
  1107. }
  1108. consumeEoi(obj);
  1109. return new FallbackFragment(_guard, inputs, mutability === "payable");
  1110. }
  1111. if (obj.type === "receive") {
  1112. return new FallbackFragment(_guard, [], true);
  1113. }
  1114. if (obj.type === "fallback") {
  1115. const inputs = [ParamType.from("bytes")];
  1116. const payable = (obj.stateMutability === "payable");
  1117. return new FallbackFragment(_guard, inputs, payable);
  1118. }
  1119. assertArgument(false, "invalid fallback description", "obj", obj);
  1120. }
  1121. /**
  1122. * Returns ``true`` and provides a type guard if %%value%% is a
  1123. * **FallbackFragment**.
  1124. */
  1125. static isFragment(value) {
  1126. return (value && value[internal] === FallbackFragmentInternal);
  1127. }
  1128. }
  1129. /**
  1130. * A Fragment which represents a method.
  1131. */
  1132. export class FunctionFragment extends NamedFragment {
  1133. /**
  1134. * If the function is constant (e.g. ``pure`` or ``view`` functions).
  1135. */
  1136. constant;
  1137. /**
  1138. * The returned types for the result of calling this function.
  1139. */
  1140. outputs;
  1141. /**
  1142. * The state mutability (e.g. ``payable``, ``nonpayable``, ``view``
  1143. * or ``pure``)
  1144. */
  1145. stateMutability;
  1146. /**
  1147. * If the function can be sent value during invocation.
  1148. */
  1149. payable;
  1150. /**
  1151. * The recommended gas limit to send when calling this function.
  1152. */
  1153. gas;
  1154. /**
  1155. * @private
  1156. */
  1157. constructor(guard, name, stateMutability, inputs, outputs, gas) {
  1158. super(guard, "function", name, inputs);
  1159. Object.defineProperty(this, internal, { value: FunctionFragmentInternal });
  1160. outputs = Object.freeze(outputs.slice());
  1161. const constant = (stateMutability === "view" || stateMutability === "pure");
  1162. const payable = (stateMutability === "payable");
  1163. defineProperties(this, { constant, gas, outputs, payable, stateMutability });
  1164. }
  1165. /**
  1166. * The Function selector.
  1167. */
  1168. get selector() {
  1169. return id(this.format("sighash")).substring(0, 10);
  1170. }
  1171. /**
  1172. * Returns a string representation of this function as %%format%%.
  1173. */
  1174. format(format) {
  1175. if (format == null) {
  1176. format = "sighash";
  1177. }
  1178. if (format === "json") {
  1179. return JSON.stringify({
  1180. type: "function",
  1181. name: this.name,
  1182. constant: this.constant,
  1183. stateMutability: ((this.stateMutability !== "nonpayable") ? this.stateMutability : undefined),
  1184. payable: this.payable,
  1185. gas: ((this.gas != null) ? this.gas : undefined),
  1186. inputs: this.inputs.map((i) => JSON.parse(i.format(format))),
  1187. outputs: this.outputs.map((o) => JSON.parse(o.format(format))),
  1188. });
  1189. }
  1190. const result = [];
  1191. if (format !== "sighash") {
  1192. result.push("function");
  1193. }
  1194. result.push(this.name + joinParams(format, this.inputs));
  1195. if (format !== "sighash") {
  1196. if (this.stateMutability !== "nonpayable") {
  1197. result.push(this.stateMutability);
  1198. }
  1199. if (this.outputs && this.outputs.length) {
  1200. result.push("returns");
  1201. result.push(joinParams(format, this.outputs));
  1202. }
  1203. if (this.gas != null) {
  1204. result.push(`@${this.gas.toString()}`);
  1205. }
  1206. }
  1207. return result.join(" ");
  1208. }
  1209. /**
  1210. * Return the selector for a function with %%name%% and %%params%%.
  1211. */
  1212. static getSelector(name, params) {
  1213. params = (params || []).map((p) => ParamType.from(p));
  1214. const fragment = new FunctionFragment(_guard, name, "view", params, [], null);
  1215. return fragment.selector;
  1216. }
  1217. /**
  1218. * Returns a new **FunctionFragment** for %%obj%%.
  1219. */
  1220. static from(obj) {
  1221. if (FunctionFragment.isFragment(obj)) {
  1222. return obj;
  1223. }
  1224. if (typeof (obj) === "string") {
  1225. try {
  1226. return FunctionFragment.from(lex(obj));
  1227. }
  1228. catch (error) {
  1229. assertArgument(false, "invalid function fragment", "obj", obj);
  1230. }
  1231. }
  1232. else if (obj instanceof TokenString) {
  1233. const name = consumeName("function", obj);
  1234. const inputs = consumeParams(obj);
  1235. const mutability = consumeMutability(obj);
  1236. let outputs = [];
  1237. if (consumeKeywords(obj, setify(["returns"])).has("returns")) {
  1238. outputs = consumeParams(obj);
  1239. }
  1240. const gas = consumeGas(obj);
  1241. consumeEoi(obj);
  1242. return new FunctionFragment(_guard, name, mutability, inputs, outputs, gas);
  1243. }
  1244. let stateMutability = obj.stateMutability;
  1245. // Use legacy Solidity ABI logic if stateMutability is missing
  1246. if (stateMutability == null) {
  1247. stateMutability = "payable";
  1248. if (typeof (obj.constant) === "boolean") {
  1249. stateMutability = "view";
  1250. if (!obj.constant) {
  1251. stateMutability = "payable";
  1252. if (typeof (obj.payable) === "boolean" && !obj.payable) {
  1253. stateMutability = "nonpayable";
  1254. }
  1255. }
  1256. }
  1257. else if (typeof (obj.payable) === "boolean" && !obj.payable) {
  1258. stateMutability = "nonpayable";
  1259. }
  1260. }
  1261. // @TODO: verifyState for stateMutability (e.g. throw if
  1262. // payable: false but stateMutability is "nonpayable")
  1263. return new FunctionFragment(_guard, obj.name, stateMutability, obj.inputs ? obj.inputs.map(ParamType.from) : [], obj.outputs ? obj.outputs.map(ParamType.from) : [], (obj.gas != null) ? obj.gas : null);
  1264. }
  1265. /**
  1266. * Returns ``true`` and provides a type guard if %%value%% is a
  1267. * **FunctionFragment**.
  1268. */
  1269. static isFragment(value) {
  1270. return (value && value[internal] === FunctionFragmentInternal);
  1271. }
  1272. }
  1273. /**
  1274. * A Fragment which represents a structure.
  1275. */
  1276. export class StructFragment extends NamedFragment {
  1277. /**
  1278. * @private
  1279. */
  1280. constructor(guard, name, inputs) {
  1281. super(guard, "struct", name, inputs);
  1282. Object.defineProperty(this, internal, { value: StructFragmentInternal });
  1283. }
  1284. /**
  1285. * Returns a string representation of this struct as %%format%%.
  1286. */
  1287. format() {
  1288. throw new Error("@TODO");
  1289. }
  1290. /**
  1291. * Returns a new **StructFragment** for %%obj%%.
  1292. */
  1293. static from(obj) {
  1294. if (typeof (obj) === "string") {
  1295. try {
  1296. return StructFragment.from(lex(obj));
  1297. }
  1298. catch (error) {
  1299. assertArgument(false, "invalid struct fragment", "obj", obj);
  1300. }
  1301. }
  1302. else if (obj instanceof TokenString) {
  1303. const name = consumeName("struct", obj);
  1304. const inputs = consumeParams(obj);
  1305. consumeEoi(obj);
  1306. return new StructFragment(_guard, name, inputs);
  1307. }
  1308. return new StructFragment(_guard, obj.name, obj.inputs ? obj.inputs.map(ParamType.from) : []);
  1309. }
  1310. // @TODO: fix this return type
  1311. /**
  1312. * Returns ``true`` and provides a type guard if %%value%% is a
  1313. * **StructFragment**.
  1314. */
  1315. static isFragment(value) {
  1316. return (value && value[internal] === StructFragmentInternal);
  1317. }
  1318. }
  1319. //# sourceMappingURL=fragments.js.map