'use strict'; var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); // See: https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI var address_1 = require("../utils/address"); var abi_coder_1 = require("../utils/abi-coder"); var bignumber_1 = require("../utils/bignumber"); var bytes_1 = require("../utils/bytes"); var hash_1 = require("../utils/hash"); var keccak256_1 = require("../utils/keccak256"); var properties_1 = require("../utils/properties"); var types_1 = require("../utils/types"); exports.Indexed = types_1.Indexed; var errors = __importStar(require("../utils/errors")); var _Indexed = /** @class */ (function (_super) { __extends(_Indexed, _super); function _Indexed(hash) { var _this = _super.call(this) || this; properties_1.defineReadOnly(_this, 'hash', hash); return _this; } return _Indexed; }(types_1.Indexed)); var _Description = /** @class */ (function () { function _Description(info) { for (var key in info) { var value = info[key]; if (value != null && typeof (value) === 'object') { properties_1.defineFrozen(this, key, info[key]); } else { properties_1.defineReadOnly(this, key, info[key]); } } } return _Description; }()); var _DeployDescription = /** @class */ (function (_super) { __extends(_DeployDescription, _super); function _DeployDescription() { return _super !== null && _super.apply(this, arguments) || this; } _DeployDescription.prototype.encode = function (bytecode, params) { if (!bytes_1.isHexString(bytecode)) { errors.throwError('invalid contract bytecode', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode }); } errors.checkArgumentCount(params.length, this.inputs.length, 'in Interface constructor'); try { return (bytecode + abi_coder_1.defaultAbiCoder.encode(this.inputs, params).substring(2)); } catch (error) { errors.throwError('invalid constructor argument', errors.INVALID_ARGUMENT, { arg: error.arg, reason: error.reason, value: error.value }); } return null; }; return _DeployDescription; }(_Description)); var _FunctionDescription = /** @class */ (function (_super) { __extends(_FunctionDescription, _super); function _FunctionDescription() { return _super !== null && _super.apply(this, arguments) || this; } _FunctionDescription.prototype.encode = function (params) { errors.checkArgumentCount(params.length, this.inputs.length, 'in interface function ' + this.name); try { return this.sighash + abi_coder_1.defaultAbiCoder.encode(this.inputs, params).substring(2); } catch (error) { errors.throwError('invalid input argument', errors.INVALID_ARGUMENT, { arg: error.arg, reason: error.reason, value: error.value }); } return null; }; _FunctionDescription.prototype.decode = function (data) { try { return abi_coder_1.defaultAbiCoder.decode(this.outputs, bytes_1.arrayify(data)); } catch (error) { errors.throwError('invalid data for function output', errors.INVALID_ARGUMENT, { arg: 'data', errorArg: error.arg, errorValue: error.value, value: data, reason: error.reason }); } }; return _FunctionDescription; }(_Description)); var Result = /** @class */ (function (_super) { __extends(Result, _super); function Result() { return _super !== null && _super.apply(this, arguments) || this; } return Result; }(_Description)); var _EventDescription = /** @class */ (function (_super) { __extends(_EventDescription, _super); function _EventDescription() { return _super !== null && _super.apply(this, arguments) || this; } _EventDescription.prototype.encodeTopics = function (params) { var _this = this; if (params.length > this.inputs.length) { errors.throwError('too many arguments for ' + this.name, errors.UNEXPECTED_ARGUMENT, { maxCount: params.length, expectedCount: this.inputs.length }); } var topics = []; if (!this.anonymous) { topics.push(this.topic); } params.forEach(function (arg, index) { if (arg === null) { topics.push(null); return; } var param = _this.inputs[index]; if (!param.indexed) { errors.throwError('cannot filter non-indexed parameters; must be null', errors.INVALID_ARGUMENT, { argument: (param.name || index), value: arg }); } if (param.type === 'string') { topics.push(hash_1.id(arg)); } else if (param.type === 'bytes') { topics.push(keccak256_1.keccak256(arg)); } else if (param.type.indexOf('[') !== -1 || param.type.substring(0, 5) === 'tuple') { errors.throwError('filtering with tuples or arrays not implemented yet; bug us on GitHub', errors.NOT_IMPLEMENTED, { operation: 'filter(array|tuple)' }); } else { if (param.type === 'address') { address_1.getAddress(arg); } topics.push(bytes_1.hexZeroPad(bytes_1.hexlify(arg), 32).toLowerCase()); } }); // Trim off trailing nulls while (topics.length && topics[topics.length - 1] === null) { topics.pop(); } return topics; }; _EventDescription.prototype.decode = function (data, topics) { // Strip the signature off of non-anonymous topics if (topics != null && !this.anonymous) { topics = topics.slice(1); } var inputIndexed = []; var inputNonIndexed = []; var inputDynamic = []; this.inputs.forEach(function (param, index) { if (param.indexed) { if (param.type === 'string' || param.type === 'bytes' || param.type.indexOf('[') >= 0 || param.type.substring(0, 5) === 'tuple') { inputIndexed.push({ type: 'bytes32', name: (param.name || '') }); inputDynamic.push(true); } else { inputIndexed.push(param); inputDynamic.push(false); } } else { inputNonIndexed.push(param); inputDynamic.push(false); } }); if (topics != null) { var resultIndexed = abi_coder_1.defaultAbiCoder.decode(inputIndexed, bytes_1.concat(topics)); } var resultNonIndexed = abi_coder_1.defaultAbiCoder.decode(inputNonIndexed, bytes_1.arrayify(data)); var result = new Result({}); var nonIndexedIndex = 0, indexedIndex = 0; this.inputs.forEach(function (input, index) { if (input.indexed) { if (topics == null) { result[index] = new _Indexed(null); } else if (inputDynamic[index]) { result[index] = new _Indexed(resultIndexed[indexedIndex++]); } else { result[index] = resultIndexed[indexedIndex++]; } } else { result[index] = resultNonIndexed[nonIndexedIndex++]; } if (input.name) { result[input.name] = result[index]; } }); result.length = this.inputs.length; return result; }; return _EventDescription; }(_Description)); var TransactionDescription = /** @class */ (function (_super) { __extends(TransactionDescription, _super); function TransactionDescription() { return _super !== null && _super.apply(this, arguments) || this; } return TransactionDescription; }(_Description)); var LogDescription = /** @class */ (function (_super) { __extends(LogDescription, _super); function LogDescription() { return _super !== null && _super.apply(this, arguments) || this; } return LogDescription; }(_Description)); function addMethod(method) { switch (method.type) { case 'constructor': { var description = new _DeployDescription({ inputs: method.inputs, payable: (method.payable == null || !!method.payable), type: "deploy" }); if (!this.deployFunction) { this.deployFunction = description; } break; } case 'function': { var signature = abi_coder_1.formatSignature(method).replace(/tuple/g, ''); var sighash = hash_1.id(signature).substring(0, 10); var description = new _FunctionDescription({ inputs: method.inputs, outputs: method.outputs, payable: (method.payable == null || !!method.payable), type: ((method.constant) ? 'call' : 'transaction'), signature: signature, sighash: sighash, }); // Expose the first (and hopefully unique named function if (method.name && this.functions[method.name] == null) { properties_1.defineReadOnly(this.functions, method.name, description); } // Expose all methods by their signature, for overloaded functions if (this.functions[description.signature] == null) { properties_1.defineReadOnly(this.functions, description.signature, description); } break; } case 'event': { var signature = abi_coder_1.formatSignature(method).replace(/tuple/g, ''); var description = new _EventDescription({ name: method.name, signature: signature, inputs: method.inputs, topic: hash_1.id(signature), anonymous: (!!method.anonymous), type: 'event' }); // Expose the first (and hopefully unique) event name if (method.name && this.events[method.name] == null) { properties_1.defineReadOnly(this.events, method.name, description); } // Expose all events by their signature, for overloaded functions if (this.events[description.signature] == null) { properties_1.defineReadOnly(this.events, description.signature, description); } break; } case 'fallback': // Nothing to do for fallback break; default: console.log('WARNING: unsupported ABI type - ' + method.type); break; } } var Interface = /** @class */ (function () { function Interface(abi) { errors.checkNew(this, Interface); if (typeof (abi) === 'string') { try { abi = JSON.parse(abi); } catch (error) { errors.throwError('could not parse ABI JSON', errors.INVALID_ARGUMENT, { arg: 'abi', errorMessage: error.message, value: abi }); } if (!Array.isArray(abi)) { errors.throwError('invalid abi', errors.INVALID_ARGUMENT, { arg: 'abi', value: abi }); return null; } } properties_1.defineReadOnly(this, 'functions', {}); properties_1.defineReadOnly(this, 'events', {}); // Convert any supported ABI format into a standard ABI format var _abi = []; abi.forEach(function (fragment) { if (typeof (fragment) === 'string') { fragment = abi_coder_1.parseSignature(fragment); } // @TODO: We should probable do some validation; create abiCoder.formatSignature for checking _abi.push(fragment); }); properties_1.defineFrozen(this, 'abi', _abi); _abi.forEach(addMethod, this); // If there wasn't a constructor, create the default constructor if (!this.deployFunction) { addMethod.call(this, { type: 'constructor', inputs: [] }); } } Interface.prototype.parseTransaction = function (tx) { var sighash = tx.data.substring(0, 10).toLowerCase(); for (var name in this.functions) { if (name.indexOf('(') === -1) { continue; } var func = this.functions[name]; if (func.sighash === sighash) { var result = abi_coder_1.defaultAbiCoder.decode(func.inputs, '0x' + tx.data.substring(10)); return new TransactionDescription({ args: result, decode: func.decode, name: name, signature: func.signature, sighash: func.sighash, type: 'transaction', value: bignumber_1.bigNumberify(tx.value || 0), }); } } return null; }; Interface.prototype.parseLog = function (log) { for (var name in this.events) { if (name.indexOf('(') === -1) { continue; } var event = this.events[name]; if (event.anonymous) { continue; } if (event.topic !== log.topics[0]) { continue; } // @TODO: If anonymous, and the only method, and the input count matches, should we parse and return it? return new LogDescription({ name: event.name, signature: event.signature, topic: event.topic, type: 'log', values: event.decode(log.data, log.topics) }); } return null; }; return Interface; }()); exports.Interface = Interface;