/**
 * Test Banner
 */

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@harmony-js/crypto'), require('@harmony-js/utils'), require('@harmony-js/transaction'), require('@harmony-js/network')) :
    typeof define === 'function' && define.amd ? define(['exports', '@harmony-js/crypto', '@harmony-js/utils', '@harmony-js/transaction', '@harmony-js/network'], factory) :
    (factory((global.harmonyContract = {}),global['@harmony-js/crypto'],global['@harmony-js/utils'],global['@harmony-js/transaction'],global['@harmony-js/network']));
}(this, (function (exports,crypto,utils,transaction,network) { 'use strict';

    /*! *****************************************************************************
    Copyright (c) Microsoft Corporation. All rights reserved.
    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
    this file except in compliance with the License. You may obtain a copy of the
    License at http://www.apache.org/licenses/LICENSE-2.0

    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
    MERCHANTABLITY OR NON-INFRINGEMENT.

    See the Apache Version 2.0 License for specific language governing permissions
    and limitations under the License.
    ***************************************************************************** */
    /* global Reflect, Promise */

    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 (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };

    function __extends(d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    }

    var __assign = function() {
        __assign = Object.assign || function __assign(t) {
            for (var s, i = 1, n = arguments.length; i < n; i++) {
                s = arguments[i];
                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
            }
            return t;
        };
        return __assign.apply(this, arguments);
    };

    function __awaiter(thisArg, _arguments, P, generator) {
        return new (P || (P = Promise))(function (resolve, reject) {
            function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
            function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
            function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
            step((generator = generator.apply(thisArg, _arguments || [])).next());
        });
    }

    function __generator(thisArg, body) {
        var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
        return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
        function verb(n) { return function (v) { return step([n, v]); }; }
        function step(op) {
            if (f) throw new TypeError("Generator is already executing.");
            while (_) try {
                if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
                if (y = 0, t) op = [op[0] & 2, t.value];
                switch (op[0]) {
                    case 0: case 1: t = op; break;
                    case 4: _.label++; return { value: op[1], done: false };
                    case 5: _.label++; y = op[1]; op = [0]; continue;
                    case 7: op = _.ops.pop(); _.trys.pop(); continue;
                    default:
                        if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                        if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                        if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                        if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                        if (t[2]) _.ops.pop();
                        _.trys.pop(); continue;
                }
                op = body.call(thisArg, _);
            } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
            if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
        }
    }

    function __read(o, n) {
        var m = typeof Symbol === "function" && o[Symbol.iterator];
        if (!m) return o;
        var i = m.call(o), r, ar = [], e;
        try {
            while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
        }
        catch (error) { e = { error: error }; }
        finally {
            try {
                if (r && !r.done && (m = i["return"])) m.call(i);
            }
            finally { if (e) throw e.error; }
        }
        return ar;
    }

    // this file is mainly ported from `ethers.js`, but done some fixes
    var NegativeOne = new crypto.BN(-1);
    var One = new crypto.BN(1);
    // const Two: BN = new BN(2);
    var Zero = new crypto.BN(0);
    var HashZero = '0x0000000000000000000000000000000000000000000000000000000000000000';
    var MaxUint256 = utils.hexToBN('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
    ///////////////////////////////
    var paramTypeBytes = new RegExp(/^bytes([0-9]*)$/);
    var paramTypeNumber = new RegExp(/^(u?int)([0-9]*)$/);
    var paramTypeArray = new RegExp(/^(.*)\[([0-9]*)\]$/);
    var defaultCoerceFunc = function (type, value) {
        var match = type.match(paramTypeNumber);
        if (match && parseInt(match[2], 10) <= 48) {
            // return value.toNumber();
            return value.toString('hex');
        }
        return value;
    };
    function verifyType(type) {
        // These need to be transformed to their full description
        if (type.match(/^uint($|[^1-9])/)) {
            type = 'uint256' + type.substring(4);
        }
        else if (type.match(/^int($|[^1-9])/)) {
            type = 'int256' + type.substring(3);
        }
        return type;
    }
    function parseParam(param, allowIndexed) {
        var originalParam = param;
        // tslint:disable-next-line: no-shadowed-variable
        function throwError(i) {
            throw new Error('unexpected character "' +
                originalParam[i] +
                '" at position ' +
                i +
                ' in "' +
                originalParam +
                '"');
        }
        param = param.replace(/\s/g, ' ');
        var parent = { type: '', name: '', state: { allowType: true } };
        var node = parent;
        for (var i = 0; i < param.length; i++) {
            var c = param[i];
            switch (c) {
                case '(':
                    if (!node.state || !node.state.allowParams) {
                        throwError(i);
                    }
                    if (node.state) {
                        node.state.allowType = false;
                    }
                    if (node.type) {
                        node.type = verifyType(node.type);
                    }
                    node.components = [
                        { type: '', name: '', parent: node, state: { allowType: true } },
                    ];
                    node = node.components[0];
                    break;
                case ')':
                    delete node.state;
                    if (allowIndexed && node.name === 'indexed') {
                        node.indexed = true;
                        node.name = '';
                    }
                    if (node.type) {
                        node.type = verifyType(node.type);
                    }
                    var child = node;
                    node = node.parent;
                    if (!node) {
                        throwError(i);
                    }
                    delete child.parent;
                    if (node.state) {
                        node.state.allowParams = false;
                        node.state.allowName = true;
                        node.state.allowArray = true;
                    }
                    break;
                case ',':
                    delete node.state;
                    if (allowIndexed && node.name === 'indexed') {
                        node.indexed = true;
                        node.name = '';
                    }
                    if (node.type) {
                        node.type = verifyType(node.type);
                    }
                    var sibling = {
                        type: '',
                        name: '',
                        parent: node.parent,
                        state: { allowType: true },
                    };
                    node.parent.components.push(sibling);
                    delete node.parent;
                    node = sibling;
                    break;
                // Hit a space...
                case ' ':
                    // If reading type, the type is done and may read a param or name
                    if (node.state) {
                        if (node.state.allowType) {
                            if (node.type !== '' && node.type) {
                                node.type = verifyType(node.type);
                                delete node.state.allowType;
                                node.state.allowName = true;
                                node.state.allowParams = true;
                            }
                        }
                        // If reading name, the name is done
                        if (node.state.allowName) {
                            if (node.name !== '') {
                                if (allowIndexed && node.name === 'indexed') {
                                    node.indexed = true;
                                    node.name = '';
                                }
                                else {
                                    node.state.allowName = false;
                                }
                            }
                        }
                    }
                    break;
                case '[':
                    if (!node.state || !node.state.allowArray) {
                        throwError(i);
                    }
                    if (node.state) {
                        node.type += c;
                        node.state.allowArray = false;
                        node.state.allowName = false;
                        node.state.readArray = true;
                    }
                    break;
                case ']':
                    if (!node.state || !node.state.readArray) {
                        throwError(i);
                    }
                    if (node.state) {
                        node.type += c;
                        node.state.readArray = false;
                        node.state.allowArray = true;
                        node.state.allowName = true;
                    }
                    break;
                default:
                    if (node.state) {
                        if (node.state.allowType) {
                            node.type += c;
                            node.state.allowParams = true;
                            node.state.allowArray = true;
                        }
                        else if (node.state.allowName) {
                            node.name += c;
                            delete node.state.allowArray;
                        }
                        else if (node.state.readArray) {
                            node.type += c;
                        }
                        else {
                            throwError(i);
                        }
                    }
            }
        }
        if (node.parent) {
            throw new Error('unexpected eof');
        }
        delete parent.state;
        if (allowIndexed && node.name === 'indexed') {
            node.indexed = true;
            node.name = '';
        }
        if (parent.type) {
            parent.type = verifyType(parent.type);
        }
        return parent;
    }
    var Coder = /** @class */ (function () {
        function Coder(coerceFunc, name, type, localName, dynamic) {
            this.coerceFunc = coerceFunc;
            this.name = name;
            this.type = type;
            this.localName = localName;
            this.dynamic = dynamic;
        }
        return Coder;
    }());
    // Clones the functionality of an existing Coder, but without a localName
    // tslint:disable-next-line: max-classes-per-file
    var CoderAnonymous = /** @class */ (function (_super) {
        __extends(CoderAnonymous, _super);
        function CoderAnonymous(coder) {
            var _this = _super.call(this, coder.coerceFunc, coder.name, coder.type, undefined, coder.dynamic) || this;
            _this.coder = coder;
            return _this;
        }
        CoderAnonymous.prototype.encode = function (value) {
            return this.coder.encode(value);
        };
        CoderAnonymous.prototype.decode = function (data, offset) {
            return this.coder.decode(data, offset);
        };
        return CoderAnonymous;
    }(Coder));
    // tslint:disable-next-line: max-classes-per-file
    var CoderNull = /** @class */ (function (_super) {
        __extends(CoderNull, _super);
        function CoderNull(coerceFunc, localName) {
            return _super.call(this, coerceFunc, 'null', '', localName, false) || this;
        }
        CoderNull.prototype.encode = function (value) {
            var result = crypto.arrayify([]) || new Uint8Array();
            return result;
        };
        CoderNull.prototype.decode = function (data, offset) {
            if (offset > data.length) {
                throw new Error('invalid null');
            }
            return {
                consumed: 0,
                value: this.coerceFunc('null', undefined),
            };
        };
        return CoderNull;
    }(Coder));
    // tslint:disable-next-line: max-classes-per-file
    var CoderNumber = /** @class */ (function (_super) {
        __extends(CoderNumber, _super);
        function CoderNumber(coerceFunc, size, signed, localName) {
            var _this = this;
            var name = (signed ? 'int' : 'uint') + size * 8;
            _this = _super.call(this, coerceFunc, name, name, localName, false) || this;
            _this.size = size;
            _this.signed = signed;
            return _this;
        }
        CoderNumber.prototype.encode = function (value) {
            var result;
            try {
                var v = new crypto.BN(value);
                if (this.signed) {
                    var bounds = MaxUint256.maskn(this.size * 8 - 1);
                    if (v.gt(bounds)) {
                        throw new Error('out-of-bounds');
                    }
                    bounds = bounds.add(One).mul(NegativeOne);
                    if (v.lt(bounds)) {
                        throw new Error('out-of-bounds');
                    }
                }
                else if (v.lt(Zero) || v.gt(MaxUint256.maskn(this.size * 8))) {
                    throw new Error('out-of-bounds');
                }
                v = v.toTwos(this.size * 8).maskn(this.size * 8);
                if (this.signed) {
                    v = v.fromTwos(this.size * 8).toTwos(256);
                }
                var vString = v.toString('hex');
                result = crypto.padZeros(crypto.arrayify("0x" + vString) || new Uint8Array(), 32);
            }
            catch (error) {
                crypto.throwError('invalid number value', crypto.INVALID_ARGUMENT, {
                    arg: this.localName,
                    coderType: this.name,
                    value: value,
                });
            }
            return result || crypto.padZeros(new Uint8Array(), 32);
        };
        CoderNumber.prototype.decode = function (data, offset) {
            if (data.length < offset + 32) {
                crypto.throwError('insufficient data for ' + this.name + ' type', crypto.INVALID_ARGUMENT, {
                    arg: this.localName,
                    coderType: this.name,
                    value: crypto.hexlify(data.slice(offset, offset + 32)),
                });
            }
            var junkLength = 32 - this.size;
            var dataValue = crypto.hexlify(data.slice(offset + junkLength, offset + 32));
            var value = utils.hexToBN(dataValue);
            // tslint:disable-next-line: prefer-conditional-expression
            if (this.signed) {
                value = value.fromTwos(this.size * 8);
            }
            else {
                value = value.maskn(this.size * 8);
            }
            return {
                consumed: 32,
                value: this.coerceFunc(this.name, value),
            };
        };
        return CoderNumber;
    }(Coder));
    var uint256Coder = new CoderNumber(function (type, value) {
        return value;
    }, 32, false, 'none');
    // tslint:disable-next-line: max-classes-per-file
    var CoderBoolean = /** @class */ (function (_super) {
        __extends(CoderBoolean, _super);
        function CoderBoolean(coerceFunc, localName) {
            return _super.call(this, coerceFunc, 'bool', 'bool', localName, false) || this;
        }
        CoderBoolean.prototype.encode = function (value) {
            return uint256Coder.encode(!!value ? new crypto.BN(1) : new crypto.BN(0));
        };
        CoderBoolean.prototype.decode = function (data, offset) {
            var result;
            try {
                result = uint256Coder.decode(data, offset);
            }
            catch (error) {
                if (error.reason === 'insufficient data for uint256 type') {
                    crypto.throwError('insufficient data for boolean type', crypto.INVALID_ARGUMENT, {
                        arg: this.localName,
                        coderType: 'boolean',
                        value: error.value,
                    });
                }
                throw error;
            }
            return {
                consumed: result.consumed,
                value: this.coerceFunc('bool', !result.value.isZero()),
            };
        };
        return CoderBoolean;
    }(Coder));
    // tslint:disable-next-line: max-classes-per-file
    var CoderFixedBytes = /** @class */ (function (_super) {
        __extends(CoderFixedBytes, _super);
        function CoderFixedBytes(coerceFunc, length, localName) {
            var _this = this;
            var name = 'bytes' + length;
            _this = _super.call(this, coerceFunc, name, name, localName, false) || this;
            _this.length = length;
            return _this;
        }
        CoderFixedBytes.prototype.encode = function (value) {
            var result = new Uint8Array(this.length);
            try {
                var arrayied = crypto.arrayify(value);
                var data = null;
                if (arrayied !== null) {
                    var valueToByte = crypto.hexlify(arrayied);
                    data = crypto.arrayify(crypto.bytesPadRight(valueToByte, this.length));
                }
                else {
                    throw new Error('cannot arraify data');
                }
                if (data === null || data.length !== this.length) {
                    throw new Error('incorrect data length');
                }
                result.set(data);
            }
            catch (error) {
                crypto.throwError('invalid ' + this.name + ' value', crypto.INVALID_ARGUMENT, {
                    arg: this.localName,
                    coderType: this.name,
                    value: error.value || value,
                });
            }
            return result;
        };
        CoderFixedBytes.prototype.decode = function (data, offset) {
            if (data.length < offset + 32) {
                crypto.throwError('insufficient data for ' + name + ' type', crypto.INVALID_ARGUMENT, {
                    arg: this.localName,
                    coderType: this.name,
                    value: crypto.hexlify(data.slice(offset, offset + 32)),
                });
            }
            return {
                consumed: 32,
                value: this.coerceFunc(this.name, crypto.hexlify(data.slice(offset, offset + this.length))),
            };
        };
        return CoderFixedBytes;
    }(Coder));
    // tslint:disable-next-line: max-classes-per-file
    var CoderAddress = /** @class */ (function (_super) {
        __extends(CoderAddress, _super);
        function CoderAddress(coerceFunc, localName) {
            return _super.call(this, coerceFunc, 'address', 'address', localName, false) || this;
        }
        CoderAddress.prototype.encode = function (value) {
            var result = new Uint8Array(32);
            try {
                var addr = crypto.arrayify(crypto.toChecksumAddress(value)) || new Uint8Array();
                result.set(addr, 12);
            }
            catch (error) {
                crypto.throwError('invalid address', crypto.INVALID_ARGUMENT, {
                    arg: this.localName,
                    coderType: 'address',
                    value: value,
                });
            }
            return result;
        };
        CoderAddress.prototype.decode = function (data, offset) {
            if (data.length < offset + 32) {
                crypto.throwError('insufficuent data for address type', crypto.INVALID_ARGUMENT, {
                    arg: this.localName,
                    coderType: 'address',
                    value: crypto.hexlify(data.slice(offset, offset + 32)),
                });
            }
            return {
                consumed: 32,
                value: this.coerceFunc('address', crypto.toChecksumAddress(crypto.hexlify(data.slice(offset + 12, offset + 32)))),
            };
        };
        return CoderAddress;
    }(Coder));
    function _encodeDynamicBytes(value) {
        var dataLength = 32 * Math.ceil(value.length / 32);
        var padding = new Uint8Array(dataLength - value.length);
        return crypto.concat([uint256Coder.encode(new crypto.BN(value.length)), value, padding]);
    }
    function _decodeDynamicBytes(data, offset, localName) {
        if (data.length < offset + 32) {
            crypto.throwError('insufficient data for dynamicBytes length', crypto.INVALID_ARGUMENT, {
                arg: localName,
                coderType: 'dynamicBytes',
                value: crypto.hexlify(data.slice(offset, offset + 32)),
            });
        }
        var length = uint256Coder.decode(data, offset).value;
        try {
            length = length.toNumber();
        }
        catch (error) {
            crypto.throwError('dynamic bytes count too large', crypto.INVALID_ARGUMENT, {
                arg: localName,
                coderType: 'dynamicBytes',
                value: length.toString(),
            });
        }
        if (data.length < offset + 32 + length) {
            crypto.throwError('insufficient data for dynamicBytes type', crypto.INVALID_ARGUMENT, {
                arg: localName,
                coderType: 'dynamicBytes',
                value: crypto.hexlify(data.slice(offset, offset + 32 + length)),
            });
        }
        return {
            consumed: 32 + 32 * Math.ceil(length / 32),
            value: data.slice(offset + 32, offset + 32 + length),
        };
    }
    // tslint:disable-next-line: max-classes-per-file
    var CoderDynamicBytes = /** @class */ (function (_super) {
        __extends(CoderDynamicBytes, _super);
        function CoderDynamicBytes(coerceFunc, localName) {
            return _super.call(this, coerceFunc, 'bytes', 'bytes', localName, true) || this;
        }
        CoderDynamicBytes.prototype.encode = function (value) {
            var result = new Uint8Array();
            try {
                result = _encodeDynamicBytes(crypto.arrayify(value) || new Uint8Array());
            }
            catch (error) {
                crypto.throwError('invalid bytes value', crypto.INVALID_ARGUMENT, {
                    arg: this.localName,
                    coderType: 'bytes',
                    value: error.value,
                });
            }
            return result;
        };
        CoderDynamicBytes.prototype.decode = function (data, offset) {
            var result = _decodeDynamicBytes(data, offset, this.localName || '');
            result.value = this.coerceFunc('bytes', crypto.hexlify(result.value));
            return result;
        };
        return CoderDynamicBytes;
    }(Coder));
    // tslint:disable-next-line: max-classes-per-file
    var CoderString = /** @class */ (function (_super) {
        __extends(CoderString, _super);
        function CoderString(coerceFunc, localName) {
            return _super.call(this, coerceFunc, 'string', 'string', localName, true) || this;
        }
        CoderString.prototype.encode = function (value) {
            if (typeof value !== 'string') {
                crypto.throwError('invalid string value', crypto.INVALID_ARGUMENT, {
                    arg: this.localName,
                    coderType: 'string',
                    value: value,
                });
            }
            return _encodeDynamicBytes(toUtf8Bytes(value));
        };
        CoderString.prototype.decode = function (data, offset) {
            var result = _decodeDynamicBytes(data, offset, this.localName || '');
            result.value = this.coerceFunc('string', toUtf8String(result.value));
            return result;
        };
        return CoderString;
    }(Coder));
    function alignSize(size) {
        return 32 * Math.ceil(size / 32);
    }
    function pack(coders, values) {
        if (Array.isArray(values)) ;
        else if (values && typeof values === 'object') {
            var arrayValues_1 = [];
            coders.forEach(function (coder) {
                arrayValues_1.push(values[coder.localName || '']);
            });
            values = arrayValues_1;
        }
        else {
            crypto.throwError('invalid tuple value', crypto.INVALID_ARGUMENT, {
                coderType: 'tuple',
                value: values,
            });
        }
        if (coders.length !== values.length) {
            crypto.throwError('types/value length mismatch', crypto.INVALID_ARGUMENT, {
                coderType: 'tuple',
                value: values,
            });
        }
        var parts = [];
        coders.forEach(function (coder, index) {
            parts.push({ dynamic: coder.dynamic, value: coder.encode(values[index]) });
        });
        var staticSize = 0;
        var dynamicSize = 0;
        parts.forEach(function (part) {
            if (part.dynamic) {
                staticSize += 32;
                dynamicSize += alignSize(part.value.length);
            }
            else {
                staticSize += alignSize(part.value.length);
                // todo : is it to be static size not alignSize?
            }
        });
        var offset = 0;
        var dynamicOffset = staticSize;
        var data = new Uint8Array(staticSize + dynamicSize);
        parts.forEach(function (part) {
            if (part.dynamic) {
                // uint256Coder.encode(dynamicOffset).copy(data, offset);
                data.set(uint256Coder.encode(new crypto.BN(dynamicOffset)), offset);
                offset += 32;
                // part.value.copy(data, dynamicOffset);  @TODO
                data.set(part.value, dynamicOffset);
                dynamicOffset += alignSize(part.value.length);
            }
            else {
                // part.value.copy(data, offset);  @TODO
                data.set(part.value, offset);
                offset += alignSize(part.value.length);
            }
        });
        return data;
    }
    function unpack(coders, data, offset) {
        var baseOffset = offset;
        var consumed = 0;
        var value = [];
        coders.forEach(function (coder) {
            var result;
            if (coder.dynamic) {
                var dynamicOffset = uint256Coder.decode(data, offset);
                result = coder.decode(data, baseOffset + dynamicOffset.value.toNumber());
                // The dynamic part is leap-frogged somewhere else; doesn't count towards size
                result.consumed = dynamicOffset.consumed;
            }
            else {
                result = coder.decode(data, offset);
            }
            if (result.value !== undefined) {
                value.push(result.value);
            }
            offset += result.consumed;
            consumed += result.consumed;
        });
        coders.forEach(function (coder, index) {
            var name = coder.localName;
            if (!name) {
                return;
            }
            if (name === 'length') {
                name = '_length';
            }
            if (value[name] != null) {
                return;
            }
            value[name] = value[index];
        });
        return {
            value: value,
            consumed: consumed,
        };
    }
    // tslint:disable-next-line: max-classes-per-file
    var CoderArray = /** @class */ (function (_super) {
        __extends(CoderArray, _super);
        function CoderArray(coerceFunc, coder, length, localName) {
            var _this = this;
            var type = coder.type + '[' + (length >= 0 ? length : '') + ']';
            var dynamic = length === -1 || coder.dynamic;
            _this = _super.call(this, coerceFunc, 'array', type, localName, dynamic) || this;
            _this.coder = coder;
            _this.length = length;
            return _this;
        }
        CoderArray.prototype.encode = function (value) {
            if (!Array.isArray(value)) {
                crypto.throwError('expected array value', crypto.INVALID_ARGUMENT, {
                    arg: this.localName,
                    coderType: 'array',
                    value: value,
                });
            }
            var count = this.length;
            var result = new Uint8Array(0);
            if (count === -1) {
                count = value.length;
                result = uint256Coder.encode(new crypto.BN(count));
            }
            crypto.checkArgumentCount(count, value.length, ' in coder array' + (this.localName ? ' ' + this.localName : ''));
            var coders = [];
            // tslint:disable-next-line: prefer-for-of
            for (var i = 0; i < value.length; i++) {
                coders.push(this.coder);
            }
            return crypto.concat([result, pack(coders, value)]);
        };
        CoderArray.prototype.decode = function (data, offset) {
            // @TODO:
            // if (data.length < offset + length * 32) { throw new Error('invalid array'); }
            var consumed = 0;
            var count = this.length;
            var decodedLength = { consumed: 0, value: undefined };
            if (count === -1) {
                try {
                    decodedLength = uint256Coder.decode(data, offset);
                }
                catch (error) {
                    crypto.throwError('insufficient data for dynamic array length', crypto.INVALID_ARGUMENT, {
                        arg: this.localName,
                        coderType: 'array',
                        value: error.value,
                    });
                }
                try {
                    count = decodedLength.value.toNumber();
                }
                catch (error) {
                    crypto.throwError('array count too large', crypto.INVALID_ARGUMENT, {
                        arg: this.localName,
                        coderType: 'array',
                        value: decodedLength.value.toString(),
                    });
                }
                consumed += decodedLength.consumed;
                offset += decodedLength.consumed;
            }
            var coders = [];
            for (var i = 0; i < count; i++) {
                coders.push(new CoderAnonymous(this.coder));
            }
            var result = unpack(coders, data, offset);
            result.consumed += consumed;
            result.value = this.coerceFunc(this.type, result.value);
            return result;
        };
        return CoderArray;
    }(Coder));
    // tslint:disable-next-line: max-classes-per-file
    var CoderTuple = /** @class */ (function (_super) {
        __extends(CoderTuple, _super);
        function CoderTuple(coerceFunc, coders, localName) {
            var _this = this;
            var dynamic = false;
            var types = [];
            coders.forEach(function (coder) {
                if (coder.dynamic) {
                    dynamic = true;
                }
                types.push(coder.type);
            });
            var type = 'tuple(' + types.join(',') + ')';
            _this = _super.call(this, coerceFunc, 'tuple', type, localName, dynamic) || this;
            _this.coders = coders;
            return _this;
        }
        CoderTuple.prototype.encode = function (value) {
            return pack(this.coders, value);
        };
        CoderTuple.prototype.decode = function (data, offset) {
            var result = unpack(this.coders, data, offset);
            result.value = this.coerceFunc(this.type, result.value);
            return result;
        };
        return CoderTuple;
    }(Coder));
    function defineReadOnly(object, name, value) {
        Object.defineProperty(object, name, {
            enumerable: true,
            value: value,
            writable: false,
        });
    }
    // @TODO: Is there a way to return "class"?
    var paramTypeSimple = {
        address: CoderAddress,
        bool: CoderBoolean,
        string: CoderString,
        bytes: CoderDynamicBytes,
    };
    function getTupleParamCoder(coerceFunc, components, localName) {
        if (!components) {
            components = [];
        }
        var coders = [];
        components.forEach(function (component) {
            coders.push(getParamCoder(coerceFunc, component));
        });
        return new CoderTuple(coerceFunc, coders, localName);
    }
    function getParamCoder(coerceFunc, param) {
        var coder = paramTypeSimple[param.type];
        if (coder) {
            return new coder(coerceFunc, param.name);
        }
        var matcher = param.type.match(paramTypeNumber);
        if (matcher) {
            var size = parseInt(matcher[2] || '256', 10);
            if (size === 0 || size > 256 || size % 8 !== 0) {
                crypto.throwError('invalid ' + matcher[1] + ' bit length', crypto.INVALID_ARGUMENT, {
                    arg: 'param',
                    value: param,
                });
            }
            return new CoderNumber(coerceFunc, size / 8, matcher[1] === 'int', param.name || '');
        }
        var matcher2 = param.type.match(paramTypeBytes);
        if (matcher2) {
            var size = parseInt(matcher2[1], 10);
            if (size === 0 || size > 32) {
                crypto.throwError('invalid bytes length', crypto.INVALID_ARGUMENT, {
                    arg: 'param',
                    value: param,
                });
            }
            return new CoderFixedBytes(coerceFunc, size, param.name || '');
        }
        var matcher3 = param.type.match(paramTypeArray);
        if (matcher3) {
            var size = parseInt(matcher3[2] || '-1', 10);
            param = shallowCopy(param);
            param.type = matcher3[1];
            param = deepCopy(param);
            return new CoderArray(coerceFunc, getParamCoder(coerceFunc, param), size, param.name || '');
        }
        if (param.type.substring(0, 5) === 'tuple') {
            return getTupleParamCoder(coerceFunc, param.components || [], param.name || '');
        }
        if (param.type === '') {
            return new CoderNull(coerceFunc, param.name || '');
        }
        crypto.throwError('invalid type', crypto.INVALID_ARGUMENT, {
            arg: 'type',
            value: param.type,
        });
    }
    var UnicodeNormalizationForm;
    (function (UnicodeNormalizationForm) {
        UnicodeNormalizationForm["current"] = "";
        UnicodeNormalizationForm["NFC"] = "NFC";
        UnicodeNormalizationForm["NFD"] = "NFD";
        UnicodeNormalizationForm["NFKC"] = "NFKC";
        UnicodeNormalizationForm["NFKD"] = "NFKD";
    })(UnicodeNormalizationForm || (UnicodeNormalizationForm = {}));
    function toUtf8Bytes(str, form) {
        if (form === void 0) { form = UnicodeNormalizationForm.current; }
        if (form !== UnicodeNormalizationForm.current) {
            crypto.checkNormalize();
            str = str.normalize(form);
        }
        var result = [];
        for (var i = 0; i < str.length; i++) {
            var c = str.charCodeAt(i);
            if (c < 0x80) {
                result.push(c);
            }
            else if (c < 0x800) {
                result.push((c >> 6) | 0xc0);
                result.push((c & 0x3f) | 0x80);
            }
            else if ((c & 0xfc00) === 0xd800) {
                i++;
                var c2 = str.charCodeAt(i);
                if (i >= str.length || (c2 & 0xfc00) !== 0xdc00) {
                    throw new Error('invalid utf-8 string');
                }
                // Surrogate Pair
                c = 0x10000 + ((c & 0x03ff) << 10) + (c2 & 0x03ff);
                result.push((c >> 18) | 0xf0);
                result.push(((c >> 12) & 0x3f) | 0x80);
                result.push(((c >> 6) & 0x3f) | 0x80);
                result.push((c & 0x3f) | 0x80);
            }
            else {
                result.push((c >> 12) | 0xe0);
                result.push(((c >> 6) & 0x3f) | 0x80);
                result.push((c & 0x3f) | 0x80);
            }
        }
        return crypto.arrayify(result) || new Uint8Array();
    }
    // http://stackoverflow.com/questions/13356493/decode-utf-8-with-javascript#13691499
    function toUtf8String(bytes, ignoreErrors) {
        bytes = crypto.arrayify(bytes) || new Uint8Array();
        var result = '';
        var i = 0;
        // Invalid bytes are ignored
        while (i < bytes.length) {
            var c = bytes[i++];
            // 0xxx xxxx
            if (c >> 7 === 0) {
                result += String.fromCharCode(c);
                continue;
            }
            // Multibyte; how many bytes left for this character?
            var extraLength = null;
            var overlongMask = null;
            // 110x xxxx 10xx xxxx
            if ((c & 0xe0) === 0xc0) {
                extraLength = 1;
                overlongMask = 0x7f;
                // 1110 xxxx 10xx xxxx 10xx xxxx
            }
            else if ((c & 0xf0) === 0xe0) {
                extraLength = 2;
                overlongMask = 0x7ff;
                // 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
            }
            else if ((c & 0xf8) === 0xf0) {
                extraLength = 3;
                overlongMask = 0xffff;
            }
            else {
                if (!ignoreErrors) {
                    if ((c & 0xc0) === 0x80) {
                        throw new Error('invalid utf8 byte sequence; unexpected continuation byte');
                    }
                    throw new Error('invalid utf8 byte sequence; invalid prefix');
                }
                continue;
            }
            // Do we have enough bytes in our data?
            if (i + extraLength > bytes.length) {
                if (!ignoreErrors) {
                    throw new Error('invalid utf8 byte sequence; too short');
                }
                // If there is an invalid unprocessed byte, skip continuation bytes
                for (; i < bytes.length; i++) {
                    if (bytes[i] >> 6 !== 0x02) {
                        break;
                    }
                }
                continue;
            }
            // Remove the length prefix from the char
            var res = c & ((1 << (8 - extraLength - 1)) - 1);
            for (var j = 0; j < extraLength; j++) {
                var nextChar = bytes[i];
                // Invalid continuation byte
                if ((nextChar & 0xc0) !== 0x80) {
                    res = null;
                    break;
                }
                res = (res << 6) | (nextChar & 0x3f);
                i++;
            }
            if (res === null) {
                if (!ignoreErrors) {
                    throw new Error('invalid utf8 byte sequence; invalid continuation byte');
                }
                continue;
            }
            // Check for overlong seuences (more bytes than needed)
            if (res <= overlongMask) {
                if (!ignoreErrors) {
                    throw new Error('invalid utf8 byte sequence; overlong');
                }
                continue;
            }
            // Maximum code point
            if (res > 0x10ffff) {
                if (!ignoreErrors) {
                    throw new Error('invalid utf8 byte sequence; out-of-range');
                }
                continue;
            }
            // Reserved for UTF-16 surrogate halves
            if (res >= 0xd800 && res <= 0xdfff) {
                if (!ignoreErrors) {
                    throw new Error('invalid utf8 byte sequence; utf-16 surrogate');
                }
                continue;
            }
            if (res <= 0xffff) {
                result += String.fromCharCode(res);
                continue;
            }
            res -= 0x10000;
            result += String.fromCharCode(((res >> 10) & 0x3ff) + 0xd800, (res & 0x3ff) + 0xdc00);
        }
        return result;
    }
    function formatBytes32String(text) {
        // Get the bytes
        var bytes = toUtf8Bytes(text);
        // Check we have room for null-termination
        if (bytes.length > 31) {
            throw new Error('bytes32 string must be less than 32 bytes');
        }
        // Zero-pad (implicitly null-terminates)
        return crypto.hexlify(crypto.concat([bytes, HashZero]).slice(0, 32));
    }
    function parseBytes32String(bytes) {
        var data = crypto.arrayify(bytes) || new Uint8Array();
        // Must be 32 bytes with a null-termination
        if (data.length !== 32) {
            throw new Error('invalid bytes32 - not 32 bytes long');
        }
        if (data[31] !== 0) {
            throw new Error('invalid bytes32 sdtring - no null terminator');
        }
        // Find the null termination
        var length = 31;
        while (data[length - 1] === 0) {
            length--;
        }
        // Determine the string value
        return toUtf8String(data.slice(0, length));
    }
    function isType(object, type) {
        return object && object._ethersType === type;
    }
    function shallowCopy(object) {
        var result = {};
        // tslint:disable-next-line: forin
        for (var key in object) {
            result[key] = object[key];
        }
        return result;
    }
    var opaque = {
        boolean: true,
        number: true,
        string: true,
    };
    function deepCopy(object, frozen) {
        // Opaque objects are not mutable, so safe to copy by assignment
        if (object === undefined || object === null || opaque[typeof object]) {
            return object;
        }
        // Arrays are mutable, so we need to create a copy
        if (Array.isArray(object)) {
            var result = object.map(function (item) { return deepCopy(item, frozen); });
            if (frozen) {
                Object.freeze(result);
            }
            return result;
        }
        if (typeof object === 'object') {
            // Some internal objects, which are already immutable
            if (isType(object, 'BigNumber')) {
                return object;
            }
            if (isType(object, 'Description')) {
                return object;
            }
            if (isType(object, 'Indexed')) {
                return object;
            }
            var result = {};
            // tslint:disable-next-line: forin
            for (var key in object) {
                var value = object[key];
                if (value === undefined) {
                    continue;
                }
                defineReadOnly(result, key, deepCopy(value, frozen));
            }
            if (frozen) {
                Object.freeze(result);
            }
            return result;
        }
        // The function type is also immutable, so safe to copy by assignment
        if (typeof object === 'function') {
            return object;
        }
        throw new Error('Cannot deepCopy ' + typeof object);
    }
    // tslint:disable-next-line: max-classes-per-file
    var AbiCoder = /** @class */ (function () {
        function AbiCoder(coerceFunc) {
            crypto.checkNew(this, AbiCoder);
            if (!coerceFunc) {
                coerceFunc = defaultCoerceFunc;
            }
            this.coerceFunc = coerceFunc;
        }
        AbiCoder.prototype.encode = function (types, values) {
            var _this = this;
            if (types.length !== values.length) {
                crypto.throwError('types/values length mismatch', crypto.INVALID_ARGUMENT, {
                    count: { types: types.length, values: values.length },
                    value: { types: types, values: values },
                });
            }
            var coders = [];
            types.forEach(function (type) {
                // Convert types to type objects
                //   - "uint foo" => { type: "uint", name: "foo" }
                //   - "tuple(uint, uint)" => { type: "tuple", components: [ { type: "uint" }, { type: "uint" }, ] }
                var typeObject = null;
                // tslint:disable-next-line: prefer-conditional-expression
                if (typeof type === 'string') {
                    typeObject = parseParam(type);
                }
                else {
                    typeObject = type;
                }
                coders.push(getParamCoder(_this.coerceFunc, typeObject));
            }, this);
            var encodedArray = new CoderTuple(this.coerceFunc, coders, '_').encode(values);
            return crypto.hexlify(encodedArray);
        };
        AbiCoder.prototype.decode = function (types, data) {
            var _this = this;
            var coders = [];
            types.forEach(function (type) {
                // See encode for details
                var typeObject = null;
                // tslint:disable-next-line: prefer-conditional-expression
                if (typeof type === 'string') {
                    typeObject = parseParam(type);
                }
                else {
                    typeObject = deepCopy(type);
                }
                coders.push(getParamCoder(_this.coerceFunc, typeObject));
            }, this);
            var result = new CoderTuple(this.coerceFunc, coders, '_').decode(crypto.arrayify(data) || new Uint8Array(), 0).value;
            return result;
        };
        return AbiCoder;
    }());
    var defaultAbiCoder = new AbiCoder();

    var jsonInterfaceMethodToString = function (json) {
        if (utils.isObject(json) && json.name && json.name.includes('(')) {
            return json.name;
        }
        return json.name + "(" + flattenTypes(false, json.inputs).join(',') + ")";
    };
    var flattenTypes = function (includeTuple, puts) {
        // console.log("entered _flattenTypes. inputs/outputs: " + puts)
        var types = [];
        puts.forEach(function (param) {
            if (typeof param.components === 'object') {
                if (param.type.substring(0, 5) !== 'tuple') {
                    throw new Error('components found but type is not tuple; report on GitHub');
                }
                var suffix = '';
                var arrayBracket = param.type.indexOf('[');
                if (arrayBracket >= 0) {
                    suffix = param.type.substring(arrayBracket);
                }
                var result = flattenTypes(includeTuple, param.components);
                // console.log("result should have things: " + result)
                if (utils.isArray(result) && includeTuple) {
                    // console.log("include tuple word, and its an array. joining...: " + result.types)
                    types.push("tuple(" + result.join(',') + ")" + suffix);
                }
                else if (!includeTuple) {
                    // console.log("don't include tuple, but its an array. joining...: " + result)
                    types.push("(" + result.join(',') + ")" + suffix);
                }
                else {
                    // console.log("its a single type within a tuple: " + result.types)
                    types.push("(" + result + ")");
                }
            }
            else {
                // console.log("its a type and not directly in a tuple: " + param.type)
                types.push(param.type);
            }
        });
        return types;
    };
    function bnToString(result) {
        if (crypto.BN.isBN(result)) {
            return result.toString();
        }
        else {
            return result;
        }
    }

    var AbiCoderClass = /** @class */ (function () {
        function AbiCoderClass(coder) {
            this.coder = coder;
        }
        AbiCoderClass.prototype.encodeFunctionSignature = function (functionName) {
            if (utils.isObject(functionName)) {
                functionName = jsonInterfaceMethodToString(functionName);
            }
            var result = crypto.keccak256(toUtf8Bytes(functionName));
            return result.slice(0, 10);
        };
        AbiCoderClass.prototype.encodeEventSignature = function (functionName) {
            if (utils.isObject(functionName)) {
                functionName = jsonInterfaceMethodToString(functionName);
            }
            var result = crypto.keccak256(toUtf8Bytes(functionName));
            return result;
        };
        AbiCoderClass.prototype.encodeParameter = function (types, param) {
            return this.encodeParameters([types], [param]);
        };
        AbiCoderClass.prototype.encodeParameters = function (types, params) {
            return this.coder.encode(types, params);
        };
        AbiCoderClass.prototype.encodeFunctionCall = function (jsonInterface, params) {
            return (this.encodeFunctionSignature(jsonInterface) +
                this.encodeParameters(jsonInterface.inputs, params).replace('0x', ''));
        };
        AbiCoderClass.prototype.decodeParameter = function (type, bytes) {
            return this.decodeParameters([type], bytes)[0];
        };
        AbiCoderClass.prototype.decodeParameters = function (outputs, bytes) {
            if (utils.isArray(outputs) && outputs.length === 0) {
                throw new Error('Empty outputs array given!');
            }
            if (!bytes || bytes === '0x' || bytes === '0X') {
                throw new Error("Invalid bytes string given: " + bytes);
            }
            var result = this.coder.decode(outputs, bytes);
            var returnValues = {};
            var decodedValue;
            if (utils.isArray(result)) {
                if (outputs.length > 1) {
                    outputs.forEach(function (output, i) {
                        decodedValue = result[i];
                        if (decodedValue === '0x') {
                            decodedValue = null;
                        }
                        returnValues[i] = bnToString(decodedValue);
                        if (utils.isObject(output) && output.name) {
                            returnValues[output.name] = bnToString(decodedValue);
                        }
                    });
                    return returnValues;
                }
                return bnToString(result);
            }
            if (utils.isObject(outputs[0]) && outputs[0].name) {
                returnValues[outputs[0].name] = bnToString(result);
            }
            returnValues[0] = bnToString(result);
            return returnValues;
        };
        AbiCoderClass.prototype.decodeLog = function (inputs, data, topics) {
            var _this = this;
            if (data === void 0) { data = ''; }
            var returnValues = {};
            var topicCount = 0;
            var value;
            var nonIndexedInputKeys = [];
            var nonIndexedInputItems = [];
            if (!utils.isArray(topics)) {
                topics = [topics];
            }
            inputs.forEach(function (input, i) {
                if (input.indexed) {
                    if (input.type === 'string') {
                        return;
                    }
                    value = topics[topicCount];
                    if (_this.isStaticType(input.type)) {
                        value = _this.decodeParameter(input.type, topics[topicCount]);
                    }
                    returnValues[i] = bnToString(value);
                    returnValues[input.name] = bnToString(value);
                    topicCount++;
                    return;
                }
                nonIndexedInputKeys.push(i);
                nonIndexedInputItems.push(input);
            });
            if (data) {
                var values_1 = this.decodeParameters(nonIndexedInputItems, data);
                var decodedValue_1;
                nonIndexedInputKeys.forEach(function (itemKey, index) {
                    decodedValue_1 = values_1[index];
                    returnValues[itemKey] = bnToString(decodedValue_1);
                    returnValues[nonIndexedInputItems[index].name] = bnToString(decodedValue_1);
                });
            }
            return returnValues;
        };
        AbiCoderClass.prototype.isStaticType = function (type) {
            if (type === 'bytes') {
                return false;
            }
            if (type === 'string') {
                return false;
            }
            if (type.indexOf('[') && type.slice(type.indexOf('[')).length === 2) {
                return false;
            }
            return true;
        };
        return AbiCoderClass;
    }());

    function AbiCoder$1() {
        return new AbiCoderClass(new AbiCoder());
    }

    var AbiItem = /** @class */ (function () {
        // constructor
        function AbiItem(abiItem) {
            this.abiItem = abiItem;
            this.signature = this.abiItem.signature;
            this.name = this.abiItem.name;
            this.payable = this.abiItem.payable;
            this.anonymous = this.abiItem.anonymous;
            this.type = this.abiItem.type;
            this.inputs = this.abiItem.inputs;
            this.outputs = this.abiItem.outputs;
            this.contractMethodParameters = [];
        }
        AbiItem.prototype.getInputLength = function () {
            if (utils.isArray(this.abiItem.inputs)) {
                return this.abiItem.inputs.length;
            }
            return 0;
        };
        AbiItem.prototype.getInputs = function () {
            if (utils.isArray(this.abiItem.inputs)) {
                return this.abiItem.inputs;
            }
            return [];
        };
        AbiItem.prototype.getOutputs = function () {
            if (utils.isArray(this.abiItem.outputs)) {
                return this.abiItem.outputs;
            }
            return [];
        };
        AbiItem.prototype.getIndexedInputs = function () {
            return this.getInputs().filter(function (input) {
                return input.indexed === true;
            });
        };
        AbiItem.prototype.isOfType = function (type) {
            return this.abiItem.type === type;
        };
        return AbiItem;
    }());

    var AbiModel = /** @class */ (function () {
        function AbiModel(mappedAbi) {
            this.abi = mappedAbi;
        }
        AbiModel.prototype.getMethod = function (name) {
            if (this.hasMethod(name)) {
                return this.abi.methods[name];
            }
            return false;
        };
        AbiModel.prototype.getMethods = function () {
            return this.abi.methods;
        };
        AbiModel.prototype.getEvent = function (name) {
            if (this.hasEvent(name)) {
                return this.abi.events[name];
            }
            return false;
        };
        AbiModel.prototype.getEvents = function () {
            return this.abi.events;
        };
        AbiModel.prototype.getEventBySignature = function (signature) {
            var _this = this;
            var event;
            Object.keys(this.abi.events).forEach(function (key) {
                if (_this.abi.events[key].signature === signature) {
                    event = _this.abi.events[key];
                }
            });
            return event;
        };
        AbiModel.prototype.hasMethod = function (name) {
            return typeof this.abi.methods[name] !== 'undefined';
        };
        AbiModel.prototype.hasEvent = function (name) {
            return typeof this.abi.events[name] !== 'undefined';
        };
        return AbiModel;
    }());

    var abiMapper = function (abi, abiCoder) {
        var mappedAbiItems = {
            methods: {},
            events: {},
        };
        var hasConstructor = false;
        abi.forEach(function (abiItem) {
            abiItem.constant = isConstant(abiItem);
            abiItem.payable = isPayable(abiItem);
            if (abiItem.name) {
                abiItem.funcName = jsonInterfaceMethodToString(abiItem);
            }
            var abiItemModel;
            if (abiItem.type === 'function') {
                abiItem.signature = abiCoder.encodeFunctionSignature(abiItem.funcName);
                abiItemModel = new AbiItem(abiItem);
                // Check if an method already exists with this name and if it exists than create an array and push this abiItem
                // into it. This will be used if there are methods with the same name but with different arguments.
                if (!mappedAbiItems.methods[abiItem.name]) {
                    mappedAbiItems.methods[abiItem.name] = abiItemModel;
                }
                else {
                    if (utils.isArray(mappedAbiItems.methods[abiItem.name])) {
                        mappedAbiItems.methods[abiItem.name].push(abiItemModel);
                    }
                    else {
                        mappedAbiItems.methods[abiItem.name] = [
                            mappedAbiItems.methods[abiItem.name],
                            abiItemModel,
                        ];
                    }
                }
                mappedAbiItems.methods[abiItem.signature] = abiItemModel;
                mappedAbiItems.methods[abiItem.funcName] = abiItemModel;
                return;
            }
            if (abiItem.type === 'event') {
                abiItem.signature = abiCoder.encodeEventSignature(abiItem.funcName);
                abiItemModel = new AbiItem(abiItem);
                if (!mappedAbiItems.events[abiItem.name] ||
                    mappedAbiItems.events[abiItem.name].name === 'bound ') {
                    mappedAbiItems.events[abiItem.name] = abiItemModel;
                }
                mappedAbiItems.events[abiItem.signature] = abiItemModel;
                mappedAbiItems.events[abiItem.funcName] = abiItemModel;
            }
            if (abiItem.type === 'constructor') {
                abiItem.signature = abiItem.type;
                // tslint:disable-next-line: no-string-literal
                mappedAbiItems.methods['contractConstructor'] = new AbiItem(abiItem);
                hasConstructor = true;
            }
        });
        if (!hasConstructor) {
            // tslint:disable-next-line: no-string-literal
            mappedAbiItems.methods['contractConstructor'] = new AbiItem({
                inputs: [],
                payable: false,
                constant: false,
                type: 'constructor',
            });
        }
        return new AbiModel(mappedAbiItems);
    };
    var isConstant = function (abiItem) {
        return (abiItem.stateMutability === 'view' ||
            abiItem.stateMutability === 'pure' ||
            abiItem.constant);
    };
    var isPayable = function (abiItem) {
        return abiItem.stateMutability === 'payable' || abiItem.payable;
    };

    var methodEncoder = function (abiCoder, abiItemModel, deployData) {
        var encodedParameters = abiCoder.encodeParameters(abiItemModel.getInputs(), abiItemModel.contractMethodParameters);
        if (encodedParameters.startsWith('0x')) {
            encodedParameters = encodedParameters.slice(2);
        }
        if (abiItemModel.isOfType('constructor')) {
            if (!deployData) {
                throw new Error('The contract has no contract data option set. This is necessary to append the constructor parameters.');
            }
            return deployData + encodedParameters;
        }
        if (abiItemModel.isOfType('function')) {
            return abiItemModel.signature + encodedParameters;
        }
        return encodedParameters;
    };
    var eventFilterEncoder = function (abiCoder, abiItemModel, filter) {
        var topics = [];
        abiItemModel.getIndexedInputs().forEach(function (input) {
            if (filter[input.name]) {
                var filterItem = filter[input.name];
                if (utils.isArray(filterItem)) {
                    filterItem = filterItem.map(function (item) {
                        return abiCoder.encodeParameter(input.type, item);
                    });
                    topics.push(filterItem);
                    return;
                }
                topics.push(abiCoder.encodeParameter(input.type, filterItem));
                return;
            }
            topics.push(null);
        });
        return topics;
    };

    var ContractStatus;
    (function (ContractStatus) {
        ContractStatus["INITIALISED"] = "initialised";
        ContractStatus["TESTED"] = "tested";
        ContractStatus["ERROR"] = "error";
        ContractStatus["SIGNED"] = "signed";
        ContractStatus["SENT"] = "sent";
        ContractStatus["REJECTED"] = "rejected";
        ContractStatus["DEPLOYED"] = "deployed";
        ContractStatus["CALLED"] = "called";
    })(ContractStatus || (ContractStatus = {}));

    // todo: have to judge if it is contractConstructor
    var ContractMethod = /** @class */ (function () {
        function ContractMethod(methodKey, params, abiItem, contract) {
            this.methodKey = methodKey;
            this.contract = contract;
            this.wallet = contract.wallet;
            this.params = params;
            this.abiItem = abiItem;
            this.transaction = this.createTransaction();
            // this.addEventListeners();
        }
        ContractMethod.prototype.send = function (params) {
            var _this = this;
            try {
                this.transaction = this.transaction.map(function (tx) {
                    return __assign({}, tx, params);
                });
                this.signTransaction().then(function (signed) {
                    _this.sendTransaction(signed).then(function (sent) {
                        var _a = __read(sent, 2), txn = _a[0], id = _a[1];
                        _this.transaction = txn;
                        _this.contract.transaction = _this.transaction;
                        _this.confirm(id).then(function () {
                            _this.transaction.emitter.resolve(_this.contract);
                        });
                    });
                });
                return this.transaction.emitter;
            }
            catch (error) {
                throw error;
            }
        };
        ContractMethod.prototype.call = function (options, blockNumber) {
            if (blockNumber === void 0) { blockNumber = 'latest'; }
            return __awaiter(this, void 0, void 0, function () {
                var nonce_1, _a, gasLimit_1, _b, result, error_1;
                var _this = this;
                return __generator(this, function (_c) {
                    switch (_c.label) {
                        case 0:
                            _c.trys.push([0, 6, , 7]);
                            _a = network.getResultForData;
                            return [4 /*yield*/, this.wallet.messenger.send("hmy_getTransactionCount" /* GetTransactionCount */, [
                                    this.wallet.signer ? this.wallet.signer.address : options.from,
                                    blockNumber,
                                ])];
                        case 1:
                            nonce_1 = _a.apply(void 0, [_c.sent()]);
                            if (!options) return [3 /*break*/, 2];
                            gasLimit_1 = options.gas || options.gasLimit;
                            return [3 /*break*/, 4];
                        case 2:
                            _b = utils.hexToBN;
                            return [4 /*yield*/, this.estimateGas()];
                        case 3:
                            gasLimit_1 = _b.apply(void 0, [_c.sent()]);
                            _c.label = 4;
                        case 4:
                            this.transaction = this.transaction.map(function (tx) {
                                return __assign({}, tx, options, { from: options
                                        ? options.from
                                        : _this.wallet.signer
                                            ? _this.wallet.signer.address
                                            : tx.from, gasPrice: options ? options.gasPrice : tx.gasPrice, gasLimit: gasLimit_1 || tx.gasLimit, nonce: Number.parseInt(utils.hexToNumber(nonce_1), 10) });
                            });
                            return [4 /*yield*/, this.wallet.messenger.send("hmy_call" /* Call */, [
                                    this.transaction.txPayload,
                                    blockNumber,
                                ])];
                        case 5:
                            result = _c.sent();
                            if (result.isError()) {
                                throw result.message;
                            }
                            else if (result.isResult()) {
                                if (result.result === null) {
                                    return [2 /*return*/, this.afterCall(undefined)];
                                }
                                else {
                                    return [2 /*return*/, this.afterCall(result.result)];
                                }
                            }
                            return [3 /*break*/, 7];
                        case 6:
                            error_1 = _c.sent();
                            throw error_1;
                        case 7: return [2 /*return*/];
                    }
                });
            });
        };
        ContractMethod.prototype.estimateGas = function () {
            return __awaiter(this, void 0, void 0, function () {
                var result, _a, error_2;
                return __generator(this, function (_b) {
                    switch (_b.label) {
                        case 0:
                            _b.trys.push([0, 2, , 3]);
                            _a = network.getResultForData;
                            return [4 /*yield*/, this.wallet.messenger.send("hmy_estimateGas" /* EstimateGas */, [
                                    {
                                        to: this.transaction.txParams.to,
                                        data: this.transaction.txParams.data,
                                    },
                                ])];
                        case 1:
                            result = _a.apply(void 0, [_b.sent()]);
                            if (result.responseType === 'error') {
                                throw result.message;
                            }
                            else if (result.responseType === 'raw') {
                                throw new Error('Get estimateGas fail');
                            }
                            else {
                                return [2 /*return*/, result];
                            }
                            return [3 /*break*/, 3];
                        case 2:
                            error_2 = _b.sent();
                            throw error_2;
                        case 3: return [2 /*return*/];
                    }
                });
            });
        };
        ContractMethod.prototype.encodeABI = function () {
            return methodEncoder(this.contract.abiCoder, this.abiItem, this.contract.data);
        };
        ContractMethod.prototype.signTransaction = function () {
            return __awaiter(this, void 0, void 0, function () {
                var signed, error_3;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            _a.trys.push([0, 2, , 3]);
                            return [4 /*yield*/, this.wallet.signTransaction(this.transaction, this.wallet.signer, undefined, true, 'rlp', 'pending')];
                        case 1:
                            signed = _a.sent();
                            this.contract.address = transaction.TransactionFactory.getContractAddress(signed);
                            this.contract.setStatus(ContractStatus.SIGNED);
                            return [2 /*return*/, signed];
                        case 2:
                            error_3 = _a.sent();
                            throw error_3;
                        case 3: return [2 /*return*/];
                    }
                });
            });
        };
        ContractMethod.prototype.sendTransaction = function (signed) {
            return __awaiter(this, void 0, void 0, function () {
                var result, error_4;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            _a.trys.push([0, 2, , 3]);
                            return [4 /*yield*/, signed.sendTransaction()];
                        case 1:
                            result = _a.sent();
                            this.contract.setStatus(ContractStatus.SENT);
                            return [2 /*return*/, result];
                        case 2:
                            error_4 = _a.sent();
                            throw error_4;
                        case 3: return [2 /*return*/];
                    }
                });
            });
        };
        ContractMethod.prototype.confirm = function (id) {
            return __awaiter(this, void 0, void 0, function () {
                var result, error_5;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            _a.trys.push([0, 2, , 3]);
                            return [4 /*yield*/, this.transaction.confirm(id)];
                        case 1:
                            result = _a.sent();
                            if (result.receipt && result.receipt.status === '0x1') {
                                if (this.methodKey === 'contractConstructor') {
                                    this.contract.setStatus(ContractStatus.DEPLOYED);
                                }
                                else {
                                    this.contract.setStatus(ContractStatus.CALLED);
                                }
                            }
                            else {
                                this.contract.setStatus(ContractStatus.REJECTED);
                            }
                            return [3 /*break*/, 3];
                        case 2:
                            error_5 = _a.sent();
                            throw error_5;
                        case 3: return [2 /*return*/];
                    }
                });
            });
        };
        ContractMethod.prototype.createTransaction = function () {
            if (this.wallet.messenger) {
                if (this.methodKey === 'contractConstructor') {
                    // tslint:disable-next-line: no-string-literal
                    this.contract.data = this.params[0]['data'] || '0x';
                    this.abiItem.contractMethodParameters =
                        // tslint:disable-next-line: no-string-literal
                        this.params[0]['arguments'] || [];
                }
                else {
                    this.abiItem.contractMethodParameters = this.params || [];
                }
                var txObject = __assign({}, this.params[0], { to: crypto.getAddress(this.contract.address).checksum, data: this.encodeABI() });
                var result = new transaction.TransactionFactory(this.wallet.messenger).newTx(txObject);
                return result;
            }
            else {
                throw new Error('Messenger is not found');
            }
        };
        ContractMethod.prototype.afterCall = function (response) {
            if (!response || response === '0x') {
                return null;
            }
            var outputs = this.abiItem.getOutputs();
            if (outputs.length > 1) {
                return this.contract.abiCoder.decodeParameters(outputs, response);
            }
            return this.contract.abiCoder.decodeParameter(outputs[0], response);
            // return outputs;
        };
        return ContractMethod;
    }());

    var MethodFactory = /** @class */ (function () {
        // constructor
        function MethodFactory(contract) {
            this.contract = contract;
            this.abiModel = this.contract.abiModel;
            this.abiCoder = this.contract.abiCoder;
            this.methodKeys = this.mapMethodKeys();
        }
        MethodFactory.prototype.addMethodsToContract = function () {
            var _this = this;
            this.methodKeys.forEach(function (key) {
                var newObject = {};
                newObject[key] = function () {
                    var params = [];
                    for (var _i = 0; _i < arguments.length; _i++) {
                        params[_i] = arguments[_i];
                    }
                    return new ContractMethod(key, params, _this.abiModel.getMethod(key), _this.contract);
                };
                Object.assign(_this.contract.methods, newObject);
            });
            return this.contract;
        };
        /**
         * @function mapMethodKeys
         * @return {string[]} {description}
         */
        MethodFactory.prototype.mapMethodKeys = function () {
            return Object.keys(this.abiModel.abi.methods);
        };
        return MethodFactory;
    }());

    var decode = function (abiCoder, abiItemModel, response) {
        var argumentTopics = response.topics;
        if (!abiItemModel.anonymous) {
            argumentTopics = response.topics.slice(1);
        }
        if (response.data === '0x') {
            response.data = null;
        }
        response.returnValues = abiCoder.decodeLog(abiItemModel.getInputs(), response.data, argumentTopics);
        response.event = abiItemModel.name;
        response.signature = abiItemModel.signature;
        response.raw = {
            data: response.data,
            topics: response.topics,
        };
        if (abiItemModel.anonymous || !response.topics[0]) {
            response.signature = null;
        }
        delete response.data;
        delete response.topics;
        return response;
    };

    var inputLogFormatter = function (options) {
        if (options.fromBlock) {
            options.fromBlock = inputBlockNumberFormatter(options.fromBlock);
        }
        if (options.toBlock) {
            options.toBlock = inputBlockNumberFormatter(options.toBlock);
        }
        // make sure topics, get converted to hex
        options.topics = options.topics || [];
        options.topics = options.topics.map(function (topic) {
            return utils.isArray(topic) ? topic.map(toTopic) : toTopic(topic);
        });
        if (options.address) {
            if (utils.isArray(options.address)) {
                options.address = options.address.map(function (addr) {
                    return inputAddressFormatter(addr);
                });
            }
            else {
                options.address = inputAddressFormatter(options.address);
            }
        }
        return options;
    };
    /**
     * Formats the output of a log
     *
     * @method outputLogFormatter
     *
     * @param {Object} log object
     *
     * @returns {Object} log
     */
    var outputLogFormatter = function (log) {
        // generate a custom log id
        if (typeof log.blockHash === 'string' &&
            typeof log.transactionHash === 'string' &&
            typeof log.logIndex === 'string') {
            var shaId = crypto.keccak256('0x' +
                log.blockHash.replace('0x', '') +
                log.transactionHash.replace('0x', '') +
                log.logIndex.replace('0x', ''));
            shaId.replace('0x', '').substr(0, 8);
            log.id = "log_" + shaId;
        }
        else if (!log.id) {
            log.id = null;
        }
        if (log.blockNumber !== null) {
            log.blockNumber = utils.hexToBN(log.blockNumber).toNumber();
        }
        if (log.transactionIndex !== null) {
            log.transactionIndex = utils.hexToBN(log.transactionIndex).toNumber();
        }
        if (log.logIndex !== null) {
            log.logIndex = utils.hexToBN(log.logIndex).toNumber();
        }
        if (log.address) {
            log.address = crypto.toChecksumAddress(log.address);
        }
        return log;
    };
    var inputBlockNumberFormatter = function (blockNumber) {
        if (blockNumber === undefined ||
            blockNumber === null ||
            isPredefinedBlockNumber(blockNumber)) {
            return blockNumber;
        }
        if (crypto.isHexString(blockNumber)) {
            if (utils.isString(blockNumber)) {
                return blockNumber.toLowerCase();
            }
            return blockNumber;
        }
        return utils.numberToHex(blockNumber);
    };
    var isPredefinedBlockNumber = function (blockNumber) {
        return (blockNumber === 'latest' ||
            blockNumber === 'pending' ||
            blockNumber === 'earliest');
    };
    var inputAddressFormatter = function (address) {
        if (utils.isAddress(address)) {
            return "0x" + address.toLowerCase().replace('0x', '');
        }
        throw new Error("Provided address \"" + address + "\" is invalid, the capitalization checksum test failed, or its an indrect IBAN address which can't be converted.");
    };
    var toTopic = function (value) {
        if (value === null || typeof value === 'undefined') {
            return null;
        }
        value = String(value);
        if (value.indexOf('0x') === 0) {
            return value;
        }
        return crypto.hexlify(toUtf8Bytes(value));
    };

    var EventMethod = /** @class */ (function (_super) {
        __extends(EventMethod, _super);
        function EventMethod(methodKey, params, abiItem, contract) {
            var _this = _super.call(this, inputLogFormatter(params), contract.wallet.messenger) || this;
            _this.methodKey = methodKey;
            _this.contract = contract;
            _this.params = params;
            _this.abiItem = abiItem;
            return _this;
            // this.subscribe();
        }
        // call() {}
        // estimateGas() {}
        // encodeABI() {}
        EventMethod.prototype.onNewSubscriptionItem = function (subscriptionItem) {
            var formatted = outputLogFormatter(subscriptionItem.method !== undefined
                ? subscriptionItem.params.result
                : subscriptionItem);
            var log = decode(this.contract.abiCoder, this.abiItem, formatted);
            if (log.removed && this.emitter) {
                this.emitter.emit('changed', log);
            }
            return log;
        };
        return EventMethod;
    }(network.LogSub));

    var EventFactory = /** @class */ (function () {
        // constructor
        function EventFactory(contract) {
            this.contract = contract;
            this.abiModel = this.contract.abiModel;
            this.abiCoder = this.contract.abiCoder;
            this.eventKeys = this.mapEventKeys();
        }
        EventFactory.prototype.addEventsToContract = function () {
            var _this = this;
            this.eventKeys.forEach(function (key) {
                var newObject = {};
                newObject[key] = function (params) {
                    return new EventMethod(key, 
                    // params,
                    _this.map(_this.abiModel, _this.contract, params), _this.abiModel.getEvent(key), _this.contract);
                };
                Object.assign(_this.contract.events, newObject);
            });
            return this.contract;
        };
        /**
         * @function mapMethodKeys
         * @return {string[]} {description}
         */
        EventFactory.prototype.mapEventKeys = function () {
            return Object.keys(this.abiModel.abi.events);
        };
        EventFactory.prototype.map = function (abiItemModel, contract, options) {
            if (!options) {
                options = {};
            }
            if (!utils.isArray(options.topics)) {
                options.topics = [];
            }
            if (typeof options.fromBlock !== 'undefined') {
                options.fromBlock = inputBlockNumberFormatter(options.fromBlock);
            }
            // else if (contract.defaultBlock !== null) {
            //   options.fromBlock = contract.defaultBlock;
            // }
            if (typeof options.toBlock !== 'undefined') {
                options.toBlock = inputBlockNumberFormatter(options.toBlock);
            }
            if (typeof options.filter !== 'undefined') {
                options.topics = options.topics.concat(eventFilterEncoder(this.abiCoder, abiItemModel, options.filter));
                delete options.filter;
            }
            if (!abiItemModel.anonymous) {
                options.topics.unshift(abiItemModel.signature);
            }
            if (!options.address) {
                options.address = contract.address;
            }
            return options;
        };
        return EventFactory;
    }());

    // class Contract
    var Contract = /** @class */ (function () {
        function Contract(abi, address, options, wallet, status) {
            if (abi === void 0) { abi = []; }
            if (address === void 0) { address = '0x'; }
            if (options === void 0) { options = {}; }
            if (status === void 0) { status = ContractStatus.INITIALISED; }
            // super();
            this.abiCoder = AbiCoder$1();
            this.abiModel = abiMapper(abi, this.abiCoder);
            this.options = options;
            this.address = this.options.address || address;
            this.wallet = wallet;
            this.methods = {};
            this.events = {};
            this.runMethodFactory();
            this.runEventFactory();
            this.status = status;
            // tslint:disable-next-line: no-unused-expression
        }
        Contract.prototype.isInitialised = function () {
            return this.status === ContractStatus.INITIALISED;
        };
        Contract.prototype.isSigned = function () {
            return this.status === ContractStatus.SIGNED;
        };
        Contract.prototype.isSent = function () {
            return this.status === ContractStatus.SENT;
        };
        Contract.prototype.isDeployed = function () {
            return this.status === ContractStatus.DEPLOYED;
        };
        Contract.prototype.isRejected = function () {
            return this.status === ContractStatus.REJECTED;
        };
        Contract.prototype.isCalled = function () {
            return this.status === ContractStatus.CALLED;
        };
        Contract.prototype.setStatus = function (status) {
            this.status = status;
        };
        Object.defineProperty(Contract.prototype, "jsonInterface", {
            get: function () {
                return this.abiModel;
            },
            set: function (value) {
                this.abiModel = abiMapper(value, this.abiCoder);
                this.runMethodFactory();
                this.runEventFactory();
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(Contract.prototype, "address", {
            get: function () {
                return this.options.address || this.address;
            },
            set: function (value) {
                this.options.address = value;
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(Contract.prototype, "data", {
            get: function () {
                return this.options.data;
            },
            set: function (value) {
                this.options.data = value;
            },
            enumerable: true,
            configurable: true
        });
        // deploy
        Contract.prototype.deploy = function (options) {
            return this.methods.contractConstructor(options);
        };
        Contract.prototype.runMethodFactory = function () {
            return new MethodFactory(this).addMethodsToContract();
        };
        Contract.prototype.runEventFactory = function () {
            return new EventFactory(this).addEventsToContract();
        };
        return Contract;
    }());

    var ContractFactory = /** @class */ (function () {
        function ContractFactory(wallet) {
            this.wallet = wallet;
        }
        ContractFactory.prototype.createContract = function (abi, address, options) {
            return new Contract(abi, address, options, this.wallet);
        };
        return ContractFactory;
    }());

    exports.toUtf8Bytes = toUtf8Bytes;
    exports.toUtf8String = toUtf8String;
    exports.formatBytes32String = formatBytes32String;
    exports.parseBytes32String = parseBytes32String;
    exports.Contract = Contract;
    exports.ContractFactory = ContractFactory;
    exports.AbiCoder = AbiCoder$1;

    Object.defineProperty(exports, '__esModule', { value: true });

})));
//# sourceMappingURL=index.umd.js.map
