text: '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("./address");
  var abi_coder_1 = require("./abi-coder");
  var bignumber_1 = require("./bignumber");
  var bytes_1 = require("./bytes");
  var hash_1 = require("./hash");
  var keccak256_1 = require("./keccak256");
  var properties_1 = require("./properties");
  var errors = __importStar(require("../errors"));
  ///////////////////////////////
  var _Indexed = /** @class */ (function () {
      function _Indexed(hash) {
          properties_1.setType(this, 'Indexed');
          properties_1.defineReadOnly(this, 'hash', hash);
      }
      return _Indexed;
  }());
  var Description = /** @class */ (function () {
      function Description(info) {
          properties_1.setType(this, 'Description');
          for (var key in info) {
              properties_1.defineReadOnly(this, key, properties_1.deepCopy(info[key], true));
          }
          Object.freeze(this);
      }
      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) {
              var param = _this.inputs[index];
              if (!param.indexed) {
                  if (arg != null) {
                      errors.throwError('cannot filter non-indexed parameters; must be null', errors.INVALID_ARGUMENT, { argument: (param.name || index), value: arg });
                  }
                  return;
              }
              if (arg == null) {
                  topics.push(null);
              }
              else 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 = {};
          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 new Result(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)
              });
              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 isConst = false;
              if (method.constant != null) {
                  isConst = method.constant;
              }
              else if (method.stateMutability != null) {
                  isConst = (method.stateMutability == "view" || method.stateMutability == "pure");
              }
              var description = new _FunctionDescription({
                  inputs: method.inputs,
                  outputs: method.outputs,
                  gas: method.gas,
                  payable: (method.payable == null || !!method.payable),
                  type: (isConst ? 'call' : 'transaction'),
                  name: method.name,
                  signature: signature,
                  sighash: sighash,
              });
              // Expose the first (and hopefully unique named function)
              if (method.name) {
                  if (this.functions[method.name] == null) {
                      properties_1.defineReadOnly(this.functions, method.name, description);
                  }
                  else {
                      errors.warn('WARNING: Multiple definitions for ' + method.name);
                  }
              }
              // 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)
              });
              // 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:
              errors.warn('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.defineReadOnly(this, 'abi', properties_1.deepCopy(_abi, true));
          _abi.forEach(addMethod, this);
          // If there wasn't a constructor, create the default constructor
          if (!this.deployFunction) {
              addMethod.call(this, { type: 'constructor', inputs: [] });
          }
          properties_1.setType(this, 'Interface');
      }
      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: func.name,
                      signature: func.signature,
                      sighash: func.sighash,
                      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({
                  decode: event.decode,
                  name: event.name,
                  signature: event.signature,
                  topic: event.topic,
                  values: event.decode(log.data, log.topics)
              });
          }
          return null;
      };
      Interface.isInterface = function (value) {
          return properties_1.isType(value, 'Interface');
      };
      Interface.isIndexed = function (value) {
          return properties_1.isType(value, 'Indexed');
      };
      return Interface;
  }());
  exports.Interface = Interface;
  