UNPKG

jcc-moac-abi

Version:

Decoder and encoder for the MOAC ABI and decode events from MOAC transactions

177 lines 5.86 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MoacABI = void 0; const ethers_1 = require("ethers"); const isFunction = require("lodash/isFunction"); // Global ABI registry (replaces abi-decoder) const _abis = []; let _iface = null; function _getIface() { if (!_iface) { _iface = new ethers_1.Interface(_abis); } return _iface; } function _convertValue(value, type) { if (typeof value === "bigint") { return value.toString(); } if (type === "address") { return String(value).toLowerCase(); } return value; } // Topics in MOAC logs may not always be zero-padded to 32 bytes. // Ethers requires 32-byte hex topics, so we pad them before decoding. function _padTopic(topic) { return topic.length < 66 ? "0x" + topic.slice(2).padStart(64, "0") : topic; } /** * decoder and encoder for moac * * @export * @class MoacABI */ class MoacABI { /** * Creates an instance of MoacABI. * @param {Contract} contract moac contract instance * @memberof MoacABI */ constructor(contract) { /** * get item of function meta data * * @param {string} name defined function name in the abi * @param {*} args parameters according to the defined inputs * @returns {IABIItem} * @memberof MoacABI */ this.getAbiItem = (name, ...args) => { const method = this._contract[name]; if (!isFunction(method)) { throw new Error(`The contract doesn't contain "${name}" function`); } const filterABIs = this._abi.filter((item) => item.name === name); let abi; if (filterABIs.length === 1) { abi = filterABIs[0]; } else { abi = filterABIs.find((item) => item.inputs.length === args.length); if (!abi) { throw new Error("Invalid number of arguments to Solidity function"); } } return abi; }; /** * encode the input value by function name * * @param {string} name defined function name in the abi * @param {*} args parameters according to the defined inputs * @returns {string} * @memberof MoacABI */ this.encode = (name, ...args) => { const method = this._contract[name]; if (!isFunction(method)) { throw new Error(`The contract doesn't contain "${name}" function`); } const filterABIs = this._abi.filter((item) => item.name === name); let encodedData; if (filterABIs.length === 1) { encodedData = method["getData"].apply(null, args); } else { const abi = filterABIs.find((item) => item.inputs.length === args.length); if (!abi) { throw new Error("Invalid number of arguments to Solidity function"); } // detail: https://github.com/MOACChain/chain3/blob/master/lib/chain3/function.js#L282 const typename = abi.inputs.map((input) => input.type).join(","); encodedData = method[typename].getData.apply(null, args); } if (encodedData.includes("NaN")) { throw new Error('The encoded data contains "NaN", please check the input arguments'); } return encodedData; }; if (contract && Array.isArray(contract.abi)) { this._contract = contract; this._abi = contract.abi; } else { throw new Error("The input value isn't a contract instance"); } } /** * decode the input value * * @static * @param {string} data * @returns {IDecoded[]} * @memberof MoacABI */ static decode(data) { const parsed = _getIface().parseTransaction({ data }); return { name: parsed.name, params: parsed.fragment.inputs.map((input, i) => ({ name: input.name, value: _convertValue(parsed.args[i], input.type), type: input.type, })), }; } /** * decode moac transaction logs * * @static * @param {ILog[]} logs * @returns {IDecodedLog[]} if event is defined and decode succeed, return log that contains * events as input arguments and name as event's name, otherwise return itself. * @memberof MoacABI */ static decodeLogs(logs) { const iface = _getIface(); return logs.filter((log) => log.topics.length > 0).map((logItem) => { const paddedTopics = logItem.topics.map(_padTopic); const parsed = iface.parseLog({ topics: paddedTopics, data: logItem.TxData }); if (parsed) { const decodedParams = parsed.fragment.inputs.map((input, i) => ({ name: input.name, type: input.type, value: _convertValue(parsed.args[i], input.type), })); return Object.assign(logItem, { events: decodedParams, name: parsed.name }); } return logItem; }); } /** * add abi to registry * * @static * @param {IABIItem[]} abi * @memberof MoacABI */ static addABI(abi) { _abis.push(...abi); _iface = null; } /** * remove all ABIs from registry * * @static * @param {IABIItem[]} abi * @memberof MoacABI */ static removeABI(_abi) { _abis.length = 0; _iface = null; } } exports.default = MoacABI; exports.MoacABI = MoacABI; //# sourceMappingURL=abi.js.map