"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.Interface = exports.Indexed = exports.ErrorDescription = exports.TransactionDescription = exports.LogDescription = exports.checkResultErrors = void 0; var address_1 = require("@ethersproject/address"); var bignumber_1 = require("@ethersproject/bignumber"); var bytes_1 = require("@ethersproject/bytes"); var hash_1 = require("@ethersproject/hash"); var keccak256_1 = require("@ethersproject/keccak256"); var properties_1 = require("@ethersproject/properties"); var abi_coder_1 = require("./abi-coder"); var abstract_coder_1 = require("./coders/abstract-coder"); Object.defineProperty(exports, "checkResultErrors", { enumerable: true, get: function () { return abstract_coder_1.checkResultErrors; } }); var fragments_1 = require("./fragments"); var logger_1 = require("@ethersproject/logger"); var _version_1 = require("./_version"); var logger = new logger_1.Logger(_version_1.version); var LogDescription = /** @class */ (function (_super) { __extends(LogDescription, _super); function LogDescription() { return _super !== null && _super.apply(this, arguments) || this; } return LogDescription; }(properties_1.Description)); exports.LogDescription = LogDescription; var TransactionDescription = /** @class */ (function (_super) { __extends(TransactionDescription, _super); function TransactionDescription() { return _super !== null && _super.apply(this, arguments) || this; } return TransactionDescription; }(properties_1.Description)); exports.TransactionDescription = TransactionDescription; var ErrorDescription = /** @class */ (function (_super) { __extends(ErrorDescription, _super); function ErrorDescription() { return _super !== null && _super.apply(this, arguments) || this; } return ErrorDescription; }(properties_1.Description)); exports.ErrorDescription = ErrorDescription; var Indexed = /** @class */ (function (_super) { __extends(Indexed, _super); function Indexed() { return _super !== null && _super.apply(this, arguments) || this; } Indexed.isIndexed = function (value) { return !!(value && value._isIndexed); }; return Indexed; }(properties_1.Description)); exports.Indexed = Indexed; var BuiltinErrors = { "0x08c379a0": { signature: "Error(string)", name: "Error", inputs: ["string"], reason: true }, "0x4e487b71": { signature: "Panic(uint256)", name: "Panic", inputs: ["uint256"] } }; function wrapAccessError(property, error) { var wrap = new Error("deferred error during ABI decoding triggered accessing " + property); wrap.error = error; return wrap; } /* function checkNames(fragment: Fragment, type: "input" | "output", params: Array): void { params.reduce((accum, param) => { if (param.name) { if (accum[param.name]) { logger.throwArgumentError(`duplicate ${ type } parameter ${ JSON.stringify(param.name) } in ${ fragment.format("full") }`, "fragment", fragment); } accum[param.name] = true; } return accum; }, <{ [ name: string ]: boolean }>{ }); } */ var Interface = /** @class */ (function () { function Interface(fragments) { var _newTarget = this.constructor; var _this = this; logger.checkNew(_newTarget, Interface); var abi = []; if (typeof (fragments) === "string") { abi = JSON.parse(fragments); } else { abi = fragments; } properties_1.defineReadOnly(this, "fragments", abi.map(function (fragment) { return fragments_1.Fragment.from(fragment); }).filter(function (fragment) { return (fragment != null); })); properties_1.defineReadOnly(this, "_abiCoder", properties_1.getStatic((_newTarget), "getAbiCoder")()); properties_1.defineReadOnly(this, "functions", {}); properties_1.defineReadOnly(this, "errors", {}); properties_1.defineReadOnly(this, "events", {}); properties_1.defineReadOnly(this, "structs", {}); // Add all fragments by their signature this.fragments.forEach(function (fragment) { var bucket = null; switch (fragment.type) { case "constructor": if (_this.deploy) { logger.warn("duplicate definition - constructor"); return; } //checkNames(fragment, "input", fragment.inputs); properties_1.defineReadOnly(_this, "deploy", fragment); return; case "function": //checkNames(fragment, "input", fragment.inputs); //checkNames(fragment, "output", (fragment).outputs); bucket = _this.functions; break; case "event": //checkNames(fragment, "input", fragment.inputs); bucket = _this.events; break; case "error": bucket = _this.errors; break; default: return; } var signature = fragment.format(); if (bucket[signature]) { logger.warn("duplicate definition - " + signature); return; } bucket[signature] = fragment; }); // If we do not have a constructor add a default if (!this.deploy) { properties_1.defineReadOnly(this, "deploy", fragments_1.ConstructorFragment.from({ payable: false, type: "constructor" })); } properties_1.defineReadOnly(this, "_isInterface", true); } Interface.prototype.format = function (format) { if (!format) { format = fragments_1.FormatTypes.full; } if (format === fragments_1.FormatTypes.sighash) { logger.throwArgumentError("interface does not support formatting sighash", "format", format); } var abi = this.fragments.map(function (fragment) { return fragment.format(format); }); // We need to re-bundle the JSON fragments a bit if (format === fragments_1.FormatTypes.json) { return JSON.stringify(abi.map(function (j) { return JSON.parse(j); })); } return abi; }; // Sub-classes can override these to handle other blockchains Interface.getAbiCoder = function () { return abi_coder_1.defaultAbiCoder; }; Interface.getAddress = function (address) { return address_1.getAddress(address); }; Interface.getSighash = function (fragment) { return bytes_1.hexDataSlice(hash_1.id(fragment.format()), 0, 4); }; Interface.getEventTopic = function (eventFragment) { return hash_1.id(eventFragment.format()); }; // Find a function definition by any means necessary (unless it is ambiguous) Interface.prototype.getFunction = function (nameOrSignatureOrSighash) { if (bytes_1.isHexString(nameOrSignatureOrSighash)) { for (var name_1 in this.functions) { if (nameOrSignatureOrSighash === this.getSighash(name_1)) { return this.functions[name_1]; } } logger.throwArgumentError("no matching function", "sighash", nameOrSignatureOrSighash); } // It is a bare name, look up the function (will return null if ambiguous) if (nameOrSignatureOrSighash.indexOf("(") === -1) { var name_2 = nameOrSignatureOrSighash.trim(); var matching = Object.keys(this.functions).filter(function (f) { return (f.split("(" /* fix:) */)[0] === name_2); }); if (matching.length === 0) { logger.throwArgumentError("no matching function", "name", name_2); } else if (matching.length > 1) { logger.throwArgumentError("multiple matching functions", "name", name_2); } return this.functions[matching[0]]; } // Normlize the signature and lookup the function var result = this.functions[fragments_1.FunctionFragment.fromString(nameOrSignatureOrSighash).format()]; if (!result) { logger.throwArgumentError("no matching function", "signature", nameOrSignatureOrSighash); } return result; }; // Find an event definition by any means necessary (unless it is ambiguous) Interface.prototype.getEvent = function (nameOrSignatureOrTopic) { if (bytes_1.isHexString(nameOrSignatureOrTopic)) { var topichash = nameOrSignatureOrTopic.toLowerCase(); for (var name_3 in this.events) { if (topichash === this.getEventTopic(name_3)) { return this.events[name_3]; } } logger.throwArgumentError("no matching event", "topichash", topichash); } // It is a bare name, look up the function (will return null if ambiguous) if (nameOrSignatureOrTopic.indexOf("(") === -1) { var name_4 = nameOrSignatureOrTopic.trim(); var matching = Object.keys(this.events).filter(function (f) { return (f.split("(" /* fix:) */)[0] === name_4); }); if (matching.length === 0) { logger.throwArgumentError("no matching event", "name", name_4); } else if (matching.length > 1) { logger.throwArgumentError("multiple matching events", "name", name_4); } return this.events[matching[0]]; } // Normlize the signature and lookup the function var result = this.events[fragments_1.EventFragment.fromString(nameOrSignatureOrTopic).format()]; if (!result) { logger.throwArgumentError("no matching event", "signature", nameOrSignatureOrTopic); } return result; }; // Find a function definition by any means necessary (unless it is ambiguous) Interface.prototype.getError = function (nameOrSignatureOrSighash) { if (bytes_1.isHexString(nameOrSignatureOrSighash)) { var getSighash = properties_1.getStatic(this.constructor, "getSighash"); for (var name_5 in this.errors) { var error = this.errors[name_5]; if (nameOrSignatureOrSighash === getSighash(error)) { return this.errors[name_5]; } } logger.throwArgumentError("no matching error", "sighash", nameOrSignatureOrSighash); } // It is a bare name, look up the function (will return null if ambiguous) if (nameOrSignatureOrSighash.indexOf("(") === -1) { var name_6 = nameOrSignatureOrSighash.trim(); var matching = Object.keys(this.errors).filter(function (f) { return (f.split("(" /* fix:) */)[0] === name_6); }); if (matching.length === 0) { logger.throwArgumentError("no matching error", "name", name_6); } else if (matching.length > 1) { logger.throwArgumentError("multiple matching errors", "name", name_6); } return this.errors[matching[0]]; } // Normlize the signature and lookup the function var result = this.errors[fragments_1.FunctionFragment.fromString(nameOrSignatureOrSighash).format()]; if (!result) { logger.throwArgumentError("no matching error", "signature", nameOrSignatureOrSighash); } return result; }; // Get the sighash (the bytes4 selector) used by Solidity to identify a function Interface.prototype.getSighash = function (fragment) { if (typeof (fragment) === "string") { try { fragment = this.getFunction(fragment); } catch (error) { try { fragment = this.getError(fragment); } catch (_) { throw error; } } } return properties_1.getStatic(this.constructor, "getSighash")(fragment); }; // Get the topic (the bytes32 hash) used by Solidity to identify an event Interface.prototype.getEventTopic = function (eventFragment) { if (typeof (eventFragment) === "string") { eventFragment = this.getEvent(eventFragment); } return properties_1.getStatic(this.constructor, "getEventTopic")(eventFragment); }; Interface.prototype._decodeParams = function (params, data) { return this._abiCoder.decode(params, data); }; Interface.prototype._encodeParams = function (params, values) { return this._abiCoder.encode(params, values); }; Interface.prototype.encodeDeploy = function (values) { return this._encodeParams(this.deploy.inputs, values || []); }; Interface.prototype.decodeErrorResult = function (fragment, data) { if (typeof (fragment) === "string") { fragment = this.getError(fragment); } var bytes = bytes_1.arrayify(data); if (bytes_1.hexlify(bytes.slice(0, 4)) !== this.getSighash(fragment)) { logger.throwArgumentError("data signature does not match error " + fragment.name + ".", "data", bytes_1.hexlify(bytes)); } return this._decodeParams(fragment.inputs, bytes.slice(4)); }; Interface.prototype.encodeErrorResult = function (fragment, values) { if (typeof (fragment) === "string") { fragment = this.getError(fragment); } return bytes_1.hexlify(bytes_1.concat([ this.getSighash(fragment), this._encodeParams(fragment.inputs, values || []) ])); }; // Decode the data for a function call (e.g. tx.data) Interface.prototype.decodeFunctionData = function (functionFragment, data) { if (typeof (functionFragment) === "string") { functionFragment = this.getFunction(functionFragment); } var bytes = bytes_1.arrayify(data); if (bytes_1.hexlify(bytes.slice(0, 4)) !== this.getSighash(functionFragment)) { logger.throwArgumentError("data signature does not match function " + functionFragment.name + ".", "data", bytes_1.hexlify(bytes)); } return this._decodeParams(functionFragment.inputs, bytes.slice(4)); }; // Encode the data for a function call (e.g. tx.data) Interface.prototype.encodeFunctionData = function (functionFragment, values) { if (typeof (functionFragment) === "string") { functionFragment = this.getFunction(functionFragment); } return bytes_1.hexlify(bytes_1.concat([ this.getSighash(functionFragment), this._encodeParams(functionFragment.inputs, values || []) ])); }; // Decode the result from a function call (e.g. from eth_call) Interface.prototype.decodeFunctionResult = function (functionFragment, data) { if (typeof (functionFragment) === "string") { functionFragment = this.getFunction(functionFragment); } var bytes = bytes_1.arrayify(data); var reason = null; var errorArgs = null; var errorName = null; var errorSignature = null; switch (bytes.length % this._abiCoder._getWordSize()) { case 0: try { return this._abiCoder.decode(functionFragment.outputs, bytes); } catch (error) { } break; case 4: { var selector = bytes_1.hexlify(bytes.slice(0, 4)); var builtin = BuiltinErrors[selector]; if (builtin) { errorArgs = this._abiCoder.decode(builtin.inputs, bytes.slice(4)); errorName = builtin.name; errorSignature = builtin.signature; if (builtin.reason) { reason = errorArgs[0]; } } else { try { var error = this.getError(selector); errorArgs = this._abiCoder.decode(error.inputs, bytes.slice(4)); errorName = error.name; errorSignature = error.format(); } catch (error) { console.log(error); } } break; } } return logger.throwError("call revert exception", logger_1.Logger.errors.CALL_EXCEPTION, { method: functionFragment.format(), errorArgs: errorArgs, errorName: errorName, errorSignature: errorSignature, reason: reason }); }; // Encode the result for a function call (e.g. for eth_call) Interface.prototype.encodeFunctionResult = function (functionFragment, values) { if (typeof (functionFragment) === "string") { functionFragment = this.getFunction(functionFragment); } return bytes_1.hexlify(this._abiCoder.encode(functionFragment.outputs, values || [])); }; // Create the filter for the event with search criteria (e.g. for eth_filterLog) Interface.prototype.encodeFilterTopics = function (eventFragment, values) { var _this = this; if (typeof (eventFragment) === "string") { eventFragment = this.getEvent(eventFragment); } if (values.length > eventFragment.inputs.length) { logger.throwError("too many arguments for " + eventFragment.format(), logger_1.Logger.errors.UNEXPECTED_ARGUMENT, { argument: "values", value: values }); } var topics = []; if (!eventFragment.anonymous) { topics.push(this.getEventTopic(eventFragment)); } var encodeTopic = function (param, value) { if (param.type === "string") { return hash_1.id(value); } else if (param.type === "bytes") { return keccak256_1.keccak256(bytes_1.hexlify(value)); } // Check addresses are valid if (param.type === "address") { _this._abiCoder.encode(["address"], [value]); } return bytes_1.hexZeroPad(bytes_1.hexlify(value), 32); }; values.forEach(function (value, index) { var param = eventFragment.inputs[index]; if (!param.indexed) { if (value != null) { logger.throwArgumentError("cannot filter non-indexed parameters; must be null", ("contract." + param.name), value); } return; } if (value == null) { topics.push(null); } else if (param.baseType === "array" || param.baseType === "tuple") { logger.throwArgumentError("filtering with tuples or arrays not supported", ("contract." + param.name), value); } else if (Array.isArray(value)) { topics.push(value.map(function (value) { return encodeTopic(param, value); })); } else { topics.push(encodeTopic(param, value)); } }); // Trim off trailing nulls while (topics.length && topics[topics.length - 1] === null) { topics.pop(); } return topics; }; Interface.prototype.encodeEventLog = function (eventFragment, values) { var _this = this; if (typeof (eventFragment) === "string") { eventFragment = this.getEvent(eventFragment); } var topics = []; var dataTypes = []; var dataValues = []; if (!eventFragment.anonymous) { topics.push(this.getEventTopic(eventFragment)); } if (values.length !== eventFragment.inputs.length) { logger.throwArgumentError("event arguments/values mismatch", "values", values); } eventFragment.inputs.forEach(function (param, index) { var value = values[index]; if (param.indexed) { if (param.type === "string") { topics.push(hash_1.id(value)); } else if (param.type === "bytes") { topics.push(keccak256_1.keccak256(value)); } else if (param.baseType === "tuple" || param.baseType === "array") { // @TOOD throw new Error("not implemented"); } else { topics.push(_this._abiCoder.encode([param.type], [value])); } } else { dataTypes.push(param); dataValues.push(value); } }); return { data: this._abiCoder.encode(dataTypes, dataValues), topics: topics }; }; // Decode a filter for the event and the search criteria Interface.prototype.decodeEventLog = function (eventFragment, data, topics) { if (typeof (eventFragment) === "string") { eventFragment = this.getEvent(eventFragment); } if (topics != null && !eventFragment.anonymous) { var topicHash = this.getEventTopic(eventFragment); if (!bytes_1.isHexString(topics[0], 32) || topics[0].toLowerCase() !== topicHash) { logger.throwError("fragment/topic mismatch", logger_1.Logger.errors.INVALID_ARGUMENT, { argument: "topics[0]", expected: topicHash, value: topics[0] }); } topics = topics.slice(1); } var indexed = []; var nonIndexed = []; var dynamic = []; eventFragment.inputs.forEach(function (param, index) { if (param.indexed) { if (param.type === "string" || param.type === "bytes" || param.baseType === "tuple" || param.baseType === "array") { indexed.push(fragments_1.ParamType.fromObject({ type: "bytes32", name: param.name })); dynamic.push(true); } else { indexed.push(param); dynamic.push(false); } } else { nonIndexed.push(param); dynamic.push(false); } }); var resultIndexed = (topics != null) ? this._abiCoder.decode(indexed, bytes_1.concat(topics)) : null; var resultNonIndexed = this._abiCoder.decode(nonIndexed, data, true); var result = []; var nonIndexedIndex = 0, indexedIndex = 0; eventFragment.inputs.forEach(function (param, index) { if (param.indexed) { if (resultIndexed == null) { result[index] = new Indexed({ _isIndexed: true, hash: null }); } else if (dynamic[index]) { result[index] = new Indexed({ _isIndexed: true, hash: resultIndexed[indexedIndex++] }); } else { try { result[index] = resultIndexed[indexedIndex++]; } catch (error) { result[index] = error; } } } else { try { result[index] = resultNonIndexed[nonIndexedIndex++]; } catch (error) { result[index] = error; } } // Add the keyword argument if named and safe if (param.name && result[param.name] == null) { var value_1 = result[index]; // Make error named values throw on access if (value_1 instanceof Error) { Object.defineProperty(result, param.name, { get: function () { throw wrapAccessError("property " + JSON.stringify(param.name), value_1); } }); } else { result[param.name] = value_1; } } }); var _loop_1 = function (i) { var value = result[i]; if (value instanceof Error) { Object.defineProperty(result, i, { get: function () { throw wrapAccessError("index " + i, value); } }); } }; // Make all error indexed values throw on access for (var i = 0; i < result.length; i++) { _loop_1(i); } return Object.freeze(result); }; // Given a transaction, find the matching function fragment (if any) and // determine all its properties and call parameters Interface.prototype.parseTransaction = function (tx) { var fragment = this.getFunction(tx.data.substring(0, 10).toLowerCase()); if (!fragment) { return null; } return new TransactionDescription({ args: this._abiCoder.decode(fragment.inputs, "0x" + tx.data.substring(10)), functionFragment: fragment, name: fragment.name, signature: fragment.format(), sighash: this.getSighash(fragment), value: bignumber_1.BigNumber.from(tx.value || "0"), }); }; // @TODO //parseCallResult(data: BytesLike): ?? // Given an event log, find the matching event fragment (if any) and // determine all its properties and values Interface.prototype.parseLog = function (log) { var fragment = this.getEvent(log.topics[0]); if (!fragment || fragment.anonymous) { return null; } // @TODO: If anonymous, and the only method, and the input count matches, should we parse? // Probably not, because just because it is the only event in the ABI does // not mean we have the full ABI; maybe jsut a fragment? return new LogDescription({ eventFragment: fragment, name: fragment.name, signature: fragment.format(), topic: this.getEventTopic(fragment), args: this.decodeEventLog(fragment, log.data, log.topics) }); }; Interface.prototype.parseError = function (data) { var hexData = bytes_1.hexlify(data); var fragment = this.getError(hexData.substring(0, 10).toLowerCase()); if (!fragment) { return null; } return new ErrorDescription({ args: this._abiCoder.decode(fragment.inputs, "0x" + hexData.substring(10)), errorFragment: fragment, name: fragment.name, signature: fragment.format(), sighash: this.getSighash(fragment), }); }; /* static from(value: Array | string | Interface) { if (Interface.isInterface(value)) { return value; } if (typeof(value) === "string") { return new Interface(JSON.parse(value)); } return new Interface(value); } */ Interface.isInterface = function (value) { return !!(value && value._isInterface); }; return Interface; }()); exports.Interface = Interface; //# sourceMappingURL=interface.js.map