"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        if (typeof b !== "function" && b !== null)
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(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);
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
var __values = (this && this.__values) || function(o) {
    var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
    if (m) return m.call(o);
    if (o && typeof o.length === "number") return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
    throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __read = (this && this.__read) || function (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;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from) {
    for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
        to[j] = from[i];
    return to;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isTypeEqual = exports.isDataValid = exports.isContractValid = exports.isTypeAnnotationsValid = exports.assertTypesEqual = exports.functionType = exports.assertDataValid = exports.assertContractValid = exports.contractEntryPoints = exports.contractEntryPoint = exports.contractSection = exports.assertTypeAnnotationsValid = exports.MichelsonInstructionError = void 0;
var michelson_types_1 = require("./michelson-types");
var utils_1 = require("./utils");
var base58_1 = require("./base58");
var binary_1 = require("./binary");
var michelson_validator_1 = require("./michelson-validator");
var MichelsonInstructionError = /** @class */ (function (_super) {
    __extends(MichelsonInstructionError, _super);
    /**
     * @param val Value of a type node caused the error
     * @param stackState Current stack state
     * @param message An error message
     */
    function MichelsonInstructionError(val, stackState, message) {
        var _this = _super.call(this, val, message) || this;
        _this.stackState = stackState;
        Object.setPrototypeOf(_this, MichelsonInstructionError.prototype);
        return _this;
    }
    return MichelsonInstructionError;
}(utils_1.MichelsonError));
exports.MichelsonInstructionError = MichelsonInstructionError;
// 'sequence as a pair' edo syntax helpers
function typeID(t) {
    return Array.isArray(t) ? "pair" : t.prim;
}
function typeArgs(t) {
    return ("prim" in t ? t.args : t);
}
function assertScalarTypesEqual(a, b, field) {
    if (field === void 0) { field = false; }
    if (typeID(a) !== typeID(b)) {
        throw new utils_1.MichelsonTypeError(a, undefined, "types mismatch: " + typeID(a) + " != " + typeID(b));
    }
    var ann = [utils_1.unpackAnnotations(a), utils_1.unpackAnnotations(b)];
    if (ann[0].t && ann[1].t && ann[0].t[0] !== ann[1].t[0]) {
        throw new utils_1.MichelsonTypeError(a, undefined, typeID(a) + ": type names mismatch: " + ann[0].t[0] + " != " + ann[1].t[0]);
    }
    if (field &&
        (ann[0].f && ann[1].f && ann[0].f[0] !== ann[1].f[0])) {
        throw new utils_1.MichelsonTypeError(a, undefined, typeID(a) + ": field names mismatch: " + ann[0].f[0] + " != " + ann[1].f);
    }
    if (utils_1.isPairType(a)) {
        var aArgs = utils_1.unpackComb("pair", a);
        var bArgs = utils_1.unpackComb("pair", b);
        assertScalarTypesEqual(aArgs.args[0], bArgs.args[0], true);
        assertScalarTypesEqual(aArgs.args[1], bArgs.args[1], true);
        return;
    }
    switch (a.prim) {
        case "option":
        case "list":
        case "contract":
        case "set":
        case "ticket":
            assertScalarTypesEqual(a.args[0], b.args[0]);
            break;
        case "or":
            assertScalarTypesEqual(a.args[0], b.args[0], true);
            assertScalarTypesEqual(a.args[1], b.args[1], true);
            break;
        case "lambda":
        case "map":
        case "big_map":
            assertScalarTypesEqual(a.args[0], b.args[0]);
            assertScalarTypesEqual(a.args[1], b.args[1]);
            break;
        case "sapling_state":
        case "sapling_transaction":
            if (parseInt(a.args[0].int, 10) !== parseInt(b.args[0].int, 10)) {
                throw new utils_1.MichelsonTypeError(a, undefined, typeID(a) + ": type argument mismatch: " + a.args[0].int + " != " + b.args[0].int);
            }
    }
}
function assertStacksEqual(a, b) {
    if (a.length !== b.length) {
        throw new utils_1.MichelsonTypeError(a, undefined, "stack length mismatch: " + a.length + " != " + b.length);
    }
    for (var i = 0; i < a.length; i++) {
        assertScalarTypesEqual(a[i], b[i]);
    }
}
function assertTypeAnnotationsValid(t, field) {
    var e_1, _a, e_2, _b;
    var _c, _d, _e;
    if (field === void 0) { field = false; }
    if (!Array.isArray(t)) {
        var ann = utils_1.unpackAnnotations(t);
        if ((((_c = ann.t) === null || _c === void 0 ? void 0 : _c.length) || 0) > 1) {
            throw new utils_1.MichelsonTypeError(t, undefined, t.prim + ": at most one type annotation allowed: " + t.annots);
        }
        if (field) {
            if ((((_d = ann.f) === null || _d === void 0 ? void 0 : _d.length) || 0) > 1) {
                throw new utils_1.MichelsonTypeError(t, undefined, t.prim + ": at most one field annotation allowed: " + t.annots);
            }
        }
        else {
            if ((((_e = ann.f) === null || _e === void 0 ? void 0 : _e.length) || 0) > 0) {
                throw new utils_1.MichelsonTypeError(t, undefined, t.prim + ": field annotations aren't allowed: " + t.annots);
            }
        }
    }
    if (utils_1.isPairType(t)) {
        var args = typeArgs(t);
        try {
            for (var args_1 = __values(args), args_1_1 = args_1.next(); !args_1_1.done; args_1_1 = args_1.next()) {
                var a = args_1_1.value;
                assertTypeAnnotationsValid(a, true);
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (args_1_1 && !args_1_1.done && (_a = args_1.return)) _a.call(args_1);
            }
            finally { if (e_1) throw e_1.error; }
        }
        return;
    }
    switch (t.prim) {
        case "option":
        case "list":
        case "contract":
        case "set":
            assertTypeAnnotationsValid(t.args[0]);
            break;
        case "or":
            try {
                for (var _f = __values(t.args), _g = _f.next(); !_g.done; _g = _f.next()) {
                    var a = _g.value;
                    assertTypeAnnotationsValid(a, true);
                }
            }
            catch (e_2_1) { e_2 = { error: e_2_1 }; }
            finally {
                try {
                    if (_g && !_g.done && (_b = _f.return)) _b.call(_f);
                }
                finally { if (e_2) throw e_2.error; }
            }
            break;
        case "lambda":
        case "map":
        case "big_map":
            assertTypeAnnotationsValid(t.args[0]);
            assertTypeAnnotationsValid(t.args[1]);
    }
}
exports.assertTypeAnnotationsValid = assertTypeAnnotationsValid;
// Data integrity check
function compareMichelsonData(t, a, b) {
    if (utils_1.isPairType(t)) {
        if (utils_1.isPairData(a) && utils_1.isPairData(b)) {
            michelson_validator_1.assertDataListIfAny(a);
            michelson_validator_1.assertDataListIfAny(b);
            var tComb = utils_1.unpackComb("pair", t);
            var aComb = utils_1.unpackComb("Pair", a);
            var bComb = utils_1.unpackComb("Pair", b);
            var x = compareMichelsonData(tComb.args[0], aComb.args[0], bComb.args[0]);
            if (x !== 0) {
                return x;
            }
            return compareMichelsonData(tComb.args[0], aComb.args[1], bComb.args[1]);
        }
    }
    else {
        switch (t.prim) {
            case "int":
            case "nat":
            case "mutez":
                if (("int" in a) && ("int" in b)) {
                    return new utils_1.LongInteger(a.int).cmp(new utils_1.LongInteger(b.int));
                }
                break;
            case "string":
                if (("string" in a) && ("string" in b)) {
                    var x = a.string.localeCompare(b.string);
                    return x < 0 ? -1 : x > 0 ? 1 : 0;
                }
                break;
            case "bytes":
                if (("bytes" in a) && ("bytes" in b)) {
                    var aa = utils_1.parseBytes(a.bytes);
                    var bb = utils_1.parseBytes(b.bytes);
                    if (aa !== null && bb !== null) {
                        return utils_1.compareBytes(aa, bb);
                    }
                }
                break;
            case "bool":
                if (("prim" in a) && ("prim" in b) && (a.prim === "True" || a.prim === "False") && (b.prim === "True" || b.prim === "False")) {
                    return a.prim === b.prim ? 0 : a.prim === "False" ? -1 : 1;
                }
                break;
            case "key":
            case "key_hash":
            case "address":
            case "signature":
            case "chain_id":
                if (("string" in a || "bytes" in a) && ("string" in b || "bytes" in b)) {
                    return utils_1.compareBytes("string" in a ? base58_1.decodeBase58Check(a.string) : utils_1.parseBytes(a.bytes) || [], "string" in b ? base58_1.decodeBase58Check(b.string) : utils_1.parseBytes(b.bytes) || []);
                }
                break;
            case "timestamp":
                if ((("string" in a) || ("int" in a)) && (("string" in b) || ("int" in b))) {
                    var aa = utils_1.parseDate(a);
                    var bb = utils_1.parseDate(b);
                    if (aa !== null && bb !== null) {
                        var x = aa.valueOf() - bb.valueOf();
                        return x < 0 ? -1 : x > 0 ? 1 : 0;
                    }
                }
                break;
            case "unit":
                if (("prim" in a) && ("prim" in b) && a.prim === "Unit" && b.prim === "Unit") {
                    return 0;
                }
        }
    }
    // Unlikely, types are expected to be verified before the function call
    throw new utils_1.MichelsonTypeError(t, undefined, typeID(t) + ": not comparable values: " + JSON.stringify(a) + ", " + JSON.stringify(b));
}
// Simplified version of assertMichelsonInstruction() for previously validated data
function isFunction(d) {
    var e_3, _a;
    if (!Array.isArray(d)) {
        return false;
    }
    try {
        for (var d_1 = __values(d), d_1_1 = d_1.next(); !d_1_1.done; d_1_1 = d_1.next()) {
            var v = d_1_1.value;
            if (!(Array.isArray(v) && isFunction(v) ||
                ("prim" in v) && michelson_validator_1.isInstruction(v))) {
                return false;
            }
        }
    }
    catch (e_3_1) { e_3 = { error: e_3_1 }; }
    finally {
        try {
            if (d_1_1 && !d_1_1.done && (_a = d_1.return)) _a.call(d_1);
        }
        finally { if (e_3) throw e_3.error; }
    }
    return true;
}
function assertDataValidInternal(d, t, ctx) {
    var e_4, _a, e_5, _b;
    if (utils_1.isPairType(t)) {
        if (utils_1.isPairData(d)) {
            michelson_validator_1.assertDataListIfAny(d);
            var dc = utils_1.unpackComb("Pair", d);
            var tc = utils_1.unpackComb("pair", t);
            assertDataValidInternal(dc.args[0], tc.args[0], ctx);
            assertDataValidInternal(dc.args[1], tc.args[1], ctx);
            return;
        }
        throw new utils_1.MichelsonTypeError(t, d, "pair expected: " + JSON.stringify(d));
    }
    switch (t.prim) {
        // Atomic literals
        case "int":
            if (("int" in d) && utils_1.isDecimal(d.int)) {
                return;
            }
            throw new utils_1.MichelsonTypeError(t, d, "integer value expected: " + JSON.stringify(d));
        case "nat":
        case "mutez":
            if (("int" in d) && utils_1.isNatural(d.int)) {
                return;
            }
            throw new utils_1.MichelsonTypeError(t, d, "natural value expected: " + JSON.stringify(d));
        case "string":
            if ("string" in d) {
                return;
            }
            throw new utils_1.MichelsonTypeError(t, d, "string value expected: " + JSON.stringify(d));
        case "bytes":
        case "bls12_381_g1":
        case "bls12_381_g2":
            if ("bytes" in d && utils_1.parseBytes(d.bytes) !== null) {
                return;
            }
            throw new utils_1.MichelsonTypeError(t, d, "bytes value expected: " + JSON.stringify(d));
        case "bool":
            if (("prim" in d) && (d.prim === "True" || d.prim === "False")) {
                return;
            }
            throw new utils_1.MichelsonTypeError(t, d, "boolean value expected: " + JSON.stringify(d));
        case "key_hash":
            if (("string" in d) &&
                utils_1.checkDecodeTezosID(d.string, "ED25519PublicKeyHash", "SECP256K1PublicKeyHash", "P256PublicKeyHash") !== null) {
                return;
            }
            else if ("bytes" in d) {
                try {
                    binary_1.decodePublicKeyHashBytes(d);
                    return;
                }
                catch (err) {
                    // ignore message
                }
            }
            throw new utils_1.MichelsonTypeError(t, d, "key hash expected: " + JSON.stringify(d));
        case "timestamp":
            if ((("string" in d) || ("int" in d)) && utils_1.parseDate(d) !== null) {
                return;
            }
            throw new utils_1.MichelsonTypeError(t, d, "timestamp expected: " + JSON.stringify(d));
        case "address":
            if ("string" in d) {
                var address = d.string;
                var ep = d.string.indexOf("%");
                if (ep >= 0) {
                    // trim entry point
                    address = d.string.slice(0, ep);
                }
                if (utils_1.checkDecodeTezosID(address, "ED25519PublicKeyHash", "SECP256K1PublicKeyHash", "P256PublicKeyHash", "ContractHash") !== null) {
                    return;
                }
            }
            else if ("bytes" in d) {
                try {
                    binary_1.decodeAddressBytes(d);
                    return;
                }
                catch (err) {
                    // ignore message
                }
            }
            throw new utils_1.MichelsonTypeError(t, d, "address expected: " + JSON.stringify(d));
        case "key":
            if (("string" in d) &&
                utils_1.checkDecodeTezosID(d.string, "ED25519PublicKey", "SECP256K1PublicKey", "P256PublicKey") !== null) {
                return;
            }
            else if ("bytes" in d) {
                try {
                    binary_1.decodePublicKeyBytes(d);
                    return;
                }
                catch (err) {
                    // ignore message
                }
            }
            throw new utils_1.MichelsonTypeError(t, d, "public key expected: " + JSON.stringify(d));
        case "unit":
            if (("prim" in d) && d.prim === "Unit") {
                return;
            }
            throw new utils_1.MichelsonTypeError(t, d, "unit value expected: " + JSON.stringify(d));
        case "signature":
            if (("bytes" in d) || ("string" in d) &&
                utils_1.checkDecodeTezosID(d.string, "ED25519Signature", "SECP256K1Signature", "P256Signature", "GenericSignature") !== null) {
                return;
            }
            throw new utils_1.MichelsonTypeError(t, d, "signature expected: " + JSON.stringify(d));
        case "chain_id":
            if ("bytes" in d || "string" in d) {
                var x = "string" in d ? base58_1.decodeBase58Check(d.string) : utils_1.parseBytes(d.bytes);
                if (x !== null) {
                    return;
                }
            }
            throw new utils_1.MichelsonTypeError(t, d, "chain id expected: " + JSON.stringify(d));
        case "operation":
            throw new utils_1.MichelsonTypeError(t, d, "operation type can't be represented as a literal value");
        case "contract":
            throw new utils_1.MichelsonTypeError(t, d, "contract type can't be represented as a literal value");
        // Complex types
        case "option":
            if ("prim" in d) {
                if (d.prim === "None") {
                    return;
                }
                else if (d.prim === "Some") {
                    assertDataValidInternal(d.args[0], t.args[0], ctx);
                    return;
                }
            }
            throw new utils_1.MichelsonTypeError(t, d, "option expected: " + JSON.stringify(d));
        case "list":
        case "set":
            if (michelson_validator_1.assertDataListIfAny(d)) {
                var prev = void 0;
                try {
                    for (var d_2 = __values(d), d_2_1 = d_2.next(); !d_2_1.done; d_2_1 = d_2.next()) {
                        var v = d_2_1.value;
                        assertDataValidInternal(v, t.args[0], ctx);
                        if (t.prim === "set") {
                            if (prev === undefined) {
                                prev = v;
                            }
                            else if (compareMichelsonData(t.args[0], prev, v) > 0) {
                                throw new utils_1.MichelsonTypeError(t, d, "set elements must be ordered: " + JSON.stringify(d));
                            }
                        }
                    }
                }
                catch (e_4_1) { e_4 = { error: e_4_1 }; }
                finally {
                    try {
                        if (d_2_1 && !d_2_1.done && (_a = d_2.return)) _a.call(d_2);
                    }
                    finally { if (e_4) throw e_4.error; }
                }
                return;
            }
            throw new utils_1.MichelsonTypeError(t, d, t.prim + " expected: " + JSON.stringify(d));
        case "or":
            if ("prim" in d) {
                if (d.prim === "Left") {
                    assertDataValidInternal(d.args[0], t.args[0], ctx);
                    return;
                }
                else if (d.prim === "Right") {
                    assertDataValidInternal(d.args[0], t.args[1], ctx);
                    return;
                }
            }
            throw new utils_1.MichelsonTypeError(t, d, "union (or) expected: " + JSON.stringify(d));
        case "lambda":
            if (isFunction(d)) {
                var ret = functionTypeInternal(d, [t.args[0]], ctx);
                if ("failed" in ret) {
                    throw new utils_1.MichelsonTypeError(t, d, "function is failed with error type: " + ret.failed);
                }
                if (ret.length !== 1) {
                    throw new utils_1.MichelsonTypeError(t, d, "function must return a value");
                }
                assertScalarTypesEqual(t.args[1], ret[0]);
                return;
            }
            throw new utils_1.MichelsonTypeError(t, d, "function expected: " + JSON.stringify(d));
        case "map":
        case "big_map":
            if (Array.isArray(d)) {
                var prev = void 0;
                try {
                    for (var d_3 = __values(d), d_3_1 = d_3.next(); !d_3_1.done; d_3_1 = d_3.next()) {
                        var v = d_3_1.value;
                        if (!("prim" in v) || v.prim !== "Elt") {
                            throw new utils_1.MichelsonTypeError(t, d, "map elements expected: " + JSON.stringify(d));
                        }
                        assertDataValidInternal(v.args[0], t.args[0], ctx);
                        assertDataValidInternal(v.args[1], t.args[1], ctx);
                        if (prev === undefined) {
                            prev = v;
                        }
                        else if (compareMichelsonData(t.args[0], prev.args[0], v.args[0]) > 0) {
                            throw new utils_1.MichelsonTypeError(t, d, "map elements must be ordered: " + JSON.stringify(d));
                        }
                    }
                }
                catch (e_5_1) { e_5 = { error: e_5_1 }; }
                finally {
                    try {
                        if (d_3_1 && !d_3_1.done && (_b = d_3.return)) _b.call(d_3);
                    }
                    finally { if (e_5) throw e_5.error; }
                }
                return;
            }
            throw new utils_1.MichelsonTypeError(t, d, t.prim + " expected: " + JSON.stringify(d));
        case "bls12_381_fr":
            if ("int" in d && utils_1.isDecimal(d.int) || "bytes" in d && utils_1.parseBytes(d.bytes) !== null) {
                return;
            }
            throw new utils_1.MichelsonTypeError(t, d, "BLS12-381 element expected: " + JSON.stringify(d));
        case "sapling_state":
            if (Array.isArray(d)) {
                return;
            }
            throw new utils_1.MichelsonTypeError(t, d, "sapling state expected: " + JSON.stringify(d));
        case "ticket":
            assertDataValidInternal(d, {
                prim: "pair", args: [
                    { prim: "address" },
                    t.args[0],
                    { prim: "nat" },
                ]
            }, ctx);
            return;
        default:
            throw new utils_1.MichelsonTypeError(t, d, "unexpected type: " + typeID(t));
    }
}
function instructionListType(inst, stack, ctx) {
    var e_6, _a;
    var ret = stack;
    var s = stack;
    var i = 0;
    try {
        for (var inst_1 = __values(inst), inst_1_1 = inst_1.next(); !inst_1_1.done; inst_1_1 = inst_1.next()) {
            var op = inst_1_1.value;
            var ft = functionTypeInternal(op, s, ctx);
            ret = ft;
            if ("failed" in ft) {
                break;
            }
            s = ft;
            i++;
        }
    }
    catch (e_6_1) { e_6 = { error: e_6_1 }; }
    finally {
        try {
            if (inst_1_1 && !inst_1_1.done && (_a = inst_1.return)) _a.call(inst_1);
        }
        finally { if (e_6) throw e_6.error; }
    }
    if (("failed" in ret) && i !== inst.length - 1) {
        throw new MichelsonInstructionError(inst, ret, "FAIL must appear in a tail position");
    }
    if ((ctx === null || ctx === void 0 ? void 0 : ctx.traceCallback) !== undefined) {
        var trace = {
            op: inst,
            in: stack,
            out: ret,
        };
        ctx.traceCallback(trace);
    }
    return ret;
}
function functionTypeInternal(inst, stack, ctx) {
    var proto = (ctx === null || ctx === void 0 ? void 0 : ctx.protocol) || michelson_types_1.DefaultProtocol;
    if (Array.isArray(inst)) {
        return instructionListType(inst, stack, ctx);
    }
    var instruction = inst; // Make it const for type guarding
    // make sure the stack has enough number of arguments of specific types
    function args(n) {
        var e_7, _a;
        var typeIds = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            typeIds[_i - 1] = arguments[_i];
        }
        if (stack.length < typeIds.length + n) {
            throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": stack must have at least " + typeIds.length + " element(s)");
        }
        var i = n;
        try {
            for (var typeIds_1 = __values(typeIds), typeIds_1_1 = typeIds_1.next(); !typeIds_1_1.done; typeIds_1_1 = typeIds_1.next()) {
                var ids = typeIds_1_1.value;
                if (ids !== null && ids.length !== 0) {
                    var ii = 0;
                    while (ii < ids.length && ids[ii] !== typeID(stack[i])) {
                        ii++;
                    }
                    if (ii === ids.length) {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": stack type mismatch: [" + i + "] expected to be " + ids + ", got " + typeID(stack[i]) + " instead");
                    }
                }
                i++;
            }
        }
        catch (e_7_1) { e_7 = { error: e_7_1 }; }
        finally {
            try {
                if (typeIds_1_1 && !typeIds_1_1.done && (_a = typeIds_1.return)) _a.call(typeIds_1);
            }
            finally { if (e_7) throw e_7.error; }
        }
        return stack.slice(n, typeIds.length + n);
    }
    function rethrow(fn) {
        return function () {
            var args = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                args[_i] = arguments[_i];
            }
            try {
                return fn.apply(void 0, __spreadArray([], __read(args)));
            }
            catch (err) {
                if (err instanceof utils_1.MichelsonError) {
                    throw new MichelsonInstructionError(instruction, stack, err.message);
                }
                else {
                    throw err;
                }
            }
        };
    }
    function rethrowTypeGuard(fn) {
        return function (arg) {
            try {
                return fn(arg);
            }
            catch (err) {
                if (err instanceof utils_1.MichelsonError) {
                    throw new MichelsonInstructionError(instruction, stack, err.message);
                }
                else {
                    throw err;
                }
            }
        };
    }
    var argAnn = rethrow(utils_1.unpackAnnotations);
    var ensureStacksEqual = rethrow(assertStacksEqual);
    var ensureTypesEqual = rethrow(assertScalarTypesEqual);
    var ensureComparableType = rethrowTypeGuard(michelson_validator_1.assertMichelsonComparableType);
    var ensurePackableType = rethrowTypeGuard(michelson_validator_1.assertMichelsonPackableType);
    var ensureStorableType = rethrowTypeGuard(michelson_validator_1.assertMichelsonStorableType);
    var ensurePushableType = rethrowTypeGuard(michelson_validator_1.assertMichelsonPushableType);
    var ensureBigMapStorableType = rethrowTypeGuard(michelson_validator_1.assertMichelsonBigMapStorableType);
    // unpack instruction annotations and assert their maximum number
    function instructionAnn(num, opt) {
        var a = argAnn(instruction, __assign(__assign({}, opt), { emptyFields: num.f !== undefined && num.f > 1, emptyVar: num.v !== undefined && num.v > 1 }));
        var assertNum = function (a, n, type) {
            if (a && a.length > (n || 0)) {
                throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": at most " + (n || 0) + " " + type + " annotations allowed");
            }
        };
        assertNum(a.f, num.f, "field");
        assertNum(a.t, num.t, "type");
        assertNum(a.v, num.v, "variable");
        return a;
    }
    // also keeps annotation class if null is provided
    function annotate(tt, a) {
        var tx = tt;
        var t = Array.isArray(tx) ? { prim: "pair", args: tx } : tx;
        var src = argAnn(t);
        var ann = (a.v !== undefined || a.t !== undefined || a.f !== undefined) ? __spreadArray(__spreadArray(__spreadArray([], __read(((a.v === null ? src.v : a.v) || []))), __read(((a.t === null ? src.t : a.t) || []))), __read(((a.f === null ? src.f : a.f) || []))) : undefined;
        var annots = t.annots, rest = __rest(t, ["annots"]);
        return __assign(__assign({}, rest), (ann && ann.length !== 0 && { annots: ann }));
    }
    // shortcut to copy at most one variable annotation from the instruction to the type
    function annotateVar(t, def) {
        var ia = instructionAnn({ v: 1 });
        return annotate(t, { v: ia.v !== undefined ? ia.v : def !== undefined ? [def] : null, t: null });
    }
    // annotate CAR/CDR/UNPAIR/GET
    function annotateField(arg, field, insAnn, n, defField) {
        var _a, _b, _c, _d;
        var fieldAnn = (_a = argAnn(field).f) === null || _a === void 0 ? void 0 : _a[0]; // field's field annotation
        var insFieldAnn = (_b = insAnn.f) === null || _b === void 0 ? void 0 : _b[n];
        if (insFieldAnn !== undefined && insFieldAnn !== "%" && fieldAnn !== undefined && insFieldAnn !== fieldAnn) {
            throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": field names doesn't match: " + insFieldAnn + " !== " + fieldAnn);
        }
        var insVarAnn = (_c = insAnn.v) === null || _c === void 0 ? void 0 : _c[n]; // nth instruction's variable annotation
        var varAnn = (_d = argAnn(arg).v) === null || _d === void 0 ? void 0 : _d[0]; // instruction argument's variable annotation
        return annotate(field, {
            t: null,
            v: insVarAnn ?
                insVarAnn === "@%" ?
                    fieldAnn ? ["@" + fieldAnn.slice(1)] : undefined :
                    insVarAnn === "@%%" ?
                        varAnn ?
                            ["@" + varAnn.slice(1) + "." + (fieldAnn ? fieldAnn.slice(1) : defField)] :
                            fieldAnn ? ["@" + fieldAnn.slice(1)] : undefined :
                        [insVarAnn] :
                null,
        });
    }
    // comb helper functions
    function getN(src, n, i) {
        if (i === void 0) { i = n; }
        var p = utils_1.unpackComb("pair", src);
        if (i === 1) {
            return [p.args[0]];
        }
        else if (i === 2) {
            return p.args;
        }
        var right = p.args[1];
        if (utils_1.isPairType(right)) {
            return __spreadArray([p.args[0]], __read(getN(right, n, i - 1)));
        }
        else {
            throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": at least " + n + " fields are expected");
        }
    }
    function getNth(src, n, i) {
        if (i === void 0) { i = n; }
        if (i === 0) {
            return src;
        }
        var p = utils_1.unpackComb("pair", src);
        if (i === 1) {
            return p.args[0];
        }
        var right = p.args[1];
        if (utils_1.isPairType(right)) {
            return getNth(right, n, i - 2);
        }
        else if (i === 2) {
            return right;
        }
        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": at least " + (n + 1) + " fields are expected");
    }
    function updateNth(src, x, n, i) {
        if (i === void 0) { i = n; }
        if (i === 0) {
            return x;
        }
        var p = utils_1.unpackComb("pair", src);
        if (i === 1) {
            return __assign(__assign({}, p), { args: [x, p.args[1]] });
        }
        var right = p.args[1];
        if (utils_1.isPairType(right)) {
            return __assign(__assign({}, p), { args: [p.args[0], updateNth(right, x, n, i - 2)] });
        }
        else if (i === 2) {
            return __assign(__assign({}, p), { args: [p.args[0], x] });
        }
        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": at least " + (n + 1) + " fields are expected");
    }
    var varSuffix = function (a, suffix) { return ["@" + (a.v ? a.v[0].slice(1) + "." : "") + suffix]; };
    function branchType(br0, br1) {
        if (("failed" in br0) || ("failed" in br1)) {
            // Might be useful for debugging
            if (("failed" in br0) && ("failed" in br1)) {
                try {
                    assertScalarTypesEqual(br0.failed, br1.failed);
                    return br0;
                }
                catch (_a) {
                    return { failed: { prim: "or", args: [br0.failed, br1.failed] } };
                }
            }
            else {
                return ("failed" in br0) ? br1 : br0;
            }
        }
        else {
            ensureStacksEqual(br0, br1);
            return br0;
        }
    }
    var retStack = (function (instruction) {
        var _a, _b;
        var _c, _d, _e, _f, _g;
        switch (instruction.prim) {
            case "DUP":
                {
                    var n = instruction.args ? parseInt(instruction.args[0].int, 10) : 1;
                    if (n === 0) {
                        throw new MichelsonInstructionError(instruction, stack, "DUP 0 is forbidden");
                    }
                    var s_1 = args(n - 1, null)[0];
                    if (typeID(s_1) === "ticket") {
                        throw new MichelsonInstructionError(instruction, stack, "ticket can't be DUPed");
                    }
                    return __spreadArray([s_1], __read(stack));
                }
            case "SWAP":
                {
                    var s_2 = args(0, null, null);
                    instructionAnn({});
                    return __spreadArray([s_2[1], s_2[0]], __read(stack.slice(2)));
                }
            case "SOME":
                return __spreadArray([annotate({ prim: "option", args: [args(0, null)[0]] }, instructionAnn({ t: 1, v: 1 }))], __read(stack.slice(1)));
            case "UNIT":
                return __spreadArray([annotate({ prim: "unit" }, instructionAnn({ v: 1, t: 1 }))], __read(stack));
            case "PAIR":
                {
                    var n = instruction.args ? parseInt(instruction.args[0].int, 10) : 2;
                    if (n < 2) {
                        throw new MichelsonInstructionError(instruction, stack, "PAIR " + n + " is forbidden");
                    }
                    var s_3 = args.apply(void 0, __spreadArray([0], __read(new Array(n).fill(null))));
                    var ia_1 = instructionAnn({ f: n, t: 1, v: 1 }, { specialFields: true });
                    var trim_1 = function (s) {
                        var i = s.lastIndexOf(".");
                        return s.slice(i > 0 ? i + 1 : 1);
                    };
                    var retArgs = s_3.map(function (v, i) {
                        var _a;
                        var va = argAnn(v);
                        var f = ia_1.f && ia_1.f.length > i && ia_1.f[i] !== "%" ?
                            ia_1.f[i] === "%@" ?
                                va.v ? ["%" + trim_1(((_a = va.v) === null || _a === void 0 ? void 0 : _a[0]) || "")] : undefined :
                                [ia_1.f[i]] :
                            undefined;
                        return annotate(v, { v: null, t: null, f: f });
                    });
                    return __spreadArray([
                        annotate({
                            prim: "pair",
                            args: retArgs,
                        }, { t: ia_1.t, v: ia_1.v })
                    ], __read(stack.slice(n)));
                }
            case "UNPAIR":
                {
                    var n = instruction.args ? parseInt(instruction.args[0].int, 10) : 2;
                    if (n < 2) {
                        throw new MichelsonInstructionError(instruction, stack, "UNPAIR " + n + " is forbidden");
                    }
                    var s_4 = args(0, ["pair"])[0];
                    var ia_2 = instructionAnn({ f: 2, v: 2 }, { specialVar: true });
                    var fields = getN(s_4, n);
                    return __spreadArray(__spreadArray([], __read(fields.map(function (field, i) { return annotateField(s_4, field, ia_2, i, i === 0 ? "car" : "cdr"); }))), __read(stack.slice(1)));
                }
            case "CAR":
            case "CDR":
                {
                    var s_5 = utils_1.unpackComb("pair", args(0, ["pair"])[0]);
                    var field = s_5.args[instruction.prim === "CAR" ? 0 : 1];
                    var ia = instructionAnn({ f: 1, v: 1 }, { specialVar: true });
                    return __spreadArray([annotateField(s_5, field, ia, 0, instruction.prim.toLocaleLowerCase())], __read(stack.slice(1)));
                }
            case "CONS":
                {
                    var s_6 = args(0, null, ["list"]);
                    ensureTypesEqual(s_6[0], s_6[1].args[0]);
                    return __spreadArray([annotateVar({ prim: "list", args: [s_6[1].args[0]] })], __read(stack.slice(2)));
                }
            case "SIZE":
                args(0, ["string", "list", "set", "map", "bytes"]);
                return __spreadArray([annotateVar({ prim: "nat" })], __read(stack.slice(1)));
            case "MEM":
                {
                    var s_7 = args(0, null, ["set", "map", "big_map"]);
                    ensureComparableType(s_7[0]);
                    ensureTypesEqual(s_7[0], s_7[1].args[0]);
                    return __spreadArray([annotateVar({ prim: "bool" })], __read(stack.slice(2)));
                }
            case "GET":
                if (instruction.args) {
                    // comb operation
                    var n = parseInt(instruction.args[0].int, 10);
                    var s_8 = args(0, ["pair"])[0];
                    return __spreadArray([annotateVar(getNth(s_8, n))], __read(stack.slice(1)));
                }
                else {
                    // map operation
                    var s_9 = args(0, null, ["map", "big_map"]);
                    ensureComparableType(s_9[0]);
                    ensureTypesEqual(s_9[0], s_9[1].args[0]);
                    return __spreadArray([annotateVar({ prim: "option", args: [s_9[1].args[1]] })], __read(stack.slice(2)));
                }
            case "UPDATE":
                if (instruction.args) {
                    // comb operation
                    var n = parseInt(instruction.args[0].int, 10);
                    var s_10 = args(0, null, ["pair"]);
                    return __spreadArray([annotateVar(updateNth(s_10[1], s_10[0], n))], __read(stack.slice(2)));
                }
                else {
                    // map operation
                    var s0 = args(0, null, ["bool", "option"]);
                    ensureComparableType(s0[0]);
                    if (s0[1].prim === "bool") {
                        var s1_1 = args(2, ["set"]);
                        ensureTypesEqual(s0[0], s1_1[0].args[0]);
                        return __spreadArray([annotateVar({
                                prim: "set",
                                args: [annotate(s0[0], { t: null })],
                            })], __read(stack.slice(3)));
                    }
                    var s1 = args(2, ["map", "big_map"]);
                    ensureTypesEqual(s0[0], s1[0].args[0]);
                    if (s1[0].prim === "map") {
                        return __spreadArray([annotateVar({
                                prim: "map",
                                args: [
                                    annotate(s0[0], { t: null }),
                                    annotate(s0[1].args[0], { t: null }),
                                ],
                            })], __read(stack.slice(3)));
                    }
                    ensureBigMapStorableType(s0[1].args[0]);
                    return __spreadArray([annotateVar({
                            prim: "big_map",
                            args: [
                                annotate(s0[0], { t: null }),
                                annotate(s0[1].args[0], { t: null }),
                            ],
                        })], __read(stack.slice(3)));
                }
            case "GET_AND_UPDATE":
                {
                    var ia = instructionAnn({ v: 2 });
                    var s_11 = args(0, null, ["option"], ["map", "big_map"]);
                    ensureComparableType(s_11[0]);
                    ensureTypesEqual(s_11[0], s_11[2].args[0]);
                    ensureTypesEqual(s_11[1].args[0], s_11[2].args[1]);
                    var va = (_c = ia.v) === null || _c === void 0 ? void 0 : _c.map(function (v) { return v !== "@" ? [v] : undefined; });
                    if (s_11[2].prim === "map") {
                        return __spreadArray([
                            annotate({ prim: "option", args: [s_11[2].args[1]] }, { v: va === null || va === void 0 ? void 0 : va[0] }),
                            annotate({
                                prim: "map",
                                args: [
                                    annotate(s_11[0], { t: null }),
                                    annotate(s_11[1].args[0], { t: null }),
                                ],
                            }, { v: va === null || va === void 0 ? void 0 : va[1] })
                        ], __read(stack.slice(3)));
                    }
                    ensureBigMapStorableType(s_11[1].args[0]);
                    return __spreadArray([
                        annotate({ prim: "option", args: [s_11[2].args[1]] }, { v: va === null || va === void 0 ? void 0 : va[0] }),
                        annotate({
                            prim: "big_map",
                            args: [
                                annotate(s_11[0], { t: null }),
                                annotate(s_11[1].args[0], { t: null }),
                            ],
                        }, { v: va === null || va === void 0 ? void 0 : va[1] })
                    ], __read(stack.slice(3)));
                }
            case "EXEC":
                {
                    var s_12 = args(0, null, ["lambda"]);
                    ensureTypesEqual(s_12[0], s_12[1].args[0]);
                    return __spreadArray([annotateVar(s_12[1].args[1])], __read(stack.slice(2)));
                }
            case "APPLY":
                {
                    var s_13 = args(0, null, ["lambda"]);
                    ensureStorableType(s_13[0]);
                    ensurePushableType(s_13[0]);
                    if (!utils_1.isPairType(s_13[1].args[0])) {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": function's argument must be a pair: " + typeID(s_13[1].args[0]));
                    }
                    var pt = s_13[1].args[0];
                    ensureTypesEqual(s_13[0], typeArgs(pt)[0]);
                    return __spreadArray([annotateVar({ prim: "lambda", args: [typeArgs(pt)[1], s_13[1].args[1]] })], __read(stack.slice(2)));
                }
            case "FAILWITH":
                {
                    var s_14 = args(0, null)[0];
                    if (proto === michelson_types_1.Protocol.PtEdo2Zk || proto === michelson_types_1.Protocol.PsFLorena || proto === michelson_types_1.Protocol.PtGRANADs) {
                        ensurePackableType(s_14);
                    }
                    return { failed: s_14 };
                }
            case "NEVER":
                args(0, ["never"]);
                return { failed: { prim: "never" } };
            case "RENAME":
                return __spreadArray([annotateVar(args(0, null)[0])], __read(stack.slice(1)));
            case "CONCAT":
                {
                    var s0 = args(0, ["string", "list", "bytes"]);
                    if (s0[0].prim === "list") {
                        if (typeID(s0[0].args[0]) !== "string" && typeID(s0[0].args[0]) !== "bytes") {
                            throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": can't concatenate list of " + typeID(s0[0].args[0]) + "'s");
                        }
                        return __spreadArray([annotateVar(s0[0].args[0])], __read(stack.slice(1)));
                    }
                    var s1 = args(1, ["string", "bytes"]);
                    if (s0[0].prim !== s1[0].prim) {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": can't concatenate " + s0[0].prim + " with " + s1[0].prim);
                    }
                    return __spreadArray([annotateVar(s1[0])], __read(stack.slice(2)));
                }
            case "SLICE":
                return __spreadArray([annotateVar({ prim: "option", args: [args(0, ["nat"], ["nat"], ["string", "bytes"])[2]] }, "@slice")], __read(stack.slice(3)));
            case "PACK":
                {
                    var s_15 = args(0, null)[0];
                    ensurePackableType(s_15);
                    return __spreadArray([annotateVar({ prim: "bytes" }, "@packed")], __read(stack.slice(1)));
                }
            case "ADD":
                {
                    var s_16 = args(0, ["nat", "int", "timestamp", "mutez", "bls12_381_g1", "bls12_381_g2", "bls12_381_fr"], ["nat", "int", "timestamp", "mutez", "bls12_381_g1", "bls12_381_g2", "bls12_381_fr"]);
                    if (s_16[0].prim === "nat" && s_16[1].prim === "int" || s_16[0].prim === "int" && s_16[1].prim === "nat") {
                        return __spreadArray([annotateVar({ prim: "int" })], __read(stack.slice(2)));
                    }
                    else if (s_16[0].prim === "int" && s_16[1].prim === "timestamp" || s_16[0].prim === "timestamp" && s_16[1].prim === "int") {
                        return __spreadArray([annotateVar({ prim: "timestamp" })], __read(stack.slice(2)));
                    }
                    else if ((s_16[0].prim === "int" || s_16[0].prim === "nat" || s_16[0].prim === "mutez" || s_16[0].prim === "bls12_381_g1" || s_16[0].prim === "bls12_381_g2" || s_16[0].prim === "bls12_381_fr") && s_16[0].prim === s_16[1].prim) {
                        return __spreadArray([annotateVar(s_16[0])], __read(stack.slice(2)));
                    }
                    throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": can't add " + s_16[0].prim + " to " + s_16[1].prim);
                }
            case "SUB":
                {
                    var s_17 = args(0, ["nat", "int", "timestamp", "mutez"], ["nat", "int", "timestamp", "mutez"]);
                    if (((s_17[0].prim === "nat" || s_17[0].prim === "int") && (s_17[1].prim === "nat" || s_17[1].prim === "int")) ||
                        s_17[0].prim === "timestamp" && s_17[1].prim === "timestamp") {
                        return __spreadArray([annotateVar({ prim: "int" })], __read(stack.slice(2)));
                    }
                    else if (s_17[0].prim === "timestamp" && s_17[1].prim === "int") {
                        return __spreadArray([annotateVar({ prim: "timestamp" })], __read(stack.slice(2)));
                    }
                    else if (s_17[0].prim === "mutez" && s_17[1].prim === "mutez") {
                        return __spreadArray([annotateVar({ prim: "mutez" })], __read(stack.slice(2)));
                    }
                    throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": can't subtract " + s_17[0].prim + " from " + s_17[1].prim);
                }
            case "MUL":
                {
                    var s_18 = args(0, ["nat", "int", "mutez", "bls12_381_g1", "bls12_381_g2", "bls12_381_fr"], ["nat", "int", "mutez", "bls12_381_g1", "bls12_381_g2", "bls12_381_fr"]);
                    if (s_18[0].prim === "nat" && s_18[1].prim === "int" || s_18[0].prim === "int" && s_18[1].prim === "nat") {
                        return __spreadArray([annotateVar({ prim: "int" })], __read(stack.slice(2)));
                    }
                    else if (s_18[0].prim === "nat" && s_18[1].prim === "mutez" || s_18[0].prim === "mutez" && s_18[1].prim === "nat") {
                        return __spreadArray([annotateVar({ prim: "mutez" })], __read(stack.slice(2)));
                    }
                    else if ((s_18[0].prim === "bls12_381_g1" || s_18[0].prim === "bls12_381_g2" || s_18[0].prim === "bls12_381_fr") && s_18[1].prim === "bls12_381_fr" ||
                        (s_18[0].prim === "nat" || s_18[0].prim === "int") && s_18[0].prim === s_18[1].prim) {
                        return __spreadArray([annotateVar(s_18[0])], __read(stack.slice(2)));
                    }
                    else if ((s_18[0].prim === "nat" || s_18[0].prim === "int") && s_18[1].prim === "bls12_381_fr" ||
                        (s_18[1].prim === "nat" || s_18[1].prim === "int") && s_18[0].prim === "bls12_381_fr") {
                        return __spreadArray([annotateVar({ prim: "bls12_381_fr" })], __read(stack.slice(2)));
                    }
                    throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": can't multiply " + s_18[0].prim + " by " + s_18[1].prim);
                }
            case "EDIV":
                {
                    var res = function (a, b) { return ({ prim: "option", args: [{ prim: "pair", args: [{ prim: a }, { prim: b }] }] }); };
                    var s_19 = args(0, ["nat", "int", "mutez"], ["nat", "int", "mutez"]);
                    if (s_19[0].prim === "nat" && s_19[1].prim === "nat") {
                        return __spreadArray([annotateVar(res("nat", "nat"))], __read(stack.slice(2)));
                    }
                    else if ((s_19[0].prim === "nat" || s_19[0].prim === "int") && (s_19[1].prim === "nat" || s_19[1].prim === "int")) {
                        return __spreadArray([annotateVar(res("int", "nat"))], __read(stack.slice(2)));
                    }
                    else if (s_19[0].prim === "mutez" && s_19[1].prim === "nat") {
                        return __spreadArray([annotateVar(res("mutez", "mutez"))], __read(stack.slice(2)));
                    }
                    else if (s_19[0].prim === "mutez" && s_19[1].prim === "mutez") {
                        return __spreadArray([annotateVar(res("nat", "mutez"))], __read(stack.slice(2)));
                    }
                    throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": can't euclideally divide " + s_19[0].prim + " by " + s_19[1].prim);
                }
            case "ABS":
                args(0, ["int"]);
                return __spreadArray([annotateVar({ prim: "nat" })], __read(stack.slice(1)));
            case "ISNAT":
                args(0, ["int"]);
                return __spreadArray([annotateVar({ prim: "option", args: [{ prim: "nat" }] })], __read(stack.slice(1)));
            case "INT":
                args(0, ["nat", "bls12_381_fr"]);
                return __spreadArray([annotateVar({ prim: "int" })], __read(stack.slice(1)));
            case "NEG":
                {
                    var s_20 = args(0, ["nat", "int", "bls12_381_g1", "bls12_381_g2", "bls12_381_fr"])[0];
                    if (s_20.prim === "nat" || s_20.prim === "int") {
                        return __spreadArray([annotateVar({ prim: "int" })], __read(stack.slice(1)));
                    }
                    return __spreadArray([annotateVar(s_20)], __read(stack.slice(1)));
                }
            case "LSL":
            case "LSR":
                args(0, ["nat"], ["nat"]);
                return __spreadArray([annotateVar({ prim: "nat" })], __read(stack.slice(2)));
            case "OR":
            case "XOR":
                {
                    var s_21 = args(0, ["nat", "bool"], ["nat", "bool"]);
                    if (s_21[0].prim !== s_21[1].prim) {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": both arguments must be of the same type: " + s_21[0].prim + ", " + s_21[1].prim);
                    }
                    return __spreadArray([annotateVar(s_21[1])], __read(stack.slice(2)));
                }
            case "AND":
                {
                    var s_22 = args(0, ["nat", "bool", "int"], ["nat", "bool"]);
                    if ((s_22[0].prim !== "int" || s_22[1].prim !== "nat") && s_22[0].prim !== s_22[1].prim) {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": both arguments must be of the same type: " + s_22[0].prim + ", " + s_22[1].prim);
                    }
                    return __spreadArray([annotateVar(s_22[1])], __read(stack.slice(2)));
                }
            case "NOT":
                {
                    var s_23 = args(0, ["nat", "bool", "int"])[0];
                    if (s_23.prim === "bool") {
                        return __spreadArray([annotateVar({ prim: "bool" })], __read(stack.slice(1)));
                    }
                    return __spreadArray([annotateVar({ prim: "int" })], __read(stack.slice(1)));
                }
            case "COMPARE":
                {
                    var s_24 = args(0, null, null);
                    ensureComparableType(s_24[0]);
                    ensureComparableType(s_24[1]);
                    return __spreadArray([annotateVar({ prim: "int" })], __read(stack.slice(2)));
                }
            case "EQ":
            case "NEQ":
            case "LT":
            case "GT":
            case "LE":
            case "GE":
                args(0, ["int"]);
                return __spreadArray([annotateVar({ prim: "bool" })], __read(stack.slice(1)));
            case "SELF":
                {
                    if ((ctx === null || ctx === void 0 ? void 0 : ctx.contract) === undefined) {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": contract required");
                    }
                    var ia = instructionAnn({ f: 1, v: 1 });
                    var ep = contractEntryPoint(ctx.contract, (_d = ia.f) === null || _d === void 0 ? void 0 : _d[0]);
                    if (ep === null) {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": contract has no entrypoint " + ep);
                    }
                    return __spreadArray([annotate({ prim: "contract", args: [ep] }, { v: ia.v ? ia.v : ["@self"] })], __read(stack));
                }
            case "TRANSFER_TOKENS":
                {
                    var s_25 = args(0, null, ["mutez"], ["contract"]);
                    ensureTypesEqual(s_25[0], s_25[2].args[0]);
                    return __spreadArray([annotateVar({ prim: "operation" })], __read(stack.slice(3)));
                }
            case "SET_DELEGATE":
                {
                    var s_26 = args(0, ["option"])[0];
                    if (typeID(s_26.args[0]) !== "key_hash") {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": key hash expected: " + typeID(s_26.args[0]));
                    }
                    return __spreadArray([annotateVar({ prim: "operation" })], __read(stack.slice(1)));
                }
            case "IMPLICIT_ACCOUNT":
                args(0, ["key_hash"]);
                return __spreadArray([annotateVar({ prim: "contract", args: [{ prim: "unit" }] })], __read(stack.slice(1)));
            case "NOW":
                return __spreadArray([annotateVar({ prim: "timestamp" }, "@now")], __read(stack));
            case "AMOUNT":
                return __spreadArray([annotateVar({ prim: "mutez" }, "@amount")], __read(stack));
            case "BALANCE":
                return __spreadArray([annotateVar({ prim: "mutez" }, "@balance")], __read(stack));
            case "CHECK_SIGNATURE":
                args(0, ["key"], ["signature"], ["bytes"]);
                return __spreadArray([annotateVar({ prim: "bool" })], __read(stack.slice(3)));
            case "BLAKE2B":
            case "SHA256":
            case "SHA512":
            case "KECCAK":
            case "SHA3":
                args(0, ["bytes"]);
                return __spreadArray([annotateVar({ prim: "bytes" })], __read(stack.slice(1)));
            case "HASH_KEY":
                args(0, ["key"]);
                return __spreadArray([annotateVar({ prim: "key_hash" })], __read(stack.slice(1)));
            case "SOURCE":
                return __spreadArray([annotateVar({ prim: "address" }, "@source")], __read(stack));
            case "SENDER":
                return __spreadArray([annotateVar({ prim: "address" }, "@sender")], __read(stack));
            case "ADDRESS":
                {
                    var s_27 = args(0, ["contract"])[0];
                    var ia = instructionAnn({ v: 1 });
                    return __spreadArray([
                        annotate((_a = { prim: "address" }, _a[michelson_types_1.refContract] = s_27, _a), { v: ia.v ? ia.v : varSuffix(argAnn(s_27), "address") })
                    ], __read(stack.slice(1)));
                }
            case "SELF_ADDRESS":
                {
                    var addr = { prim: "address" };
                    if ((ctx === null || ctx === void 0 ? void 0 : ctx.contract) !== undefined) {
                        addr[michelson_types_1.refContract] = { prim: "contract", args: [contractSection(ctx.contract, "parameter").args[0]] };
                    }
                    return __spreadArray([annotateVar(addr, "@address")], __read(stack));
                }
            case "CHAIN_ID":
                return __spreadArray([annotateVar({ prim: "chain_id" })], __read(stack));
            case "DROP":
                {
                    instructionAnn({});
                    var n = instruction.args !== undefined ? parseInt(instruction.args[0].int, 10) : 1;
                    args(n - 1, null);
                    return stack.slice(n);
                }
            case "DIG":
                {
                    instructionAnn({});
                    var n = parseInt(instruction.args[0].int, 10);
                    return __spreadArray(__spreadArray([args(n, null)[0]], __read(stack.slice(0, n))), __read(stack.slice(n + 1)));
                }
            case "DUG":
                {
                    instructionAnn({});
                    var n = parseInt(instruction.args[0].int, 10);
                    return __spreadArray(__spreadArray(__spreadArray([], __read(stack.slice(1, n + 1))), [args(0, null)[0]]), __read(stack.slice(n + 1)));
                }
            case "NONE":
                assertTypeAnnotationsValid(instruction.args[0]);
                return __spreadArray([annotate({ prim: "option", args: [instruction.args[0]] }, instructionAnn({ t: 1, v: 1 }))], __read(stack));
            case "LEFT":
            case "RIGHT":
                {
                    var s_28 = args(0, null)[0];
                    var ia = instructionAnn({ f: 2, t: 1, v: 1 }, { specialFields: true });
                    var va = argAnn(s_28);
                    var children = [
                        annotate(s_28, {
                            t: null,
                            v: null,
                            f: ia.f && ia.f.length > 0 && ia.f[0] !== "%" ?
                                ia.f[0] === "%@" ?
                                    va.v ? ["%" + va.v[0].slice(1)] : undefined :
                                    ia.f :
                                undefined,
                        }),
                        annotate(instruction.args[0], {
                            t: null,
                            f: ia.f && ia.f.length > 1 && ia.f[1] !== "%" ? ia.f : undefined,
                        }),
                    ];
                    return __spreadArray([annotate({
                            prim: "or", args: instruction.prim === "LEFT" ? children : [children[1], children[0]]
                        }, { t: ia.t, v: ia.v })], __read(stack.slice(1)));
                }
            case "NIL":
                assertTypeAnnotationsValid(instruction.args[0]);
                return __spreadArray([annotate({ prim: "list", args: [instruction.args[0]] }, instructionAnn({ t: 1, v: 1 }))], __read(stack));
            case "UNPACK":
                args(0, ["bytes"]);
                assertTypeAnnotationsValid(instruction.args[0]);
                return __spreadArray([annotateVar({ prim: "option", args: [instruction.args[0]] }, "@unpacked")], __read(stack.slice(1)));
            case "CONTRACT":
                {
                    var s_29 = args(0, ["address"])[0];
                    assertTypeAnnotationsValid(instruction.args[0]);
                    var ia = instructionAnn({ v: 1, f: 1 });
                    var contract = s_29[michelson_types_1.refContract];
                    if (contract !== undefined) {
                        var ep = contractEntryPoint(contract, (_e = ia.f) === null || _e === void 0 ? void 0 : _e[0]);
                        if (ep === null) {
                            throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": contract has no entrypoint " + ep);
                        }
                        ensureTypesEqual(ep, instruction.args[0]);
                    }
                    return __spreadArray([
                        annotate({ prim: "option", args: [{ prim: "contract", args: [instruction.args[0]] }] }, { v: ia.v ? ia.v : varSuffix(argAnn(s_29), "contract") })
                    ], __read(stack.slice(1)));
                }
            case "CAST":
                instructionAnn({});
                var s = args(0, null)[0];
                assertTypeAnnotationsValid(instruction.args[0]);
                ensureTypesEqual(instruction.args[0], s);
                return __spreadArray([instruction.args[0]], __read(stack.slice(1)));
            case "IF_NONE":
                {
                    instructionAnn({});
                    var s_30 = args(0, ["option"])[0];
                    var tail = stack.slice(1);
                    var br0 = functionTypeInternal(instruction.args[0], tail, ctx);
                    var br1 = functionTypeInternal(instruction.args[1], __spreadArray([annotate(s_30.args[0], { t: null, v: varSuffix(argAnn(s_30), "some") })], __read(tail)), ctx);
                    return branchType(br0, br1);
                }
            case "IF_LEFT":
                {
                    instructionAnn({});
                    var s_31 = args(0, ["or"])[0];
                    var va = argAnn(s_31);
                    var lefta = argAnn(s_31.args[0]);
                    var righta = argAnn(s_31.args[1]);
                    var tail = stack.slice(1);
                    var br0 = functionTypeInternal(instruction.args[0], __spreadArray([
                        annotate(s_31.args[0], { t: null, v: varSuffix(va, lefta.f ? lefta.f[0].slice(1) : "left") })
                    ], __read(tail)), ctx);
                    var br1 = functionTypeInternal(instruction.args[1], __spreadArray([
                        annotate(s_31.args[1], { t: null, v: varSuffix(va, righta.f ? righta.f[0].slice(1) : "right") })
                    ], __read(tail)), ctx);
                    return branchType(br0, br1);
                }
            case "IF_CONS":
                {
                    instructionAnn({});
                    var s_32 = args(0, ["list"])[0];
                    var va = argAnn(s_32);
                    var tail = stack.slice(1);
                    var br0 = functionTypeInternal(instruction.args[0], __spreadArray([
                        annotate(s_32.args[0], { t: null, v: varSuffix(va, "hd") }),
                        annotate(s_32, { t: null, v: varSuffix(va, "tl") })
                    ], __read(tail)), ctx);
                    var br1 = functionTypeInternal(instruction.args[1], tail, ctx);
                    return branchType(br0, br1);
                }
            case "IF":
                {
                    instructionAnn({});
                    args(0, ["bool"]);
                    var tail = stack.slice(1);
                    var br0 = functionTypeInternal(instruction.args[0], tail, ctx);
                    var br1 = functionTypeInternal(instruction.args[1], tail, ctx);
                    return branchType(br0, br1);
                }
            case "MAP":
                {
                    var s_33 = args(0, ["list", "map"])[0];
                    var tail = stack.slice(1);
                    var elt = s_33.prim === "map" ? { prim: "pair", args: s_33.args } : s_33.args[0];
                    var body = functionTypeInternal(instruction.args[0], __spreadArray([annotate(elt, { t: null, v: varSuffix(argAnn(s_33), "elt") })], __read(tail)), ctx);
                    if ("failed" in body) {
                        return body;
                    }
                    if (body.length < 1) {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": function must return a value");
                    }
                    ensureStacksEqual(body.slice(1), tail);
                    if (s_33.prim === "list") {
                        return __spreadArray([annotateVar({ prim: "list", args: [body[0]] })], __read(tail));
                    }
                    return __spreadArray([annotateVar({ prim: "map", args: [s_33.args[0], body[0]] })], __read(tail));
                }
            case "ITER":
                {
                    instructionAnn({});
                    var s_34 = args(0, ["set", "list", "map"])[0];
                    var tail = stack.slice(1);
                    var elt = s_34.prim === "map" ? { prim: "pair", args: s_34.args } : s_34.args[0];
                    var body = functionTypeInternal(instruction.args[0], __spreadArray([annotate(elt, { t: null, v: varSuffix(argAnn(s_34), "elt") })], __read(tail)), ctx);
                    if ("failed" in body) {
                        return body;
                    }
                    ensureStacksEqual(body, tail);
                    return tail;
                }
            case "LOOP":
                {
                    instructionAnn({});
                    args(0, ["bool"]);
                    var tail = stack.slice(1);
                    var body = functionTypeInternal(instruction.args[0], tail, ctx);
                    if ("failed" in body) {
                        return body;
                    }
                    ensureStacksEqual(body, __spreadArray([{ prim: "bool" }], __read(tail)));
                    return tail;
                }
            case "LOOP_LEFT":
                {
                    instructionAnn({});
                    var s_35 = args(0, ["or"])[0];
                    var tail = stack.slice(1);
                    var body = functionTypeInternal(instruction.args[0], __spreadArray([annotate(s_35.args[0], { t: null, v: varSuffix(argAnn(s_35), "left") })], __read(tail)), ctx);
                    if ("failed" in body) {
                        return body;
                    }
                    ensureStacksEqual(body, __spreadArray([s_35], __read(tail)));
                    return __spreadArray([annotate(s_35.args[1], { t: null, v: instructionAnn({ v: 1 }).v })], __read(tail));
                }
            case "DIP":
                {
                    instructionAnn({});
                    var n = instruction.args.length === 2 ? parseInt(instruction.args[0].int, 10) : 1;
                    args(n - 1, null);
                    var head = stack.slice(0, n);
                    var tail = stack.slice(n);
                    // ternary operator is a type guard so use it instead of just `instruction.args.length - 1`
                    var body = instruction.args.length === 2 ?
                        functionTypeInternal(instruction.args[1], tail, ctx) :
                        functionTypeInternal(instruction.args[0], tail, ctx);
                    if ("failed" in body) {
                        return body;
                    }
                    return __spreadArray(__spreadArray([], __read(head)), __read(body));
                }
            case "CREATE_CONTRACT":
                {
                    var ia = instructionAnn({ v: 2 });
                    var s_36 = args(0, ["option"], ["mutez"], null);
                    if (typeID(s_36[0].args[0]) !== "key_hash") {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": key hash expected: " + typeID(s_36[0].args[0]));
                    }
                    if (ensureStorableType(s_36[2])) {
                        assertContractValid(instruction.args[0]);
                        assertScalarTypesEqual(contractSection(instruction.args[0], "storage").args[0], s_36[2]);
                    }
                    var va = (_f = ia.v) === null || _f === void 0 ? void 0 : _f.map(function (v) { return v !== "@" ? [v] : undefined; });
                    return __spreadArray([
                        annotate({ prim: "operation" }, { v: va === null || va === void 0 ? void 0 : va[0] }),
                        annotate((_b = {
                                prim: "address"
                            },
                            _b[michelson_types_1.refContract] = { prim: "contract", args: [contractSection(instruction.args[0], "parameter").args[0]] },
                            _b), { v: va === null || va === void 0 ? void 0 : va[1] })
                    ], __read(stack.slice(3)));
                }
            case "PUSH":
                assertTypeAnnotationsValid(instruction.args[0]);
                assertDataValidInternal(instruction.args[1], instruction.args[0], __assign(__assign({}, ctx), { contract: undefined }));
                return __spreadArray([annotateVar(instruction.args[0])], __read(stack));
            case "EMPTY_SET":
                assertTypeAnnotationsValid(instruction.args[0]);
                ensureComparableType(instruction.args[0]);
                return __spreadArray([annotate({ prim: "set", args: instruction.args }, instructionAnn({ t: 1, v: 1 }))], __read(stack));
            case "EMPTY_MAP":
                assertTypeAnnotationsValid(instruction.args[0]);
                ensureComparableType(instruction.args[0]);
                assertTypeAnnotationsValid(instruction.args[1]);
                return __spreadArray([annotate({ prim: "map", args: instruction.args }, instructionAnn({ t: 1, v: 1 }))], __read(stack));
            case "EMPTY_BIG_MAP":
                assertTypeAnnotationsValid(instruction.args[0]);
                ensureComparableType(instruction.args[0]);
                assertTypeAnnotationsValid(instruction.args[1]);
                ensureBigMapStorableType(instruction.args[0]);
                return __spreadArray([annotate({ prim: "big_map", args: instruction.args }, instructionAnn({ t: 1, v: 1 }))], __read(stack));
            case "LAMBDA":
                {
                    assertTypeAnnotationsValid(instruction.args[0]);
                    assertTypeAnnotationsValid(instruction.args[1]);
                    var body = functionTypeInternal(instruction.args[2], [instruction.args[0]], __assign(__assign({}, ctx), { contract: undefined }));
                    if ("failed" in body) {
                        return body;
                    }
                    if (body.length !== 1) {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": function must return a value");
                    }
                    ensureTypesEqual(instruction.args[1], body[0]);
                    return __spreadArray([annotateVar({ prim: "lambda", args: [instruction.args[0], instruction.args[1]] })], __read(stack));
                }
            case "LEVEL":
                return __spreadArray([annotateVar({ prim: "nat" }, "@level")], __read(stack));
            case "TOTAL_VOTING_POWER":
                return __spreadArray([annotateVar({ prim: "nat" })], __read(stack));
            case "VOTING_POWER":
                args(0, ["key_hash"]);
                return __spreadArray([annotateVar({ prim: "nat" })], __read(stack.slice(1)));
            case "TICKET":
                {
                    var s_37 = args(0, null, ["nat"])[0];
                    ensureComparableType(s_37);
                    return __spreadArray([annotate({ prim: "ticket", args: [s_37] }, instructionAnn({ t: 1, v: 1 }))], __read(stack.slice(2)));
                }
            case "JOIN_TICKETS":
                {
                    var s_38 = utils_1.unpackComb("pair", args(0, ["pair"])[0]);
                    if (typeID(s_38.args[0]) !== "ticket") {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": ticket expected: " + typeID(s_38.args[0]));
                    }
                    ensureTypesEqual(s_38.args[0], s_38.args[1]);
                    return __spreadArray([annotateVar({
                            prim: "option",
                            args: [annotate(s_38.args[0], { t: null })],
                        })], __read(stack.slice(1)));
                }
            case "SPLIT_TICKET":
                {
                    var s_39 = args(0, ["ticket"], ["pair"]);
                    var p = utils_1.unpackComb("pair", s_39[1]);
                    if (typeID(p.args[0]) !== "nat") {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": nat expected: " + typeID(p.args[0]));
                    }
                    ensureTypesEqual(p.args[0], p.args[1]);
                    return __spreadArray([annotateVar({
                            prim: "option",
                            args: [{
                                    prim: "pair",
                                    args: [
                                        annotate(s_39[0], { t: null }),
                                        annotate(s_39[0], { t: null }),
                                    ],
                                }],
                        })], __read(stack.slice(2)));
                }
            case "READ_TICKET":
                {
                    var ia = instructionAnn({ v: 2 });
                    var s_40 = args(0, ["ticket"])[0];
                    var va = (_g = ia.v) === null || _g === void 0 ? void 0 : _g.map(function (v) { return v !== "@" ? [v] : undefined; });
                    return __spreadArray([
                        annotate({
                            prim: "pair",
                            args: [
                                { prim: "address" },
                                annotate(s_40.args[0], { t: null }),
                                { prim: "nat" },
                            ]
                        }, { v: va === null || va === void 0 ? void 0 : va[0] }),
                        annotate(s_40, { v: va === null || va === void 0 ? void 0 : va[1], t: null })
                    ], __read(stack.slice(1)));
                }
            case "PAIRING_CHECK":
                {
                    var p = args(0, ["list"])[0].args[0];
                    if (!utils_1.isPairType(p)) {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": pair expected: " + typeID(p));
                    }
                    var c = utils_1.unpackComb("pair", p);
                    if (typeID(c.args[0]) !== "bls12_381_g1") {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": bls12_381_g1 expected: " + typeID(c.args[0]));
                    }
                    if (typeID(c.args[1]) !== "bls12_381_g2") {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": bls12_381_g2 expected: " + typeID(c.args[1]));
                    }
                    return __spreadArray([annotateVar({ prim: "bool" })], __read(stack.slice(1)));
                }
            case "SAPLING_EMPTY_STATE":
                return __spreadArray([annotate({ prim: "sapling_state", args: [instruction.args[0]] }, instructionAnn({ v: 1, t: 1 }))], __read(stack));
            case "SAPLING_VERIFY_UPDATE":
                {
                    var s_41 = args(0, ["sapling_transaction"], ["sapling_state"]);
                    if (parseInt(s_41[0].args[0].int, 10) !== parseInt(s_41[1].args[0].int, 10)) {
                        throw new MichelsonInstructionError(instruction, stack, instruction.prim + ": sapling memo size mismatch: " + s_41[0].args[0].int + " != " + s_41[1].args[0].int);
                    }
                    return __spreadArray([annotateVar({
                            prim: "option",
                            args: [{
                                    prim: "pair",
                                    args: [
                                        { prim: "int" },
                                        annotate(s_41[1], { t: null }),
                                    ],
                                }],
                        })], __read(stack.slice(2)));
                }
            default:
                throw new utils_1.MichelsonError(instruction, "unexpected instruction: " + instruction.prim);
        }
    })(instruction);
    if ((ctx === null || ctx === void 0 ? void 0 : ctx.traceCallback) !== undefined) {
        var trace = {
            op: instruction,
            in: stack,
            out: retStack,
        };
        ctx.traceCallback(trace);
    }
    return retStack;
}
function contractSection(contract, section) {
    var e_8, _a;
    try {
        for (var contract_1 = __values(contract), contract_1_1 = contract_1.next(); !contract_1_1.done; contract_1_1 = contract_1.next()) {
            var s = contract_1_1.value;
            if (s.prim === section) {
                return s;
            }
        }
    }
    catch (e_8_1) { e_8 = { error: e_8_1 }; }
    finally {
        try {
            if (contract_1_1 && !contract_1_1.done && (_a = contract_1.return)) _a.call(contract_1);
        }
        finally { if (e_8) throw e_8.error; }
    }
    throw new utils_1.MichelsonError(contract, "missing contract section: " + section);
}
exports.contractSection = contractSection;
function isContract(v) {
    var e_9, _a;
    if (Array.isArray(v)) {
        try {
            for (var v_1 = __values(v), v_1_1 = v_1.next(); !v_1_1.done; v_1_1 = v_1.next()) {
                var s = v_1_1.value;
                if ("prim" in s && (s.prim === "parameter" || s.prim === "storage" || s.prim === "code")) {
                    return true;
                }
            }
        }
        catch (e_9_1) { e_9 = { error: e_9_1 }; }
        finally {
            try {
                if (v_1_1 && !v_1_1.done && (_a = v_1.return)) _a.call(v_1);
            }
            finally { if (e_9) throw e_9.error; }
        }
    }
    return false;
}
function contractEntryPoint(src, ep) {
    ep = ep || "%default";
    var entryPoint = contractEntryPoints(src).find(function (x) { return x[0] === ep; });
    if (entryPoint !== undefined) {
        return entryPoint[1];
    }
    else if (ep === "%default") {
        return isContract(src) ? contractSection(src, "parameter").args[0] : src;
    }
    return null;
}
exports.contractEntryPoint = contractEntryPoint;
function isOrType(t) {
    return Array.isArray(t) || t.prim === "or";
}
function contractEntryPoints(src) {
    if (isContract(src)) {
        var param = contractSection(src, "parameter");
        var ch = contractEntryPoints(param.args[0]);
        var a = utils_1.unpackAnnotations(param);
        return a.f ? __spreadArray([[a.f[0], param.args[0]]], __read(ch)) : ch;
    }
    if (isOrType(src)) {
        var args_2 = typeArgs(src);
        var getArg = function (n) {
            var a = utils_1.unpackAnnotations(args_2[n]);
            if (typeID(args_2[n]) === "or") {
                var ch = contractEntryPoints(args_2[n]);
                return a.f ? __spreadArray([[a.f[0], args_2[n]]], __read(ch)) : ch;
            }
            return a.f ? [[a.f[0], args_2[n]]] : [];
        };
        return __spreadArray(__spreadArray([], __read(getArg(0))), __read(getArg(1)));
    }
    return [];
}
exports.contractEntryPoints = contractEntryPoints;
// Contract validation
function assertContractValid(contract, ctx) {
    var parameter = contractSection(contract, "parameter").args[0];
    assertTypeAnnotationsValid(parameter, true);
    var storage = contractSection(contract, "storage").args[0];
    assertTypeAnnotationsValid(storage);
    var arg = {
        "prim": "pair",
        args: [
            __assign(__assign({}, parameter), { annots: ["@parameter"] }),
            __assign(__assign({}, storage), { annots: ["@storage"] }),
        ]
    };
    var code = contractSection(contract, "code").args[0];
    var ret = functionTypeInternal(code, [arg], __assign(__assign({}, ctx), { contract: contract }));
    if ("failed" in ret) {
        return ret;
    }
    var expected = {
        "prim": "pair",
        args: [
            { "prim": "list", args: [{ "prim": "operation" }] },
            storage,
        ]
    };
    try {
        assertStacksEqual(ret, [expected]);
    }
    catch (err) {
        if (err instanceof utils_1.MichelsonError) {
            throw new MichelsonInstructionError(code, ret, err.message);
        }
        else {
            throw err;
        }
    }
    return ret;
}
exports.assertContractValid = assertContractValid;
// Exported wrapper functions
function assertDataValid(d, t, ctx) {
    assertTypeAnnotationsValid(t);
    assertDataValidInternal(d, t, ctx || null);
}
exports.assertDataValid = assertDataValid;
function functionType(inst, stack, ctx) {
    var e_10, _a, e_11, _b;
    try {
        for (var stack_1 = __values(stack), stack_1_1 = stack_1.next(); !stack_1_1.done; stack_1_1 = stack_1.next()) {
            var t = stack_1_1.value;
            assertTypeAnnotationsValid(t);
        }
    }
    catch (e_10_1) { e_10 = { error: e_10_1 }; }
    finally {
        try {
            if (stack_1_1 && !stack_1_1.done && (_a = stack_1.return)) _a.call(stack_1);
        }
        finally { if (e_10) throw e_10.error; }
    }
    if ((ctx === null || ctx === void 0 ? void 0 : ctx.contract) !== undefined) {
        try {
            for (var _c = __values(["parameter", "storage"]), _d = _c.next(); !_d.done; _d = _c.next()) {
                var typesec = _d.value;
                var sec = contractSection(ctx.contract, typesec).args[0];
                assertTypeAnnotationsValid(sec);
            }
        }
        catch (e_11_1) { e_11 = { error: e_11_1 }; }
        finally {
            try {
                if (_d && !_d.done && (_b = _c.return)) _b.call(_c);
            }
            finally { if (e_11) throw e_11.error; }
        }
    }
    return functionTypeInternal(inst, stack, ctx || null);
}
exports.functionType = functionType;
function assertTypesEqual(a, b, field) {
    var e_12, _a, e_13, _b;
    if (field === void 0) { field = false; }
    if (Array.isArray(a)) {
        try {
            // type guards don't work for parametrized generic types
            for (var _c = __values(a), _d = _c.next(); !_d.done; _d = _c.next()) {
                var v = _d.value;
                assertTypeAnnotationsValid(v);
            }
        }
        catch (e_12_1) { e_12 = { error: e_12_1 }; }
        finally {
            try {
                if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
            }
            finally { if (e_12) throw e_12.error; }
        }
        try {
            for (var _e = __values(b), _f = _e.next(); !_f.done; _f = _e.next()) {
                var v = _f.value;
                assertTypeAnnotationsValid(v);
            }
        }
        catch (e_13_1) { e_13 = { error: e_13_1 }; }
        finally {
            try {
                if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
            }
            finally { if (e_13) throw e_13.error; }
        }
    }
    else {
        assertTypeAnnotationsValid(a);
        assertTypeAnnotationsValid(b);
    }
    assertScalarTypesEqual(a, b, field);
}
exports.assertTypesEqual = assertTypesEqual;
function isTypeAnnotationsValid(t, field) {
    if (field === void 0) { field = false; }
    try {
        assertTypeAnnotationsValid(t, field);
        return true;
    }
    catch (_a) {
        return false;
    }
}
exports.isTypeAnnotationsValid = isTypeAnnotationsValid;
function isContractValid(contract, ctx) {
    try {
        return assertContractValid(contract, ctx);
    }
    catch (_a) {
        return null;
    }
}
exports.isContractValid = isContractValid;
function isDataValid(d, t, ctx) {
    try {
        assertDataValid(d, t, ctx);
        return true;
    }
    catch (_a) {
        return false;
    }
}
exports.isDataValid = isDataValid;
function isTypeEqual(a, b, field) {
    if (field === void 0) { field = false; }
    try {
        assertTypesEqual(a, b, field);
        return true;
    }
    catch (_a) {
        return false;
    }
}
exports.isTypeEqual = isTypeEqual;
//# sourceMappingURL=michelson-typecheck.js.map