123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617 |
- /**
- * A fragment is a single item from an ABI, which may represent any of:
- *
- * - [Functions](FunctionFragment)
- * - [Events](EventFragment)
- * - [Constructors](ConstructorFragment)
- * - Custom [Errors](ErrorFragment)
- * - [Fallback or Receive](FallbackFragment) functions
- *
- * @_subsection api/abi/abi-coder:Fragments [about-fragments]
- */
- import {
- defineProperties, getBigInt, getNumber,
- assert, assertPrivate, assertArgument
- } from "../utils/index.js";
- import { id } from "../hash/index.js";
- /**
- * A Type description in a [JSON ABI format](link-solc-jsonabi).
- */
- export interface JsonFragmentType {
- /**
- * The parameter name.
- */
- readonly name?: string;
- /**
- * If the parameter is indexed.
- */
- readonly indexed?: boolean;
- /**
- * The type of the parameter.
- */
- readonly type?: string;
- /**
- * The internal Solidity type.
- */
- readonly internalType?: string;
- /**
- * The components for a tuple.
- */
- readonly components?: ReadonlyArray<JsonFragmentType>;
- }
- /**
- * A fragment for a method, event or error in a [JSON ABI format](link-solc-jsonabi).
- */
- export interface JsonFragment {
- /**
- * The name of the error, event, function, etc.
- */
- readonly name?: string;
- /**
- * The type of the fragment (e.g. ``event``, ``"function"``, etc.)
- */
- readonly type?: string;
- /**
- * If the event is anonymous.
- */
- readonly anonymous?: boolean;
- /**
- * If the function is payable.
- */
- readonly payable?: boolean;
- /**
- * If the function is constant.
- */
- readonly constant?: boolean;
- /**
- * The mutability state of the function.
- */
- readonly stateMutability?: string;
- /**
- * The input parameters.
- */
- readonly inputs?: ReadonlyArray<JsonFragmentType>;
- /**
- * The output parameters.
- */
- readonly outputs?: ReadonlyArray<JsonFragmentType>;
- /**
- * The gas limit to use when sending a transaction for this function.
- */
- readonly gas?: string;
- };
- /**
- * The format to serialize the output as.
- *
- * **``"sighash"``** - the bare formatting, used to compute the selector
- * or topic hash; this format cannot be reversed (as it discards ``indexed``)
- * so cannot by used to export an [[Interface]].
- *
- * **``"minimal"``** - Human-Readable ABI with minimal spacing and without
- * names, so it is compact, but will result in Result objects that cannot
- * be accessed by name.
- *
- * **``"full"``** - Full Human-Readable ABI, with readable spacing and names
- * intact; this is generally the recommended format.
- *
- * **``"json"``** - The [JSON ABI format](link-solc-jsonabi).
- */
- export type FormatType = "sighash" | "minimal" | "full" | "json";
- // [ "a", "b" ] => { "a": 1, "b": 1 }
- function setify(items: Array<string>): ReadonlySet<string> {
- const result: Set<string> = new Set();
- items.forEach((k) => result.add(k));
- return Object.freeze(result);
- }
- const _kwVisibDeploy = "external public payable override";
- const KwVisibDeploy = setify(_kwVisibDeploy.split(" "));
- // Visibility Keywords
- const _kwVisib = "constant external internal payable private public pure view override";
- const KwVisib = setify(_kwVisib.split(" "));
- const _kwTypes = "constructor error event fallback function receive struct";
- const KwTypes = setify(_kwTypes.split(" "));
- const _kwModifiers = "calldata memory storage payable indexed";
- const KwModifiers = setify(_kwModifiers.split(" "));
- const _kwOther = "tuple returns";
- // All Keywords
- const _keywords = [ _kwTypes, _kwModifiers, _kwOther, _kwVisib ].join(" ");
- const Keywords = setify(_keywords.split(" "));
- // Single character tokens
- const SimpleTokens: Record<string, string> = {
- "(": "OPEN_PAREN", ")": "CLOSE_PAREN",
- "[": "OPEN_BRACKET", "]": "CLOSE_BRACKET",
- ",": "COMMA", "@": "AT"
- };
- // Parser regexes to consume the next token
- const regexWhitespacePrefix = new RegExp("^(\\s*)");
- const regexNumberPrefix = new RegExp("^([0-9]+)");
- const regexIdPrefix = new RegExp("^([a-zA-Z$_][a-zA-Z0-9$_]*)");
- // Parser regexs to check validity
- const regexId = new RegExp("^([a-zA-Z$_][a-zA-Z0-9$_]*)$");
- const regexType = new RegExp("^(address|bool|bytes([0-9]*)|string|u?int([0-9]*))$");
- /**
- * @ignore:
- */
- type Token = Readonly<{
- // Type of token (e.g. TYPE, KEYWORD, NUMBER, etc)
- type: string;
- // Offset into the original source code
- offset: number;
- // Actual text content of the token
- text: string;
- // The parenthesis depth
- depth: number;
- // If a parenthesis, the offset (in tokens) that balances it
- match: number;
- // For parenthesis and commas, the offset (in tokens) to the
- // previous/next parenthesis or comma in the list
- linkBack: number;
- linkNext: number;
- // If a BRACKET, the value inside
- value: number;
- }>;
- class TokenString {
- #offset: number;
- #tokens: ReadonlyArray<Token>;
- get offset(): number { return this.#offset; }
- get length(): number { return this.#tokens.length - this.#offset; }
- constructor(tokens: ReadonlyArray<Token>) {
- this.#offset = 0;
- this.#tokens = tokens.slice();
- }
- clone(): TokenString { return new TokenString(this.#tokens); }
- reset(): void { this.#offset = 0; }
- #subTokenString(from: number = 0, to: number = 0): TokenString {
- return new TokenString(this.#tokens.slice(from, to).map((t) => {
- return Object.freeze(Object.assign({ }, t, {
- match: (t.match - from),
- linkBack: (t.linkBack - from),
- linkNext: (t.linkNext - from),
- }));
- }));
- }
- // Pops and returns the value of the next token, if it is a keyword in allowed; throws if out of tokens
- popKeyword(allowed: ReadonlySet<string>): string {
- const top = this.peek();
- if (top.type !== "KEYWORD" || !allowed.has(top.text)) { throw new Error(`expected keyword ${ top.text }`); }
- return this.pop().text;
- }
- // Pops and returns the value of the next token if it is `type`; throws if out of tokens
- popType(type: string): string {
- if (this.peek().type !== type) {
- const top = this.peek();
- throw new Error(`expected ${ type }; got ${ top.type } ${ JSON.stringify(top.text) }`);
- }
- return this.pop().text;
- }
- // Pops and returns a "(" TOKENS ")"
- popParen(): TokenString {
- const top = this.peek();
- if (top.type !== "OPEN_PAREN") { throw new Error("bad start"); }
- const result = this.#subTokenString(this.#offset + 1, top.match + 1);
- this.#offset = top.match + 1;
- return result;
- }
- // Pops and returns the items within "(" ITEM1 "," ITEM2 "," ... ")"
- popParams(): Array<TokenString> {
- const top = this.peek();
- if (top.type !== "OPEN_PAREN") { throw new Error("bad start"); }
- const result: Array<TokenString> = [ ];
- while(this.#offset < top.match - 1) {
- const link = this.peek().linkNext;
- result.push(this.#subTokenString(this.#offset + 1, link));
- this.#offset = link;
- }
- this.#offset = top.match + 1;
- return result;
- }
- // Returns the top Token, throwing if out of tokens
- peek(): Token {
- if (this.#offset >= this.#tokens.length) {
- throw new Error("out-of-bounds");
- }
- return this.#tokens[this.#offset];
- }
- // Returns the next value, if it is a keyword in `allowed`
- peekKeyword(allowed: ReadonlySet<string>): null | string {
- const top = this.peekType("KEYWORD");
- return (top != null && allowed.has(top)) ? top: null;
- }
- // Returns the value of the next token if it is `type`
- peekType(type: string): null | string {
- if (this.length === 0) { return null; }
- const top = this.peek();
- return (top.type === type) ? top.text: null;
- }
- // Returns the next token; throws if out of tokens
- pop(): Token {
- const result = this.peek();
- this.#offset++;
- return result;
- }
- toString(): string {
- const tokens: Array<string> = [ ];
- for (let i = this.#offset; i < this.#tokens.length; i++) {
- const token = this.#tokens[i];
- tokens.push(`${ token.type }:${ token.text }`);
- }
- return `<TokenString ${ tokens.join(" ") }>`
- }
- }
- type Writeable<T> = { -readonly [P in keyof T]: T[P] };
- function lex(text: string): TokenString {
- const tokens: Array<Token> = [ ];
- const throwError = (message: string) => {
- const token = (offset < text.length) ? JSON.stringify(text[offset]): "$EOI";
- throw new Error(`invalid token ${ token } at ${ offset }: ${ message }`);
- };
- let brackets: Array<number> = [ ];
- let commas: Array<number> = [ ];
- let offset = 0;
- while (offset < text.length) {
- // Strip off any leading whitespace
- let cur = text.substring(offset);
- let match = cur.match(regexWhitespacePrefix);
- if (match) {
- offset += match[1].length;
- cur = text.substring(offset);
- }
- const token = { depth: brackets.length, linkBack: -1, linkNext: -1, match: -1, type: "", text: "", offset, value: -1 };
- tokens.push(token);
- let type = (SimpleTokens[cur[0]] || "");
- if (type) {
- token.type = type;
- token.text = cur[0];
- offset++;
- if (type === "OPEN_PAREN") {
- brackets.push(tokens.length - 1);
- commas.push(tokens.length - 1);
- } else if (type == "CLOSE_PAREN") {
- if (brackets.length === 0) { throwError("no matching open bracket"); }
- token.match = brackets.pop() as number;
- (<Writeable<Token>>(tokens[token.match])).match = tokens.length - 1;
- token.depth--;
- token.linkBack = commas.pop() as number;
- (<Writeable<Token>>(tokens[token.linkBack])).linkNext = tokens.length - 1;
- } else if (type === "COMMA") {
- token.linkBack = commas.pop() as number;
- (<Writeable<Token>>(tokens[token.linkBack])).linkNext = tokens.length - 1;
- commas.push(tokens.length - 1);
- } else if (type === "OPEN_BRACKET") {
- token.type = "BRACKET";
- } else if (type === "CLOSE_BRACKET") {
- // Remove the CLOSE_BRACKET
- let suffix = (tokens.pop() as Token).text;
- if (tokens.length > 0 && tokens[tokens.length - 1].type === "NUMBER") {
- const value = (tokens.pop() as Token).text;
- suffix = value + suffix;
- (<Writeable<Token>>(tokens[tokens.length - 1])).value = getNumber(value);
- }
- if (tokens.length === 0 || tokens[tokens.length - 1].type !== "BRACKET") {
- throw new Error("missing opening bracket");
- }
- (<Writeable<Token>>(tokens[tokens.length - 1])).text += suffix;
- }
- continue;
- }
- match = cur.match(regexIdPrefix);
- if (match) {
- token.text = match[1];
- offset += token.text.length;
- if (Keywords.has(token.text)) {
- token.type = "KEYWORD";
- continue;
- }
- if (token.text.match(regexType)) {
- token.type = "TYPE";
- continue;
- }
- token.type = "ID";
- continue;
- }
- match = cur.match(regexNumberPrefix);
- if (match) {
- token.text = match[1];
- token.type = "NUMBER";
- offset += token.text.length;
- continue;
- }
- throw new Error(`unexpected token ${ JSON.stringify(cur[0]) } at position ${ offset }`);
- }
- return new TokenString(tokens.map((t) => Object.freeze(t)));
- }
- // Check only one of `allowed` is in `set`
- function allowSingle(set: ReadonlySet<string>, allowed: ReadonlySet<string>): void {
- let included: Array<string> = [ ];
- for (const key in allowed.keys()) {
- if (set.has(key)) { included.push(key); }
- }
- if (included.length > 1) { throw new Error(`conflicting types: ${ included.join(", ") }`); }
- }
- // Functions to process a Solidity Signature TokenString from left-to-right for...
- // ...the name with an optional type, returning the name
- function consumeName(type: string, tokens: TokenString): string {
- if (tokens.peekKeyword(KwTypes)) {
- const keyword = tokens.pop().text;
- if (keyword !== type) {
- throw new Error(`expected ${ type }, got ${ keyword }`);
- }
- }
- return tokens.popType("ID");
- }
- // ...all keywords matching allowed, returning the keywords
- function consumeKeywords(tokens: TokenString, allowed?: ReadonlySet<string>): ReadonlySet<string> {
- const keywords: Set<string> = new Set();
- while (true) {
- const keyword = tokens.peekType("KEYWORD");
- if (keyword == null || (allowed && !allowed.has(keyword))) { break; }
- tokens.pop();
- if (keywords.has(keyword)) { throw new Error(`duplicate keywords: ${ JSON.stringify(keyword) }`); }
- keywords.add(keyword);
- }
- return Object.freeze(keywords);
- }
- // ...all visibility keywords, returning the coalesced mutability
- function consumeMutability(tokens: TokenString): "payable" | "nonpayable" | "view" | "pure" {
- let modifiers = consumeKeywords(tokens, KwVisib);
- // Detect conflicting modifiers
- allowSingle(modifiers, setify("constant payable nonpayable".split(" ")));
- allowSingle(modifiers, setify("pure view payable nonpayable".split(" ")));
- // Process mutability states
- if (modifiers.has("view")) { return "view"; }
- if (modifiers.has("pure")) { return "pure"; }
- if (modifiers.has("payable")) { return "payable"; }
- if (modifiers.has("nonpayable")) { return "nonpayable"; }
- // Process legacy `constant` last
- if (modifiers.has("constant")) { return "view"; }
- return "nonpayable";
- }
- // ...a parameter list, returning the ParamType list
- function consumeParams(tokens: TokenString, allowIndexed?: boolean): Array<ParamType> {
- return tokens.popParams().map((t) => ParamType.from(t, allowIndexed));
- }
- // ...a gas limit, returning a BigNumber or null if none
- function consumeGas(tokens: TokenString): null | bigint {
- if (tokens.peekType("AT")) {
- tokens.pop();
- if (tokens.peekType("NUMBER")) {
- return getBigInt(tokens.pop().text);
- }
- throw new Error("invalid gas");
- }
- return null;
- }
- function consumeEoi(tokens: TokenString): void {
- if (tokens.length) {
- throw new Error(`unexpected tokens at offset ${ tokens.offset }: ${ tokens.toString() }`);
- }
- }
- const regexArrayType = new RegExp(/^(.*)\[([0-9]*)\]$/);
- function verifyBasicType(type: string): string {
- const match = type.match(regexType);
- assertArgument(match, "invalid type", "type", type);
- if (type === "uint") { return "uint256"; }
- if (type === "int") { return "int256"; }
- if (match[2]) {
- // bytesXX
- const length = parseInt(match[2]);
- assertArgument(length !== 0 && length <= 32, "invalid bytes length", "type", type);
- } else if (match[3]) {
- // intXX or uintXX
- const size = parseInt(match[3] as string);
- assertArgument(size !== 0 && size <= 256 && (size % 8) === 0, "invalid numeric width", "type", type);
- }
- return type;
- }
- // Make the Fragment constructors effectively private
- const _guard = { };
- /**
- * When [walking](ParamType-walk) a [[ParamType]], this is called
- * on each component.
- */
- export type ParamTypeWalkFunc = (type: string, value: any) => any;
- /**
- * When [walking asynchronously](ParamType-walkAsync) a [[ParamType]],
- * this is called on each component.
- */
- export type ParamTypeWalkAsyncFunc = (type: string, value: any) => any | Promise<any>;
- const internal = Symbol.for("_ethers_internal");
- const ParamTypeInternal = "_ParamTypeInternal";
- const ErrorFragmentInternal = "_ErrorInternal";
- const EventFragmentInternal = "_EventInternal";
- const ConstructorFragmentInternal = "_ConstructorInternal";
- const FallbackFragmentInternal = "_FallbackInternal";
- const FunctionFragmentInternal = "_FunctionInternal";
- const StructFragmentInternal = "_StructInternal";
- /**
- * Each input and output of a [[Fragment]] is an Array of **ParamType**.
- */
- export class ParamType {
- /**
- * The local name of the parameter (or ``""`` if unbound)
- */
- readonly name!: string;
- /**
- * The fully qualified type (e.g. ``"address"``, ``"tuple(address)"``,
- * ``"uint256[3][]"``)
- */
- readonly type!: string;
- /**
- * The base type (e.g. ``"address"``, ``"tuple"``, ``"array"``)
- */
- readonly baseType!: string;
- /**
- * True if the parameters is indexed.
- *
- * For non-indexable types this is ``null``.
- */
- readonly indexed!: null | boolean;
- /**
- * The components for the tuple.
- *
- * For non-tuple types this is ``null``.
- */
- readonly components!: null | ReadonlyArray<ParamType>;
- /**
- * The array length, or ``-1`` for dynamic-lengthed arrays.
- *
- * For non-array types this is ``null``.
- */
- readonly arrayLength!: null | number;
- /**
- * The type of each child in the array.
- *
- * For non-array types this is ``null``.
- */
- readonly arrayChildren!: null | ParamType;
- /**
- * @private
- */
- constructor(guard: any, name: string, type: string, baseType: string, indexed: null | boolean, components: null | ReadonlyArray<ParamType>, arrayLength: null | number, arrayChildren: null | ParamType) {
- assertPrivate(guard, _guard, "ParamType");
- Object.defineProperty(this, internal, { value: ParamTypeInternal });
- if (components) { components = Object.freeze(components.slice()); }
- if (baseType === "array") {
- if (arrayLength == null || arrayChildren == null) {
- throw new Error("");
- }
- } else if (arrayLength != null || arrayChildren != null) {
- throw new Error("");
- }
- if (baseType === "tuple") {
- if (components == null) { throw new Error(""); }
- } else if (components != null) {
- throw new Error("");
- }
- defineProperties<ParamType>(this, {
- name, type, baseType, indexed, components, arrayLength, arrayChildren
- });
- }
- /**
- * Return a string representation of this type.
- *
- * For example,
- *
- * ``sighash" => "(uint256,address)"``
- *
- * ``"minimal" => "tuple(uint256,address) indexed"``
- *
- * ``"full" => "tuple(uint256 foo, address bar) indexed baz"``
- */
- format(format?: FormatType): string {
- if (format == null) { format = "sighash"; }
- if (format === "json") {
- const name = this.name || "";
- if (this.isArray()) {
- const result = JSON.parse(this.arrayChildren.format("json"));
- result.name = name;
- result.type += `[${ (this.arrayLength < 0 ? "": String(this.arrayLength)) }]`;
- return JSON.stringify(result);
- }
- const result: any = {
- type: ((this.baseType === "tuple") ? "tuple": this.type),
- name
- };
- if (typeof(this.indexed) === "boolean") { result.indexed = this.indexed; }
- if (this.isTuple()) {
- result.components = this.components.map((c) => JSON.parse(c.format(format)));
- }
- return JSON.stringify(result);
- }
- let result = "";
- // Array
- if (this.isArray()) {
- result += this.arrayChildren.format(format);
- result += `[${ (this.arrayLength < 0 ? "": String(this.arrayLength)) }]`;
- } else {
- if (this.isTuple()) {
- result += "(" + this.components.map(
- (comp) => comp.format(format)
- ).join((format === "full") ? ", ": ",") + ")";
- } else {
- result += this.type;
- }
- }
- if (format !== "sighash") {
- if (this.indexed === true) { result += " indexed"; }
- if (format === "full" && this.name) {
- result += " " + this.name;
- }
- }
- return result;
- }
- /**
- * Returns true if %%this%% is an Array type.
- *
- * This provides a type gaurd ensuring that [[arrayChildren]]
- * and [[arrayLength]] are non-null.
- */
- isArray(): this is (ParamType & { arrayChildren: ParamType, arrayLength: number }) {
- return (this.baseType === "array")
- }
- /**
- * Returns true if %%this%% is a Tuple type.
- *
- * This provides a type gaurd ensuring that [[components]]
- * is non-null.
- */
- isTuple(): this is (ParamType & { components: ReadonlyArray<ParamType> }) {
- return (this.baseType === "tuple");
- }
- /**
- * Returns true if %%this%% is an Indexable type.
- *
- * This provides a type gaurd ensuring that [[indexed]]
- * is non-null.
- */
- isIndexable(): this is (ParamType & { indexed: boolean }) {
- return (this.indexed != null);
- }
- /**
- * Walks the **ParamType** with %%value%%, calling %%process%%
- * on each type, destructing the %%value%% recursively.
- */
- walk(value: any, process: ParamTypeWalkFunc): any {
- if (this.isArray()) {
- if (!Array.isArray(value)) { throw new Error("invalid array value"); }
- if (this.arrayLength !== -1 && value.length !== this.arrayLength) {
- throw new Error("array is wrong length");
- }
- const _this = this;
- return value.map((v) => (_this.arrayChildren.walk(v, process)));
- }
- if (this.isTuple()) {
- if (!Array.isArray(value)) { throw new Error("invalid tuple value"); }
- if (value.length !== this.components.length) {
- throw new Error("array is wrong length");
- }
- const _this = this;
- return value.map((v, i) => (_this.components[i].walk(v, process)));
- }
- return process(this.type, value);
- }
- #walkAsync(promises: Array<Promise<void>>, value: any, process: ParamTypeWalkAsyncFunc, setValue: (value: any) => void): void {
- if (this.isArray()) {
- if (!Array.isArray(value)) { throw new Error("invalid array value"); }
- if (this.arrayLength !== -1 && value.length !== this.arrayLength) {
- throw new Error("array is wrong length");
- }
- const childType = this.arrayChildren;
- const result = value.slice();
- result.forEach((value, index) => {
- childType.#walkAsync(promises, value, process, (value: any) => {
- result[index] = value;
- });
- });
- setValue(result);
- return;
- }
- if (this.isTuple()) {
- const components = this.components;
- // Convert the object into an array
- let result: Array<any>;
- if (Array.isArray(value)) {
- result = value.slice();
- } else {
- if (value == null || typeof(value) !== "object") {
- throw new Error("invalid tuple value");
- }
- result = components.map((param) => {
- if (!param.name) { throw new Error("cannot use object value with unnamed components"); }
- if (!(param.name in value)) {
- throw new Error(`missing value for component ${ param.name }`);
- }
- return value[param.name];
- });
- }
- if (result.length !== this.components.length) {
- throw new Error("array is wrong length");
- }
- result.forEach((value, index) => {
- components[index].#walkAsync(promises, value, process, (value: any) => {
- result[index] = value;
- });
- });
- setValue(result);
- return;
- }
- const result = process(this.type, value);
- if (result.then) {
- promises.push((async function() { setValue(await result); })());
- } else {
- setValue(result);
- }
- }
- /**
- * Walks the **ParamType** with %%value%%, asynchronously calling
- * %%process%% on each type, destructing the %%value%% recursively.
- *
- * This can be used to resolve ENS names by walking and resolving each
- * ``"address"`` type.
- */
- async walkAsync(value: any, process: ParamTypeWalkAsyncFunc): Promise<any> {
- const promises: Array<Promise<void>> = [ ];
- const result: [ any ] = [ value ];
- this.#walkAsync(promises, value, process, (value: any) => {
- result[0] = value;
- });
- if (promises.length) { await Promise.all(promises); }
- return result[0];
- }
- /**
- * Creates a new **ParamType** for %%obj%%.
- *
- * If %%allowIndexed%% then the ``indexed`` keyword is permitted,
- * otherwise the ``indexed`` keyword will throw an error.
- */
- static from(obj: any, allowIndexed?: boolean): ParamType {
- if (ParamType.isParamType(obj)) { return obj; }
- if (typeof(obj) === "string") {
- try {
- return ParamType.from(lex(obj), allowIndexed);
- } catch (error) {
- assertArgument(false, "invalid param type", "obj", obj);
- }
- } else if (obj instanceof TokenString) {
- let type = "", baseType = "";
- let comps: null | Array<ParamType> = null;
- if (consumeKeywords(obj, setify([ "tuple" ])).has("tuple") || obj.peekType("OPEN_PAREN")) {
- // Tuple
- baseType = "tuple";
- comps = obj.popParams().map((t) => ParamType.from(t));
- type = `tuple(${ comps.map((c) => c.format()).join(",") })`;
- } else {
- // Normal
- type = verifyBasicType(obj.popType("TYPE"));
- baseType = type;
- }
- // Check for Array
- let arrayChildren: null | ParamType = null;
- let arrayLength: null | number = null;
- while (obj.length && obj.peekType("BRACKET")) {
- const bracket = obj.pop(); //arrays[i];
- arrayChildren = new ParamType(_guard, "", type, baseType, null, comps, arrayLength, arrayChildren);
- arrayLength = bracket.value;
- type += bracket.text;
- baseType = "array";
- comps = null;
- }
- let indexed: null | boolean = null;
- const keywords = consumeKeywords(obj, KwModifiers);
- if (keywords.has("indexed")) {
- if (!allowIndexed) { throw new Error(""); }
- indexed = true;
- }
- const name = (obj.peekType("ID") ? obj.pop().text: "");
- if (obj.length) { throw new Error("leftover tokens"); }
- return new ParamType(_guard, name, type, baseType, indexed, comps, arrayLength, arrayChildren);
- }
- const name = obj.name;
- assertArgument(!name || (typeof(name) === "string" && name.match(regexId)),
- "invalid name", "obj.name", name);
- let indexed = obj.indexed;
- if (indexed != null) {
- assertArgument(allowIndexed, "parameter cannot be indexed", "obj.indexed", obj.indexed);
- indexed = !!indexed;
- }
- let type = obj.type;
- let arrayMatch = type.match(regexArrayType);
- if (arrayMatch) {
- const arrayLength = parseInt(arrayMatch[2] || "-1");
- const arrayChildren = ParamType.from({
- type: arrayMatch[1],
- components: obj.components
- });
- return new ParamType(_guard, name || "", type, "array", indexed, null, arrayLength, arrayChildren);
- }
- if (type === "tuple" || type.startsWith("tuple("/* fix: ) */) || type.startsWith("(" /* fix: ) */)) {
- const comps = (obj.components != null) ? obj.components.map((c: any) => ParamType.from(c)): null;
- const tuple = new ParamType(_guard, name || "", type, "tuple", indexed, comps, null, null);
- // @TODO: use lexer to validate and normalize type
- return tuple;
- }
- type = verifyBasicType(obj.type);
- return new ParamType(_guard, name || "", type, type, indexed, null, null, null);
- }
- /**
- * Returns true if %%value%% is a **ParamType**.
- */
- static isParamType(value: any): value is ParamType {
- return (value && value[internal] === ParamTypeInternal);
- }
- }
- /**
- * The type of a [[Fragment]].
- */
- export type FragmentType = "constructor" | "error" | "event" | "fallback" | "function" | "struct";
- /**
- * An abstract class to represent An individual fragment from a parse ABI.
- */
- export abstract class Fragment {
- /**
- * The type of the fragment.
- */
- readonly type!: FragmentType;
- /**
- * The inputs for the fragment.
- */
- readonly inputs!: ReadonlyArray<ParamType>;
- /**
- * @private
- */
- constructor(guard: any, type: FragmentType, inputs: ReadonlyArray<ParamType>) {
- assertPrivate(guard, _guard, "Fragment");
- inputs = Object.freeze(inputs.slice());
- defineProperties<Fragment>(this, { type, inputs });
- }
- /**
- * Returns a string representation of this fragment as %%format%%.
- */
- abstract format(format?: FormatType): string;
- /**
- * Creates a new **Fragment** for %%obj%%, wich can be any supported
- * ABI frgament type.
- */
- static from(obj: any): Fragment {
- if (typeof(obj) === "string") {
- // Try parsing JSON...
- try {
- Fragment.from(JSON.parse(obj));
- } catch (e) { }
- // ...otherwise, use the human-readable lexer
- return Fragment.from(lex(obj));
- }
- if (obj instanceof TokenString) {
- // Human-readable ABI (already lexed)
- const type = obj.peekKeyword(KwTypes);
- switch (type) {
- case "constructor": return ConstructorFragment.from(obj);
- case "error": return ErrorFragment.from(obj);
- case "event": return EventFragment.from(obj);
- case "fallback": case "receive":
- return FallbackFragment.from(obj);
- case "function": return FunctionFragment.from(obj);
- case "struct": return StructFragment.from(obj);
- }
- } else if (typeof(obj) === "object") {
- // JSON ABI
- switch (obj.type) {
- case "constructor": return ConstructorFragment.from(obj);
- case "error": return ErrorFragment.from(obj);
- case "event": return EventFragment.from(obj);
- case "fallback": case "receive":
- return FallbackFragment.from(obj);
- case "function": return FunctionFragment.from(obj);
- case "struct": return StructFragment.from(obj);
- }
- assert(false, `unsupported type: ${ obj.type }`, "UNSUPPORTED_OPERATION", {
- operation: "Fragment.from"
- });
- }
- assertArgument(false, "unsupported frgament object", "obj", obj);
- }
- /**
- * Returns true if %%value%% is a [[ConstructorFragment]].
- */
- static isConstructor(value: any): value is ConstructorFragment {
- return ConstructorFragment.isFragment(value);
- }
- /**
- * Returns true if %%value%% is an [[ErrorFragment]].
- */
- static isError(value: any): value is ErrorFragment {
- return ErrorFragment.isFragment(value);
- }
- /**
- * Returns true if %%value%% is an [[EventFragment]].
- */
- static isEvent(value: any): value is EventFragment {
- return EventFragment.isFragment(value);
- }
- /**
- * Returns true if %%value%% is a [[FunctionFragment]].
- */
- static isFunction(value: any): value is FunctionFragment {
- return FunctionFragment.isFragment(value);
- }
- /**
- * Returns true if %%value%% is a [[StructFragment]].
- */
- static isStruct(value: any): value is StructFragment {
- return StructFragment.isFragment(value);
- }
- }
- /**
- * An abstract class to represent An individual fragment
- * which has a name from a parse ABI.
- */
- export abstract class NamedFragment extends Fragment {
- /**
- * The name of the fragment.
- */
- readonly name!: string;
- /**
- * @private
- */
- constructor(guard: any, type: FragmentType, name: string, inputs: ReadonlyArray<ParamType>) {
- super(guard, type, inputs);
- assertArgument(typeof(name) === "string" && name.match(regexId),
- "invalid identifier", "name", name);
- inputs = Object.freeze(inputs.slice());
- defineProperties<NamedFragment>(this, { name });
- }
- }
- function joinParams(format: FormatType, params: ReadonlyArray<ParamType>): string {
- return "(" + params.map((p) => p.format(format)).join((format === "full") ? ", ": ",") + ")";
- }
- /**
- * A Fragment which represents a //Custom Error//.
- */
- export class ErrorFragment extends NamedFragment {
- /**
- * @private
- */
- constructor(guard: any, name: string, inputs: ReadonlyArray<ParamType>) {
- super(guard, "error", name, inputs);
- Object.defineProperty(this, internal, { value: ErrorFragmentInternal });
- }
- /**
- * The Custom Error selector.
- */
- get selector(): string {
- return id(this.format("sighash")).substring(0, 10);
- }
- /**
- * Returns a string representation of this fragment as %%format%%.
- */
- format(format?: FormatType): string {
- if (format == null) { format = "sighash"; }
- if (format === "json") {
- return JSON.stringify({
- type: "error",
- name: this.name,
- inputs: this.inputs.map((input) => JSON.parse(input.format(format))),
- });
- }
- const result: Array<string> = [ ];
- if (format !== "sighash") { result.push("error"); }
- result.push(this.name + joinParams(format, this.inputs));
- return result.join(" ");
- }
- /**
- * Returns a new **ErrorFragment** for %%obj%%.
- */
- static from(obj: any): ErrorFragment {
- if (ErrorFragment.isFragment(obj)) { return obj; }
- if (typeof(obj) === "string") {
- return ErrorFragment.from(lex(obj));
- } else if (obj instanceof TokenString) {
- const name = consumeName("error", obj);
- const inputs = consumeParams(obj);
- consumeEoi(obj);
- return new ErrorFragment(_guard, name, inputs);
- }
- return new ErrorFragment(_guard, obj.name,
- obj.inputs ? obj.inputs.map(ParamType.from): [ ]);
- }
- /**
- * Returns ``true`` and provides a type guard if %%value%% is an
- * **ErrorFragment**.
- */
- static isFragment(value: any): value is ErrorFragment {
- return (value && value[internal] === ErrorFragmentInternal);
- }
- }
- /**
- * A Fragment which represents an Event.
- */
- export class EventFragment extends NamedFragment {
- /**
- * Whether this event is anonymous.
- */
- readonly anonymous!: boolean;
- /**
- * @private
- */
- constructor(guard: any, name: string, inputs: ReadonlyArray<ParamType>, anonymous: boolean) {
- super(guard, "event", name, inputs);
- Object.defineProperty(this, internal, { value: EventFragmentInternal });
- defineProperties<EventFragment>(this, { anonymous });
- }
- /**
- * The Event topic hash.
- */
- get topicHash(): string {
- return id(this.format("sighash"));
- }
- /**
- * Returns a string representation of this event as %%format%%.
- */
- format(format?: FormatType): string {
- if (format == null) { format = "sighash"; }
- if (format === "json") {
- return JSON.stringify({
- type: "event",
- anonymous: this.anonymous,
- name: this.name,
- inputs: this.inputs.map((i) => JSON.parse(i.format(format)))
- });
- }
- const result: Array<string> = [ ];
- if (format !== "sighash") { result.push("event"); }
- result.push(this.name + joinParams(format, this.inputs));
- if (format !== "sighash" && this.anonymous) { result.push("anonymous"); }
- return result.join(" ");
- }
- /**
- * Return the topic hash for an event with %%name%% and %%params%%.
- */
- static getTopicHash(name: string, params?: Array<any>): string {
- params = (params || []).map((p) => ParamType.from(p));
- const fragment = new EventFragment(_guard, name, params, false);
- return fragment.topicHash;
- }
- /**
- * Returns a new **EventFragment** for %%obj%%.
- */
- static from(obj: any): EventFragment {
- if (EventFragment.isFragment(obj)) { return obj; }
- if (typeof(obj) === "string") {
- try {
- return EventFragment.from(lex(obj));
- } catch (error) {
- assertArgument(false, "invalid event fragment", "obj", obj);
- }
- } else if (obj instanceof TokenString) {
- const name = consumeName("event", obj);
- const inputs = consumeParams(obj, true);
- const anonymous = !!consumeKeywords(obj, setify([ "anonymous" ])).has("anonymous");
- consumeEoi(obj);
- return new EventFragment(_guard, name, inputs, anonymous);
- }
- return new EventFragment(_guard, obj.name,
- obj.inputs ? obj.inputs.map((p: any) => ParamType.from(p, true)): [ ], !!obj.anonymous);
- }
- /**
- * Returns ``true`` and provides a type guard if %%value%% is an
- * **EventFragment**.
- */
- static isFragment(value: any): value is EventFragment {
- return (value && value[internal] === EventFragmentInternal);
- }
- }
- /**
- * A Fragment which represents a constructor.
- */
- export class ConstructorFragment extends Fragment {
- /**
- * Whether the constructor can receive an endowment.
- */
- readonly payable!: boolean;
- /**
- * The recommended gas limit for deployment or ``null``.
- */
- readonly gas!: null | bigint;
- /**
- * @private
- */
- constructor(guard: any, type: FragmentType, inputs: ReadonlyArray<ParamType>, payable: boolean, gas: null | bigint) {
- super(guard, type, inputs);
- Object.defineProperty(this, internal, { value: ConstructorFragmentInternal });
- defineProperties<ConstructorFragment>(this, { payable, gas });
- }
- /**
- * Returns a string representation of this constructor as %%format%%.
- */
- format(format?: FormatType): string {
- assert(format != null && format !== "sighash", "cannot format a constructor for sighash",
- "UNSUPPORTED_OPERATION", { operation: "format(sighash)" });
- if (format === "json") {
- return JSON.stringify({
- type: "constructor",
- stateMutability: (this.payable ? "payable": "undefined"),
- payable: this.payable,
- gas: ((this.gas != null) ? this.gas: undefined),
- inputs: this.inputs.map((i) => JSON.parse(i.format(format)))
- });
- }
- const result = [ `constructor${ joinParams(format, this.inputs) }` ];
- if (this.payable) { result.push("payable"); }
- if (this.gas != null) { result.push(`@${ this.gas.toString() }`); }
- return result.join(" ");
- }
- /**
- * Returns a new **ConstructorFragment** for %%obj%%.
- */
- static from(obj: any): ConstructorFragment {
- if (ConstructorFragment.isFragment(obj)) { return obj; }
- if (typeof(obj) === "string") {
- try {
- return ConstructorFragment.from(lex(obj));
- } catch (error) {
- assertArgument(false, "invalid constuctor fragment", "obj", obj);
- }
- } else if (obj instanceof TokenString) {
- consumeKeywords(obj, setify([ "constructor" ]));
- const inputs = consumeParams(obj);
- const payable = !!consumeKeywords(obj, KwVisibDeploy).has("payable");
- const gas = consumeGas(obj);
- consumeEoi(obj);
- return new ConstructorFragment(_guard, "constructor", inputs, payable, gas);
- }
- return new ConstructorFragment(_guard, "constructor",
- obj.inputs ? obj.inputs.map(ParamType.from): [ ],
- !!obj.payable, (obj.gas != null) ? obj.gas: null);
- }
- /**
- * Returns ``true`` and provides a type guard if %%value%% is a
- * **ConstructorFragment**.
- */
- static isFragment(value: any): value is ConstructorFragment {
- return (value && value[internal] === ConstructorFragmentInternal);
- }
- }
- /**
- * A Fragment which represents a method.
- */
- export class FallbackFragment extends Fragment {
- /**
- * If the function can be sent value during invocation.
- */
- readonly payable!: boolean;
- constructor(guard: any, inputs: ReadonlyArray<ParamType>, payable: boolean) {
- super(guard, "fallback", inputs);
- Object.defineProperty(this, internal, { value: FallbackFragmentInternal });
- defineProperties<FallbackFragment>(this, { payable });
- }
- /**
- * Returns a string representation of this fallback as %%format%%.
- */
- format(format?: FormatType): string {
- const type = ((this.inputs.length === 0) ? "receive": "fallback");
- if (format === "json") {
- const stateMutability = (this.payable ? "payable": "nonpayable");
- return JSON.stringify({ type, stateMutability });
- }
- return `${ type }()${ this.payable ? " payable": "" }`;
- }
- /**
- * Returns a new **FallbackFragment** for %%obj%%.
- */
- static from(obj: any): FallbackFragment {
- if (FallbackFragment.isFragment(obj)) { return obj; }
- if (typeof(obj) === "string") {
- try {
- return FallbackFragment.from(lex(obj));
- } catch (error) {
- assertArgument(false, "invalid fallback fragment", "obj", obj);
- }
- } else if (obj instanceof TokenString) {
- const errorObj = obj.toString();
- const topIsValid = obj.peekKeyword(setify([ "fallback", "receive" ]));
- assertArgument(topIsValid, "type must be fallback or receive", "obj", errorObj);
- const type = obj.popKeyword(setify([ "fallback", "receive" ]));
- // receive()
- if (type === "receive") {
- const inputs = consumeParams(obj);
- assertArgument(inputs.length === 0, `receive cannot have arguments`, "obj.inputs", inputs);
- consumeKeywords(obj, setify([ "payable" ]));
- consumeEoi(obj);
- return new FallbackFragment(_guard, [ ], true);
- }
- // fallback() [payable]
- // fallback(bytes) [payable] returns (bytes)
- let inputs = consumeParams(obj);
- if (inputs.length) {
- assertArgument(inputs.length === 1 && inputs[0].type === "bytes",
- "invalid fallback inputs", "obj.inputs",
- inputs.map((i) => i.format("minimal")).join(", "));
- } else {
- inputs = [ ParamType.from("bytes") ];
- }
- const mutability = consumeMutability(obj);
- assertArgument(mutability === "nonpayable" || mutability === "payable", "fallback cannot be constants", "obj.stateMutability", mutability);
- if (consumeKeywords(obj, setify([ "returns" ])).has("returns")) {
- const outputs = consumeParams(obj);
- assertArgument(outputs.length === 1 && outputs[0].type === "bytes",
- "invalid fallback outputs", "obj.outputs",
- outputs.map((i) => i.format("minimal")).join(", "));
- }
- consumeEoi(obj);
- return new FallbackFragment(_guard, inputs, mutability === "payable");
- }
- if (obj.type === "receive") {
- return new FallbackFragment(_guard, [ ], true);
- }
- if (obj.type === "fallback") {
- const inputs = [ ParamType.from("bytes") ];
- const payable = (obj.stateMutability === "payable");
- return new FallbackFragment(_guard, inputs, payable);
- }
- assertArgument(false, "invalid fallback description", "obj", obj);
- }
- /**
- * Returns ``true`` and provides a type guard if %%value%% is a
- * **FallbackFragment**.
- */
- static isFragment(value: any): value is FallbackFragment {
- return (value && value[internal] === FallbackFragmentInternal);
- }
- }
- /**
- * A Fragment which represents a method.
- */
- export class FunctionFragment extends NamedFragment {
- /**
- * If the function is constant (e.g. ``pure`` or ``view`` functions).
- */
- readonly constant!: boolean;
- /**
- * The returned types for the result of calling this function.
- */
- readonly outputs!: ReadonlyArray<ParamType>;
- /**
- * The state mutability (e.g. ``payable``, ``nonpayable``, ``view``
- * or ``pure``)
- */
- readonly stateMutability!: "payable" | "nonpayable" | "view" | "pure";
- /**
- * If the function can be sent value during invocation.
- */
- readonly payable!: boolean;
- /**
- * The recommended gas limit to send when calling this function.
- */
- readonly gas!: null | bigint;
- /**
- * @private
- */
- constructor(guard: any, name: string, stateMutability: "payable" | "nonpayable" | "view" | "pure", inputs: ReadonlyArray<ParamType>, outputs: ReadonlyArray<ParamType>, gas: null | bigint) {
- super(guard, "function", name, inputs);
- Object.defineProperty(this, internal, { value: FunctionFragmentInternal });
- outputs = Object.freeze(outputs.slice());
- const constant = (stateMutability === "view" || stateMutability === "pure");
- const payable = (stateMutability === "payable");
- defineProperties<FunctionFragment>(this, { constant, gas, outputs, payable, stateMutability });
- }
- /**
- * The Function selector.
- */
- get selector(): string {
- return id(this.format("sighash")).substring(0, 10);
- }
- /**
- * Returns a string representation of this function as %%format%%.
- */
- format(format?: FormatType): string {
- if (format == null) { format = "sighash"; }
- if (format === "json") {
- return JSON.stringify({
- type: "function",
- name: this.name,
- constant: this.constant,
- stateMutability: ((this.stateMutability !== "nonpayable") ? this.stateMutability: undefined),
- payable: this.payable,
- gas: ((this.gas != null) ? this.gas: undefined),
- inputs: this.inputs.map((i) => JSON.parse(i.format(format))),
- outputs: this.outputs.map((o) => JSON.parse(o.format(format))),
- });
- }
- const result: Array<string> = [];
- if (format !== "sighash") { result.push("function"); }
- result.push(this.name + joinParams(format, this.inputs));
- if (format !== "sighash") {
- if (this.stateMutability !== "nonpayable") {
- result.push(this.stateMutability);
- }
- if (this.outputs && this.outputs.length) {
- result.push("returns");
- result.push(joinParams(format, this.outputs));
- }
- if (this.gas != null) { result.push(`@${ this.gas.toString() }`); }
- }
- return result.join(" ");
- }
- /**
- * Return the selector for a function with %%name%% and %%params%%.
- */
- static getSelector(name: string, params?: Array<any>): string {
- params = (params || []).map((p) => ParamType.from(p));
- const fragment = new FunctionFragment(_guard, name, "view", params, [ ], null);
- return fragment.selector;
- }
- /**
- * Returns a new **FunctionFragment** for %%obj%%.
- */
- static from(obj: any): FunctionFragment {
- if (FunctionFragment.isFragment(obj)) { return obj; }
- if (typeof(obj) === "string") {
- try {
- return FunctionFragment.from(lex(obj));
- } catch (error) {
- assertArgument(false, "invalid function fragment", "obj", obj);
- }
- } else if (obj instanceof TokenString) {
- const name = consumeName("function", obj);
- const inputs = consumeParams(obj);
- const mutability = consumeMutability(obj);
- let outputs: Array<ParamType> = [ ];
- if (consumeKeywords(obj, setify([ "returns" ])).has("returns")) {
- outputs = consumeParams(obj);
- }
- const gas = consumeGas(obj);
- consumeEoi(obj);
- return new FunctionFragment(_guard, name, mutability, inputs, outputs, gas);
- }
- let stateMutability = obj.stateMutability;
- // Use legacy Solidity ABI logic if stateMutability is missing
- if (stateMutability == null) {
- stateMutability = "payable";
- if (typeof(obj.constant) === "boolean") {
- stateMutability = "view";
- if (!obj.constant) {
- stateMutability = "payable"
- if (typeof(obj.payable) === "boolean" && !obj.payable) {
- stateMutability = "nonpayable";
- }
- }
- } else if (typeof(obj.payable) === "boolean" && !obj.payable) {
- stateMutability = "nonpayable";
- }
- }
- // @TODO: verifyState for stateMutability (e.g. throw if
- // payable: false but stateMutability is "nonpayable")
- 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);
- }
- /**
- * Returns ``true`` and provides a type guard if %%value%% is a
- * **FunctionFragment**.
- */
- static isFragment(value: any): value is FunctionFragment {
- return (value && value[internal] === FunctionFragmentInternal);
- }
- }
- /**
- * A Fragment which represents a structure.
- */
- export class StructFragment extends NamedFragment {
- /**
- * @private
- */
- constructor(guard: any, name: string, inputs: ReadonlyArray<ParamType>) {
- super(guard, "struct", name, inputs);
- Object.defineProperty(this, internal, { value: StructFragmentInternal });
- }
- /**
- * Returns a string representation of this struct as %%format%%.
- */
- format(): string {
- throw new Error("@TODO");
- }
- /**
- * Returns a new **StructFragment** for %%obj%%.
- */
- static from(obj: any): StructFragment {
- if (typeof(obj) === "string") {
- try {
- return StructFragment.from(lex(obj));
- } catch (error) {
- assertArgument(false, "invalid struct fragment", "obj", obj);
- }
- } else if (obj instanceof TokenString) {
- const name = consumeName("struct", obj);
- const inputs = consumeParams(obj);
- consumeEoi(obj);
- return new StructFragment(_guard, name, inputs);
- }
- return new StructFragment(_guard, obj.name, obj.inputs ? obj.inputs.map(ParamType.from): [ ]);
- }
- // @TODO: fix this return type
- /**
- * Returns ``true`` and provides a type guard if %%value%% is a
- * **StructFragment**.
- */
- static isFragment(value: any): value is FunctionFragment {
- return (value && value[internal] === StructFragmentInternal);
- }
- }
|