UNPKG

@harmoniclabs/buildooor

Version:

Cardano transaction builder in typescript

1,397 lines 75.3 kB
"use strict";
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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    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) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (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 (g && (g = 0, op[0] && (_ = 0)), _) 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 };
    }
};
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 __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 __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 __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.splitStringByByteLength = exports.getScriptDataHash = exports.TxBuilder = void 0;
var uint8array_utils_1 = require("@harmoniclabs/uint8array-utils");
var keepRelevant_1 = require("./keepRelevant.js");
var GenesisInfos_1 = require("./GenesisInfos.js");
var cardano_costmodels_ts_1 = require("@harmoniclabs/cardano-costmodels-ts");
var cardano_ledger_ts_1 = require("@harmoniclabs/cardano-ledger-ts");
var cbor_1 = require("@harmoniclabs/cbor");
var crypto_1 = require("@harmoniclabs/crypto");
var plutus_data_1 = require("@harmoniclabs/plutus-data");
var plutus_machine_1 = require("@harmoniclabs/plutus-machine");
var uplc_1 = require("@harmoniclabs/uplc");
var toOnChain_1 = require("../toOnChain/index.js");
var txBuild_1 = require("../txBuild/index.js");
var ints_1 = require("../utils/ints.js");
var obj_utils_1 = require("@harmoniclabs/obj-utils");
var TxBuilderRunner_1 = require("./TxBuilderRunner/TxBuilderRunner.js");
var CanBeData_1 = require("../utils/CanBeData.js");
var getSpendingPurposeData_1 = require("../toOnChain/getSpendingPurposeData.js");
var TxBuilderProtocolParams_1 = require("./TxBuilderProtocolParams.js");
var utils_1 = require("./utils.js");
var Rational_1 = require("../utils/Rational.js");
var stringify_1 = require("../utils/stringify.js");
var getScript_1 = require("./utils/getScript.js");
// const scriptCache: { [x: string]: UPLCTerm } = {};
function getScriptLikeUplc(scriptLike) {
    return uplc_1.UPLCDecoder.parse(scriptLike.bytes, "flat").body;
}
var TxBuilder = /** @class */ (function () {
    function TxBuilder(protocolParamters, genesisInfos) {
        var _genesisInfos = undefined;
        var _setGenesisInfos = function (genInfos) {
            if (!(0, GenesisInfos_1.isGenesisInfos)(genInfos))
                return;
            _genesisInfos = (0, obj_utils_1.freezeAll)((0, GenesisInfos_1.normalizedGenesisInfos)(genInfos));
        };
        _setGenesisInfos(genesisInfos);
        Object.defineProperties(this, {
            genesisInfos: {
                get: function () { return _genesisInfos; },
                set: _setGenesisInfos,
                enumerable: true,
                configurable: false
            },
            setGenesisInfos: {
                value: _setGenesisInfos,
                writable: false,
                enumerable: true,
                configurable: false
            }
        });
        var pp = (0, TxBuilderProtocolParams_1.completeTxBuilderProtocolParams)(protocolParamters);
        (0, obj_utils_1.defineReadOnlyProperty)(this, "protocolParamters", (0, obj_utils_1.freezeAll)(protocolParamters));
        var costmdls = pp.costModels;
        var costs = (0, cardano_costmodels_ts_1.isCostModelsV3)(costmdls.PlutusScriptV3) ? costmdls.PlutusScriptV3 :
            (0, cardano_costmodels_ts_1.isCostModelsV2)(costmdls.PlutusScriptV2) ? costmdls.PlutusScriptV2 :
                (0, cardano_costmodels_ts_1.isCostModelsV1)(costmdls.PlutusScriptV1) ? costmdls.PlutusScriptV1 :
                    cardano_costmodels_ts_1.defaultV3Costs;
        (0, obj_utils_1.definePropertyIfNotPresent)(this, "cek", {
            // define as getter so that it can be reused without messing around things
            get: function () { return new plutus_machine_1.Machine(__assign({}, costs)); },
            // set does nothing ( aka. readonly )
            set: function () { },
            enumerable: false,
            configurable: false
        });
    }
    TxBuilder.prototype.runWithProvider = function (provider) {
        return new TxBuilderRunner_1.TxBuilderRunner(this, provider);
    };
    TxBuilder.prototype.keepRelevant = function (requestedOutputSet, initialUTxOSet, minimumLovelaceRequired) {
        if (minimumLovelaceRequired === void 0) { minimumLovelaceRequired = 5000000; }
        return (0, keepRelevant_1.keepRelevant)(requestedOutputSet, initialUTxOSet, minimumLovelaceRequired);
    };
    TxBuilder.prototype.calcLinearFee = function (tx) {
        return ((0, ints_1.forceBigUInt)(this.protocolParamters.txFeePerByte) *
            BigInt((tx instanceof cardano_ledger_ts_1.Tx ? tx.toCbor() : tx).toBuffer().length
                + 2 // for good measure
            ) +
            (0, ints_1.forceBigUInt)(this.protocolParamters.txFeeFixed));
    };
    TxBuilder.prototype.calcMinFee = function (tx, minimum) {
        var _a;
        var totRefScriptBytes = ((_a = tx.body.refInputs) !== null && _a !== void 0 ? _a : [])
            .reduce(function (sum, refIn) {
            if (refIn.resolved.refScript)
                return sum + BigInt(refIn.resolved.refScript.toCbor().toBuffer().length
                    + 10 // second Cbor wrap
                );
            return sum;
        }, BigInt(0));
        var minRefScriptFee = this.protocolParamters.minfeeRefScriptCostPerByte ? (totRefScriptBytes * this.protocolParamters.minfeeRefScriptCostPerByte.num /
            this.protocolParamters.minfeeRefScriptCostPerByte.den) : (totRefScriptBytes * (0, Rational_1.cborFromRational)(cardano_ledger_ts_1.defaultProtocolParameters.minfeeRefScriptCostPerByte).num /
            (0, Rational_1.cborFromRational)(cardano_ledger_ts_1.defaultProtocolParameters.minfeeRefScriptCostPerByte).den);
        var minFeeMultiplier = (0, ints_1.forceBigUInt)(this.protocolParamters.txFeePerByte);
        var nVkeyWits = BigInt((0, utils_1.estimateMaxSignersNeeded)(tx));
        var minFee = this.calcLinearFee(tx) +
            minRefScriptFee +
            // consider also vkeys witnesses to be added
            // each vkey witness has fixed size of 102 cbor bytes
            // (1 bytes cbor array tag (length 2)) + (34 cbor bytes of length 32) + (67 cbor bytes of length 64)
            // for a fixed length of 102
            // we also add 2 for possible unwanted encoding
            BigInt(104) * nVkeyWits * minFeeMultiplier +
            // we add some more bytes for the array tag
            BigInt(nVkeyWits < 24 ? 1 : (nVkeyWits < 256 ? 2 : 4)) * minFeeMultiplier;
        if (!(0, ints_1.canBeUInteger)(minimum))
            return minFee;
        var min = (0, ints_1.forceBigUInt)(minimum);
        return minFee < min ? min : minFee;
    };
    TxBuilder.prototype.getMinimumOutputLovelaces = function (tx_out) {
        var size = BigInt(0);
        if (tx_out instanceof cardano_ledger_ts_1.TxOut)
            tx_out = tx_out.toCbor().toBuffer();
        if (typeof tx_out === "string")
            size = BigInt(Math.ceil(tx_out.length / 2));
        else if (!(tx_out instanceof Uint8Array)) {
            if ((0, obj_utils_1.isObject)(tx_out) &&
                (0, obj_utils_1.hasOwn)(tx_out, "toBuffer") &&
                typeof tx_out.toBuffer === "function")
                tx_out = tx_out.toBuffer();
            if (!(tx_out instanceof Uint8Array))
                tx_out = (0, uint8array_utils_1.fromHex)(tx_out.toString());
        }
        if (tx_out instanceof Uint8Array)
            size = BigInt(tx_out.length);
        // overestimating the size a bit
        return BigInt(this.protocolParamters.utxoCostPerByte) * (size + BigInt(10));
    };
    TxBuilder.prototype.addMinLovelacesIfMissing = function (txOut) {
        if (txOut.value.lovelaces > 0)
            return txOut.clone();
        return this.minimizeLovelaces({
            address: txOut.address,
            value: cardano_ledger_ts_1.Value.add(cardano_ledger_ts_1.Value.lovelaces(this.getMinimumOutputLovelaces(txOut) + BigInt(1000000)), txOut.value),
            datum: txOut.datum,
            refScript: txOut.refScript
        });
    };
    TxBuilder.prototype.minimizeLovelaces = function (out, increment) {
        if (increment === void 0) { increment = 0; }
        increment = BigInt(typeof increment === "number" ? increment :
            typeof increment === "bigint" ? increment :
                0);
        var o = out instanceof cardano_ledger_ts_1.TxOut ? out : (0, txBuild_1.txBuildOutToTxOut)(out);
        var minLovelaces = (this.getMinimumOutputLovelaces(o) +
            // somehow we always underestimate a tiny bit
            (BigInt(32) * BigInt(this.protocolParamters.utxoCostPerByte)));
        return new cardano_ledger_ts_1.TxOut({
            address: o.address,
            value: cardano_ledger_ts_1.Value.add(cardano_ledger_ts_1.Value.sub(o.value, cardano_ledger_ts_1.Value.lovelaces(o.value.lovelaces)), cardano_ledger_ts_1.Value.lovelaces(minLovelaces + increment)),
            datum: o.datum,
            refScript: o.refScript
        });
    };
    /**
     *
     * @param slotN number of the slot
     * @returns POSIX time in **milliseconds**
     */
    TxBuilder.prototype.slotToPOSIX = function (slot, genesisInfos) {
        var gInfos = genesisInfos ? (0, GenesisInfos_1.normalizedGenesisInfos)(genesisInfos) : this.genesisInfos;
        if (gInfos === undefined) {
            throw new Error("can't convert slot to POSIX time because genesis infos are missing");
        }
        return (0, toOnChain_1.slotToPOSIX)((0, ints_1.unsafeForceUInt)(slot), gInfos);
    };
    /**
     *
     * @param POSIX POSIX time in milliseconds
     */
    TxBuilder.prototype.posixToSlot = function (POSIX, genesisInfos) {
        var gInfos = genesisInfos ? (0, GenesisInfos_1.normalizedGenesisInfos)(genesisInfos) : this.genesisInfos;
        if (gInfos === undefined) {
            throw new Error("can't convert POSIX to slot time because genesis infos are missing");
        }
        return (0, toOnChain_1.POSIXToSlot)((0, ints_1.unsafeForceUInt)(POSIX), gInfos);
    };
    /**
     * here mainly for forward compability
     *
     * internally calls `buildSync` so really what `build` is doing is wrapping it in a `Promise`
     *
     * In future this method might implement multi-threading using `Worker`s
     */
    TxBuilder.prototype.build = function (buildArgs, buildOpts) {
        if (buildOpts === void 0) { buildOpts = {}; }
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                return [2 /*return*/, this.buildSync(buildArgs, buildOpts)];
            });
        });
    };
    /**
     * replaces the redeemers and clears vkeyWitnesses in the witness set
     * and re-computes the `scriptDataHash` in the body
     *
     * the input transaction is readonly and is not modified
     *
     * **A NEW TRANSACTION IS CREATED** with vkey witness set empty
     * (the new transaction is unsigned)
     *
     * to summarize, the new transaction differs in:
     * 1) `tx.body.scriptDataHash`
     * 2) `tx.witnesses.redeemers`
     * 3) `tx.witnesses.vkeyWitnesses` (empty)
     */
    TxBuilder.prototype.overrideTxRedeemers = function (tx, newRedeemers, opts) {
        var _a;
        if (opts === void 0) { opts = { mustHaveV3: true }; }
        // datums passed by hash
        var datums = (_a = tx.witnesses.datums) !== null && _a !== void 0 ? _a : [];
        return new cardano_ledger_ts_1.Tx(__assign(__assign({}, tx), { body: new cardano_ledger_ts_1.TxBody(__assign(__assign({}, tx.body), { scriptDataHash: getScriptDataHash(tx.witnesses, (0, cardano_costmodels_ts_1.costModelsToLanguageViewCbor)(this.protocolParamters.costModels, opts).toBuffer()) })), witnesses: new cardano_ledger_ts_1.TxWitnessSet(__assign(__assign({}, tx.witnesses), { vkeyWitnesses: [], redeemers: newRedeemers })) }));
    };
    TxBuilder.prototype.buildSync = function (buildArgs, _a) {
        var _b;
        var _c = _a === void 0 ? {} : _a, onScriptInvalid = _c.onScriptInvalid, onScriptResult = _c.onScriptResult;
        var _initBuild = this.initTxBuild(buildArgs);
        var 
        // tx,
        scriptsToExec = _initBuild.scriptsToExec, datumsScriptData = _initBuild.datumsScriptData, languageViews = _initBuild.languageViews, totInputValue = _initBuild.totInputValue, requiredOutputValue = _initBuild.requiredOutputValue, outs = _initBuild.outs, change = _initBuild.change;
        var tx = _initBuild.tx;
        var minFee = _initBuild.minFee;
        var rdmrs = (_b = tx.witnesses.redeemers) !== null && _b !== void 0 ? _b : [];
        var nRdmrs = rdmrs.length;
        if (nRdmrs === 0) {
            this.assertMinOutLovelaces(tx.body.outputs);
            return tx;
        }
        ;
        var txOuts = new Array(outs.length + 1);
        var cek = this.cek;
        if (!(cek instanceof plutus_machine_1.Machine))
            throw new Error("unable to construct transaction including scripts " +
                "if the protocol params are missing the script evaluation costs");
        var executionUnitPrices = this.protocolParamters.executionUnitPrices;
        executionUnitPrices = Array.isArray(executionUnitPrices) ? executionUnitPrices : [
            executionUnitPrices.priceMemory,
            executionUnitPrices.priceSteps,
        ];
        var _d = __read(executionUnitPrices, 2), memRational = _d[0], cpuRational = _d[1];
        memRational = typeof memRational === "number" ? cbor_1.CborPositiveRational.fromNumber(memRational) : memRational;
        cpuRational = typeof cpuRational === "number" ? cbor_1.CborPositiveRational.fromNumber(cpuRational) : cpuRational;
        // group by purpose so we can use the redeemer index to find the script
        var spendScriptsToExec = scriptsToExec.filter(function (elem) { return elem.rdmrTag === cardano_ledger_ts_1.TxRedeemerTag.Spend; });
        var mintScriptsToExec = scriptsToExec.filter(function (elem) { return elem.rdmrTag === cardano_ledger_ts_1.TxRedeemerTag.Mint; });
        var certScriptsToExec = scriptsToExec.filter(function (elem) { return elem.rdmrTag === cardano_ledger_ts_1.TxRedeemerTag.Cert; });
        var withdrawScriptsToExec = scriptsToExec.filter(function (elem) { return elem.rdmrTag === cardano_ledger_ts_1.TxRedeemerTag.Withdraw; });
        var voteScriptsToExec = scriptsToExec.filter(function (elem) { return elem.rdmrTag === cardano_ledger_ts_1.TxRedeemerTag.Voting; });
        var proposeScriptsToExec = scriptsToExec.filter(function (elem) { return elem.rdmrTag === cardano_ledger_ts_1.TxRedeemerTag.Proposing; });
        var maxRound = 3;
        var _isScriptValid = true;
        var fee = minFee;
        var prevFee;
        var _loop_1 = function (round) {
            prevFee = fee;
            var _e = (0, toOnChain_1.getTxInfos)(tx, this_1.genesisInfos), txInfosV1 = _e.v1, txInfosV2 = _e.v2, txInfosV3 = _e.v3;
            var totExBudget = new plutus_machine_1.ExBudget({ mem: 0, cpu: 0 });
            var _loop_2 = function (i) {
                var rdmr = rdmrs[i];
                var tag = rdmr.tag, rdmrData = rdmr.data, rdmr_idx = rdmr.index;
                // "+ 1" because we keep track of lovelaces even if in mint values these are 0
                var index = rdmr_idx + (tag === cardano_ledger_ts_1.TxRedeemerTag.Mint ? 1 : 0);
                // const spendingPurpose = getSpendingPurposeData( rdmr, tx.body );
                var onlyRedeemerArg = function (purposeScriptsToExec) {
                    var _a;
                    var script = (_a = purposeScriptsToExec.find(function (_a) {
                        var idx = _a.index;
                        return idx === index;
                    })) === null || _a === void 0 ? void 0 : _a.script;
                    if (script === undefined)
                        throw new Error("missing script for " + (0, cardano_ledger_ts_1.txRedeemerTagToString)(tag) + " redeemer " + (index - 1));
                    var expectedVersion = (0, utils_1.scriptTypeToDataVersion)(script.type);
                    if (typeof expectedVersion !== "string")
                        throw new Error("unexpected redeemer for native script");
                    var ctxData = getCtx(script.type, (0, getSpendingPurposeData_1.getSpendingPurposeData)(rdmr, tx.body, expectedVersion), (0, getSpendingPurposeData_1.getScriptInfoData)(rdmr, tx.body, expectedVersion), rdmrData, txInfosV1, txInfosV2, txInfosV3);
                    var isV2OrLess = (script.type === cardano_ledger_ts_1.ScriptType.PlutusV1 ||
                        script.type === cardano_ledger_ts_1.ScriptType.PlutusV2 ||
                        script.type === cardano_ledger_ts_1.ScriptType.NativeScript);
                    var _b = cek.eval(isV2OrLess ?
                        new uplc_1.Application(new uplc_1.Application(getScriptLikeUplc(script), uplc_1.UPLCConst.data(rdmrData)), uplc_1.UPLCConst.data(ctxData)) :
                        new uplc_1.Application(getScriptLikeUplc(script), uplc_1.UPLCConst.data(ctxData))), result = _b.result, budgetSpent = _b.budgetSpent, logs = _b.logs;
                    _isScriptValid = onEvaluationResult(i, totExBudget, rdmr, result, budgetSpent, logs, isV2OrLess ? [
                        rdmrData,
                        ctxData
                    ] : [ctxData], rdmrs, script.hash.toString(), onScriptResult, onScriptInvalid);
                };
                if (tag === cardano_ledger_ts_1.TxRedeemerTag.Spend) {
                    var entry = spendScriptsToExec.find(function (_a) {
                        var idx = _a.index;
                        return idx === index;
                    });
                    if (entry === undefined)
                        throw new Error("missing script for spend redeemer " + index);
                    var script = entry.script, datum = entry.datum;
                    var isV2OrLess = script.type === cardano_ledger_ts_1.ScriptType.PlutusV1 || script.type === cardano_ledger_ts_1.ScriptType.PlutusV2;
                    if (datum === undefined && isV2OrLess)
                        throw new Error("missing datum for spend redeemer " + index);
                    var expectedVersion = (0, utils_1.scriptTypeToDataVersion)(script.type);
                    if (typeof expectedVersion !== "string")
                        throw new Error("unexpected redeemer for native script");
                    var ctxData = getCtx(script.type, (0, getSpendingPurposeData_1.getSpendingPurposeData)(rdmr, tx.body, expectedVersion), (0, getSpendingPurposeData_1.getScriptInfoData)(rdmr, tx.body, expectedVersion, datum), rdmrData, txInfosV1, txInfosV2, txInfosV3);
                    var _f = cek.eval(isV2OrLess ?
                        new uplc_1.Application(new uplc_1.Application(new uplc_1.Application(getScriptLikeUplc(script), uplc_1.UPLCConst.data(datum)), uplc_1.UPLCConst.data(rdmrData)), uplc_1.UPLCConst.data(ctxData)) :
                        new uplc_1.Application(getScriptLikeUplc(script), uplc_1.UPLCConst.data(ctxData))), result = _f.result, budgetSpent = _f.budgetSpent, logs = _f.logs;
                    _isScriptValid = onEvaluationResult(i, totExBudget, rdmr, result, budgetSpent, logs, isV2OrLess ? [
                        datum,
                        rdmrData,
                        ctxData
                    ] : [ctxData], rdmrs, script.hash.toString(), onScriptResult, onScriptInvalid);
                }
                else if (tag === cardano_ledger_ts_1.TxRedeemerTag.Mint)
                    onlyRedeemerArg(mintScriptsToExec);
                else if (tag === cardano_ledger_ts_1.TxRedeemerTag.Cert)
                    onlyRedeemerArg(certScriptsToExec);
                else if (tag === cardano_ledger_ts_1.TxRedeemerTag.Withdraw)
                    onlyRedeemerArg(withdrawScriptsToExec);
                else if (tag === cardano_ledger_ts_1.TxRedeemerTag.Voting)
                    onlyRedeemerArg(voteScriptsToExec);
                else if (tag === cardano_ledger_ts_1.TxRedeemerTag.Proposing)
                    onlyRedeemerArg(proposeScriptsToExec);
                else
                    throw new Error("unrecoignized redeemer tag " + tag);
            };
            for (var i = 0; i < nRdmrs; i++) {
                _loop_2(i);
            }
            minFee = this_1.calcMinFee(tx, buildArgs.fee);
            fee = minFee +
                ((totExBudget.mem * memRational.num) / memRational.den) +
                ((totExBudget.cpu * cpuRational.num) / cpuRational.den) +
                // bigint division truncates always towards 0;
                // we don't like that so we add `1n` for both divisions ( + 2n )
                BigInt(2);
            if (fee === prevFee)
                return "break"; // return last transaciton
            // reset for next loop
            // no need to reset if there's no next loop
            if (round === maxRound - 1)
                return "break";
            for (var i = 0; i < outs.length; i++) {
                txOuts[i] = this_1.addMinLovelacesIfMissing(outs[i]);
            }
            txOuts[txOuts.length - 1] = (new cardano_ledger_ts_1.TxOut({
                address: change.address,
                value: cardano_ledger_ts_1.Value.sub(totInputValue, cardano_ledger_ts_1.Value.add(requiredOutputValue, cardano_ledger_ts_1.Value.lovelaces(fee))),
                datum: change.datum ? ((0, cardano_ledger_ts_1.canBeHash32)(change.datum) ?
                    new cardano_ledger_ts_1.Hash32(change.datum) :
                    (0, CanBeData_1.forceData)(change.datum)) : undefined,
                refScript: change.refScript
            }));
            var nextWitnesses = new cardano_ledger_ts_1.TxWitnessSet(__assign(__assign({}, tx.witnesses), { redeemers: rdmrs }));
            tx = new cardano_ledger_ts_1.Tx(__assign(__assign({}, tx), { body: new cardano_ledger_ts_1.TxBody(__assign(__assign({}, tx.body), { outputs: txOuts.slice(), fee: fee, scriptDataHash: getScriptDataHash(nextWitnesses, languageViews) })), witnesses: nextWitnesses, isScriptValid: _isScriptValid }));
            _isScriptValid = true;
            totExBudget = new plutus_machine_1.ExBudget({ mem: 0, cpu: 0 });
        };
        var this_1 = this;
        for (var round = 0; round < maxRound; round++) {
            var state_1 = _loop_1(round);
            if (state_1 === "break")
                break;
        }
        this.assertMinOutLovelaces(tx.body.outputs);
        return tx;
    };
    TxBuilder.prototype.validatePhaseTwoVerbose = function (tx) {
        var txBody = tx.body;
        var rdmrs = tx.witnesses.redeemers;
        if (!Array.isArray(rdmrs) || rdmrs.length === 0)
            return [];
        var nRdmrs = rdmrs.length;
        var evalResults = [];
        var cek = this.cek;
        if (!(cek instanceof plutus_machine_1.Machine))
            throw new Error("protocol params are missing the script evaluation costs");
        var executionUnitPrices = this.protocolParamters.executionUnitPrices;
        executionUnitPrices = Array.isArray(executionUnitPrices) ? executionUnitPrices : [
            executionUnitPrices.priceMemory,
            executionUnitPrices.priceSteps,
        ];
        var _a = __read(executionUnitPrices, 2), memRational = _a[0], cpuRational = _a[1];
        memRational = typeof memRational === "number" ? cbor_1.CborPositiveRational.fromNumber(memRational) : memRational;
        cpuRational = typeof cpuRational === "number" ? cbor_1.CborPositiveRational.fromNumber(cpuRational) : cpuRational;
        var _b = (0, toOnChain_1.getTxInfos)(tx, this.genesisInfos), txInfosV1 = _b.v1, txInfosV2 = _b.v2, txInfosV3 = _b.v3;
        var _loop_3 = function (i) {
            var rdmr = rdmrs[i];
            var tag = rdmr.tag, rdmrData = rdmr.data, rdmr_idx = rdmr.index;
            // "+ 1" because we keep track of lovelaces even if in mint values these are 0
            var index = rdmr_idx + (tag === cardano_ledger_ts_1.TxRedeemerTag.Mint ? 1 : 0);
            var onlyRedeemerArg = function (script) {
                if (!(script instanceof cardano_ledger_ts_1.Script)) {
                    evalResults.push({
                        result: new uplc_1.ErrorUPLC("missig script"),
                        budgetSpent: new plutus_machine_1.ExBudget({ mem: 0, cpu: 0 }),
                        logs: [],
                        rdmr: rdmr,
                        isV2OrLess: false
                    });
                    return;
                }
                var expectedVersion = (0, utils_1.scriptTypeToDataVersion)(script.type);
                if (typeof expectedVersion !== "string")
                    throw new Error("unexpected redeemer for native script");
                var ctxData = getCtx(script.type, (0, getSpendingPurposeData_1.getSpendingPurposeData)(rdmr, tx.body, expectedVersion), (0, getSpendingPurposeData_1.getScriptInfoData)(rdmr, tx.body, expectedVersion), rdmrData, txInfosV1, txInfosV2, txInfosV3);
                var isV2OrLess = (script.type === cardano_ledger_ts_1.ScriptType.PlutusV1 ||
                    script.type === cardano_ledger_ts_1.ScriptType.PlutusV2 ||
                    script.type === cardano_ledger_ts_1.ScriptType.NativeScript);
                evalResults.push(__assign(__assign({}, cek.eval(isV2OrLess ?
                    new uplc_1.Application(new uplc_1.Application((0, uplc_1.parseUPLC)(script.bytes).body, uplc_1.UPLCConst.data(rdmrData)), uplc_1.UPLCConst.data(ctxData)) :
                    new uplc_1.Application((0, uplc_1.parseUPLC)(script.bytes).body, uplc_1.UPLCConst.data(ctxData)))), { rdmr: rdmr, isV2OrLess: isV2OrLess }));
            };
            if (tag === cardano_ledger_ts_1.TxRedeemerTag.Spend) {
                var entry = (0, getScript_1.getSpendingScript)(tx, index);
                if (!entry) {
                    evalResults.push({
                        result: new uplc_1.ErrorUPLC("missig script"),
                        budgetSpent: new plutus_machine_1.ExBudget({ mem: 0, cpu: 0 }),
                        logs: [],
                        rdmr: rdmr,
                        isV2OrLess: false
                    });
                    return "continue";
                }
                ;
                var script = entry.script, datum = entry.datum;
                // TODO: check if this is correct
                // I'm assuming native scripts are phase 1
                if (script.type === cardano_ledger_ts_1.ScriptType.NativeScript)
                    return "continue";
                var isV2OrLess = script.type === cardano_ledger_ts_1.ScriptType.PlutusV1 || script.type === cardano_ledger_ts_1.ScriptType.PlutusV2;
                if (datum === undefined && isV2OrLess)
                    throw new Error("missing datum for spend redeemer " + index);
                var expectedVersion = (0, utils_1.scriptTypeToDataVersion)(script.type);
                if (typeof expectedVersion !== "string")
                    throw new Error("unexpected redeemer for native script");
                var ctxData = getCtx(script.type, (0, getSpendingPurposeData_1.getSpendingPurposeData)(rdmr, tx.body, expectedVersion), (0, getSpendingPurposeData_1.getScriptInfoData)(rdmr, tx.body, expectedVersion, datum), rdmrData, txInfosV1, txInfosV2, txInfosV3);
                evalResults.push(__assign(__assign({}, cek.eval(isV2OrLess ?
                    new uplc_1.Application(new uplc_1.Application(new uplc_1.Application((0, uplc_1.parseUPLC)(script.bytes).body, uplc_1.UPLCConst.data(datum)), uplc_1.UPLCConst.data(rdmrData)), uplc_1.UPLCConst.data(ctxData)) :
                    new uplc_1.Application((0, uplc_1.parseUPLC)(script.bytes).body, uplc_1.UPLCConst.data(ctxData)))), { rdmr: rdmr, isV2OrLess: isV2OrLess }));
                return "continue";
            }
            else if (tag === cardano_ledger_ts_1.TxRedeemerTag.Mint)
                onlyRedeemerArg((0, getScript_1.getMintingScript)(tx, index));
            else if (tag === cardano_ledger_ts_1.TxRedeemerTag.Cert)
                onlyRedeemerArg((0, getScript_1.getCeritficateScript)(tx, index));
            else if (tag === cardano_ledger_ts_1.TxRedeemerTag.Withdraw)
                onlyRedeemerArg((0, getScript_1.getWithdrawalScript)(tx, index));
            else if (tag === cardano_ledger_ts_1.TxRedeemerTag.Voting)
                onlyRedeemerArg((0, getScript_1.getVotingScript)(tx, index));
            else if (tag === cardano_ledger_ts_1.TxRedeemerTag.Proposing)
                onlyRedeemerArg((0, getScript_1.getProposingScript)(tx, index));
            else
                return "continue";
        };
        for (var i = 0; i < nRdmrs; i++) {
            _loop_3(i);
        } // for loop over redeemers
        return evalResults;
    };
    TxBuilder.prototype.validatePhaseTwo = function (tx) {
        var totExBudget = new plutus_machine_1.ExBudget({ mem: 0, cpu: 0 });
        var results = this.validatePhaseTwoVerbose(tx);
        return results.every(function (_a) {
            var result = _a.result, budgetSpent = _a.budgetSpent, logs = _a.logs, isV2OrLess = _a.isV2OrLess, rdmr = _a.rdmr;
            var successExec = isV2OrLess ?
                !(result instanceof uplc_1.ErrorUPLC) :
                ( // v3 requires to return unit
                result instanceof plutus_machine_1.CEKConst
                    && Array.isArray(result.type)
                    && result.type.length === 1
                    && result.type[0] === uplc_1.ConstTyTag.unit
                    && result.value === undefined);
            if (!successExec)
                return false;
            if (budgetSpent.cpu > rdmr.execUnits.cpu
                || budgetSpent.mem > rdmr.execUnits.mem)
                return false;
            totExBudget.add(rdmr.execUnits);
            return true;
        })
            && totExBudget.cpu <= this.protocolParamters.maxTxExecutionUnits.cpu
            && totExBudget.mem <= this.protocolParamters.maxTxExecutionUnits.mem;
    };
    TxBuilder.prototype.assertMinOutLovelaces = function (txOuts) {
        for (var i = 0; i < txOuts.length; i++) {
            var out = txOuts[i];
            var minLovelaces = this.getMinimumOutputLovelaces(out);
            if (out.value.lovelaces < minLovelaces)
                throw new Error("tx output at index ".concat(i, " did not have enough lovelaces to meet the minimum allowed by protocol parameters.\n") +
                    "output size: ".concat(out.toCbor().toBuffer().length, " bytes\n") +
                    "protocol paramters \"utxoCostPerByte\": ".concat(this.protocolParamters.utxoCostPerByte, "\n") +
                    "minimum lovelaces required: ".concat(minLovelaces.toString(), "\n") +
                    "output lovelaces          : ".concat(out.value.lovelaces.toString(), "\n") +
                    "tx output: ".concat((0, stringify_1.stringify)(out.toJson(), undefined, 2)));
        }
    };
    /**
     * extracts the important data from the input
     * and returns it in an easier way to opearte with
     *
     * if the transaction is simple enough (aka. it doesn't include plutus scripts)
     * this is all that either `build` or `buildSync` needs to do
    **/
    TxBuilder.prototype.initTxBuild = function (buildArgs) {
        var _this = this;
        var _a = (0, txBuild_1.normalizeITxBuildArgs)(buildArgs), outputs = _a.outputs, requiredSigners = _a.requiredSigners, mints = _a.mints, invalidAfter = _a.invalidAfter, certificates = _a.certificates, withdrawals = _a.withdrawals, memo = _a.memo, metadata = _a.metadata, votingProcedures = _a.votingProcedures, proposalProcedures = _a.proposalProcedures, currentTreasuryValue = _a.currentTreasuryValue, paymentToTreasury = _a.paymentToTreasury, args = __rest(_a, ["outputs", "requiredSigners", "mints", "invalidAfter", "certificates", "withdrawals", "memo", "metadata", "votingProcedures", "proposalProcedures", "currentTreasuryValue", "paymentToTreasury"]);
        // mutable args
        var inputs = args.inputs, changeAddress = args.changeAddress, change = args.change, invalidBefore = args.invalidBefore, readonlyRefInputs = args.readonlyRefInputs, collaterals = args.collaterals, collateralReturn = args.collateralReturn;
        if (change)
            changeAddress = change.address;
        if (!changeAddress)
            throw new Error("missing changAddress and change entry while constructing a transaciton; unable to balance inputs and outpus");
        if (!change)
            change = { address: changeAddress };
        var network = changeAddress.network;
        if (!(0, GenesisInfos_1.isNormalizedGenesisInfos)(this.genesisInfos)) {
            this.setGenesisInfos(network === "mainnet" ?
                GenesisInfos_1.defaultMainnetGenesisInfos :
                GenesisInfos_1.defaultPreprodGenesisInfos);
        }
        var undef = void 0;
        // filter inputs so that are unique
        inputs = inputs.reduce(function (accum, input) {
            var samePresent = accum.some(function (_a) {
                var accumUtxo = _a.utxo;
                return eqUTxOByRef(accumUtxo, input.utxo);
            });
            if (!samePresent)
                accum.push(input);
            return accum;
        }, []);
        // filter refIns so that are unique
        readonlyRefInputs = readonlyRefInputs === null || readonlyRefInputs === void 0 ? void 0 : readonlyRefInputs.reduce(function (accum, utxo) {
            var samePresent = accum.some(function (accumUtxo) { return eqUTxOByRef(accumUtxo, utxo); });
            if (!samePresent)
                accum.push(utxo);
            return accum;
        }, []);
        var totInputValue = cardano_ledger_ts_1.Value.zero;
        var refIns = readonlyRefInputs !== null && readonlyRefInputs !== void 0 ? readonlyRefInputs : [];
        var outs = outputs !== null && outputs !== void 0 ? outputs : [];
        var requiredOutputValue = outs.reduce(function (acc, out) { return cardano_ledger_ts_1.Value.add(acc, out.value); }, cardano_ledger_ts_1.Value.zero);
        var vkeyWitnesses = [];
        var nativeScriptsWitnesses = [];
        var bootstrapWitnesses = [];
        var plutusV1ScriptsWitnesses = [];
        var datums = [];
        var plutusV2ScriptsWitnesses = [];
        var plutusV3ScriptsWitnesses = [];
        var dummyExecBudget = plutus_machine_1.ExBudget.maxCborSize;
        var spendRedeemers = [];
        var mintRedeemers = [];
        var certRedeemers = [];
        var withdrawRedeemers = [];
        var voteRedeemers = [];
        var proposeRedeemers = [];
        var scriptsToExec = [];
        /**
         * needed in `getScriptDataHash` to understand whoich cost model to transform in language view
         */
        var _hasV1Scripts = false;
        /**
         * needed in `getScriptDataHash` to understand whoich cost model to transform in language view
         */
        var _hasV2Scripts = false;
        /**
         * needed in `getScriptDataHash` to understand whoich cost model to transform in language view
         */
        var _hasV3Scripts = false;
        function pushScriptToExec(idx, tag, script, datum) {
            if (script.type == cardano_ledger_ts_1.ScriptType.NativeScript)
                return;
            // keep track of exsisting csript versions
            if (!_hasV1Scripts && script.type === "PlutusScriptV1") {
                _hasV1Scripts = true;
            }
            else if (!_hasV2Scripts && script.type === "PlutusScriptV2") {
                _hasV2Scripts = true;
            }
            else if (!_hasV3Scripts && script.type === "PlutusScriptV3") {
                _hasV3Scripts = true;
            }
            scriptsToExec.push({
                index: idx,
                rdmrTag: tag,
                script: {
                    type: script.type,
                    bytes: script.bytes,
                    hash: script.hash.toString()
                },
                datum: datum
            });
        }
        function pushWitScript(script) {
            var t = script.type;
            if (t === "NativeScript")
                pushUniqueScript(nativeScriptsWitnesses, script);
            else if (t === "PlutusScriptV1")
                pushUniqueScript(plutusV1ScriptsWitnesses, script);
            else if (t === "PlutusScriptV2")
                pushUniqueScript(plutusV2ScriptsWitnesses, script);
            else if (t === "PlutusScriptV3")
                pushUniqueScript(plutusV3ScriptsWitnesses, script);
        }
        /**
         * @returns `Script` to execute
         */
        function checkScriptAndPushIfInline(script) {
            var _a, _b;
            if ((0, obj_utils_1.hasOwn)(script, "inline")) {
                if ((0, obj_utils_1.hasOwn)(script, "ref"))
                    throw new Error("multiple scripts specified");
                pushWitScript(script.inline);
                return script.inline;
            }
            if ((0, obj_utils_1.hasOwn)(script, "ref")) {
                if ((0, obj_utils_1.hasOwn)(script, "inline"))
                    throw new Error("multiple scripts specified");
                var refScript = (_b = (_a = script.ref) === null || _a === void 0 ? void 0 : _a.resolved) === null || _b === void 0 ? void 0 : _b.refScript;
                if (refScript === (void 0))
                    throw new Error("script was specified to be a reference script " +
                        "but the provided utxo is missing any attached script");
                if (!refIns.some(function (u) { return eqUTxOByRef(u, script.ref); })) {
                    refIns.push(script.ref);
                }
                return refScript;
            }
            throw new Error("unexpected execution flow 'checkScriptAndPushIfInline' in TxBuilder");
        }
        /**
         *
         * @param datum
         * @param inlineDatum
         * @returns the `Data` of the datum
         */
        function pushWitDatum(datum, inlineDatum, scriptType) {
            if (scriptType === cardano_ledger_ts_1.ScriptType.NativeScript)
                return undef;
            if (datum === "inline")
                datum = undef;
            if (scriptType === cardano_ledger_ts_1.ScriptType.PlutusV1 ||
                scriptType === cardano_ledger_ts_1.ScriptType.PlutusV2) {
                if (datum === undef) {
                    if (!(0, CanBeData_1.canBeData)(inlineDatum))
                        throw new Error("datum was specified to be inline; but inline datum is missing");
                    // no need to push to witnesses
                    return (0, CanBeData_1.forceData)(inlineDatum);
                }
                else {
                    var dat = (0, CanBeData_1.forceData)(datum);
                    // add datum to witnesses
                    // the node finds it trough the datum hash (on the utxo)
                    datums.push(dat);
                    return dat;
                }
            }
            if (datum === undef) {
                if ((0, CanBeData_1.canBeData)(inlineDatum)) {
                    // no need to push to witnesses
                    return (0, CanBeData_1.forceData)(inlineDatum);
                }
                if (inlineDatum instanceof cardano_ledger_ts_1.Hash32)
                    throw new Error("datum hash specified on spending utxo, but resolved datum is missing");
                return undef;
            }
            else if (inlineDatum instanceof cardano_ledger_ts_1.Hash32) {
                var dat = (0, CanBeData_1.forceData)(datum);
                // add datum to witnesses
                // the node finds it trough the datum hash (on the utxo)
                datums.push(dat);
                return dat;
            }
            return undef;
        }
        var isScriptValid = true;
        // `sort` mutates the array; so we `slice` (clone) first
        var sortedIns = inputs.slice().sort(function (a, b) {
            var ord = (0, uint8array_utils_1.lexCompare)(a.utxo.utxoRef.id.toBuffer(), b.utxo.utxoRef.id.toBuffer());
            // if equal tx id order based on tx output index
            if (ord === 0)
                return a.utxo.utxoRef.index - b.utxo.utxoRef.index;
            // else order by tx id
            return ord;
        });
        var _inputs = inputs.map(function (input) {
            var utxo = input.utxo, referenceScript = input.referenceScript, inputScript = input.inputScript, nativeScript = input.nativeScript;
            var addr = utxo.resolved.address;
            totInputValue = cardano_ledger_ts_1.Value.add(totInputValue, utxo.resolved.value);
            if (addr.paymentCreds.type === cardano_ledger_ts_1.CredentialType.Script &&
                referenceScript === undef &&
                inputScript === undef &&
                nativeScript === undef)
                throw new Error("spending script utxo \"" + utxo.utxoRef.toString() + "\" without script source");
            if (referenceScript !== undef) {
                if (inputScript !== undef || nativeScript !== undef)
                    throw new Error("invalid input; multiple scripts specified");
                var datum = referenceScript.datum, redeemer = referenceScript.redeemer, refUtxo_1 = referenceScript.refUtxo;
                var refScript = refUtxo_1.resolved.refScript;
                if (refScript === undefined)
                    throw new Error("reference utxo specified (" + refUtxo_1.toString() + ") is missing an attached reference Script");
                var sameRefPresent = refIns.find(function (u) { return eqUTxOByRef(u, refUtxo_1); });
                if (sameRefPresent === undef) {
                    refIns.push(refUtxo_1);
                }
                var dat = pushWitDatum(datum, utxo.resolved.datum, refScript.type);
                var i_1 = sortedIns.indexOf(input);
                if (i_1 < 0)
                    throw new Error("input missing in sorted");
                spendRedeemers.push(new cardano_ledger_ts_1.TxRedeemer({
                    data: (0, CanBeData_1.forceData)(redeemer),
                    index: i_1,
                    execUnits: dummyExecBudget.clone(),
                    tag: cardano_ledger_ts_1.TxRedeemerTag.Spend
                }));
                pushScriptToExec(i_1, cardano_ledger_ts_1.TxRedeemerTag.Spend, refScript, dat);
            }
            if (inputScript !== undefined) {
                if (referenceScript !== undefined || nativeScript !== undef)
                    throw new Error("invalid input; multiple scripts specified");
                var datum = inputScript.datum, redeemer = inputScript.redeemer, script = inputScript.script;
                pushWitScript(script);
                var dat = pushWitDatum(datum, utxo.resolved.datum, script.type);
                var i_2 = sortedIns.indexOf(input);
                if (i_2 < 0)
                    throw new Error("input missing in sorted");
                spendRedeemers.push(new cardano_ledger_ts_1.TxRedeemer({
                    data: (0, CanBeData_1.forceData)(redeemer),
                    index: i_2,
                    execUnits: dummyExecBudget.clone(),
                    tag: cardano_ledger_ts_1.TxRedeemerTag.Spend
                }));
                pushScriptToExec(i_2, cardano_ledger_ts_1.TxRedeemerTag.Spend, script, dat);
            }
            if (nativeScript instanceof cardano_ledger_ts_1.Script && nativeScript.type === cardano_ledger_ts_1.ScriptType.NativeScript) {
                if (referenceScript !== undefined || inputScript !== undef)
                    throw new Error("invalid input; multiple scripts specified");
                pushWitScript(nativeScript);
            }
            return new cardano_ledger_ts_1.TxIn(utxo);
        });
        // good luck spending more than 4294.967295 ADA in fees
        // also 16.777215 ADA (3 bytes) is a lot; but CBOR only uses 2 or 4 bytes integers
        // and 2 are ~0.06 ADA (too low) so go for 4;
        var dummyFee = BigInt("0xffffffff");
        var dummyOuts = outs.map(function (txO) { return txO.clone(); });
        // add dummy change address output
        dummyOuts.push(new cardano_ledger_ts_1.TxOut({
            address: change.address,
            // this value obviously doesn't make sense
            // however now we are not able to calculate it
            // because we are missing the minted value and eventual
            // values associated with certificates
            value: totInputValue.clone(),
            datum: change.datum ? (change.datum instanceof cardano_ledger_ts_1.Hash32 ?
                change.datum :
                (0, CanBeData_1.forceData)(change.datum)) : undef,
            refScript: change.refScript
        }));
        // index to be modified
        var dummyMintRedeemers = [];
        var _mint = mints === null || mints === void 0 ? void 0 : mints.reduce(function (accum, _a, i) {
            var script = _a.script, value = _a.value;
            var redeemer = script.redeemer;
            var policyId = value.policy;
            var toExec = checkScriptAndPushIfInline(script);
            dummyMintRedeemers.push([
                policyId,
                toExec,
                new cardano_ledger_ts_1.TxRedeemer({
                    data: (0, CanBeData_1.forceData)(redeemer),
                    index: i,
                    execUnits: dummyExecBudget.clone(),
                    tag: cardano_ledger_ts_1.TxRedeemerTag.Mint
                })
            ]);
            return cardano_ledger_ts_1.Value.add(accum, new cardano_ledger_ts_1.Value([value]));
        }, cardano_ledger_ts_1.Value.zero);
        totInputValue = _mint instanceof cardano_ledger_ts_1.Value ? cardano_ledger_ts_1.Value.add(totInputValue, _mint) : totInputValue;
        function indexOfPolicy(policy) {
            var _a;
            var policyStr = policy.toString();
            return (_a = _mint === null || _mint === void 0 ? void 0 : _mint.map.findIndex(function (entry) { return entry.policy.toString() === policyStr; })) !== null && _a !== void 0 ? _a : -1;
        }
        dummyMintRedeemers.forEach(function (_a) {
            var _b = __read(_a, 3), policy = _b[0], toExec = _b[1], dummyRdmr = _b[2];
            var i = indexOfPolicy(policy);
            mintRedeemers.push(new cardano_ledger_ts_1.TxRedeemer({
                data: dummyRdmr.data,
                index: i - 1,
                execUnits: dummyRdmr.execUnits,
                tag: cardano_ledger_ts_1.TxRedeemerTag.Mint
            }));
            pushScriptToExec(i, cardano_ledger_ts_1.TxRedeemerTag.Mint, toExec);
        });
        var _certs = certificates === null || certificates === void 0 ? void 0 : certificates.map(function (_a, i) {
            var cert = _a.cert, script = _a.script;
            if (script !== undef) {
                certRedeemers.push(new cardano_ledger_ts_1.TxRedeemer({
                    data: (0, CanBeData_1.forceData)(script.redeemer),
                    index: i,
                    execUnits: dummyExecBudget.clone(),
                    tag: cardano_ledger_ts_1.TxRedeemerTag.Cert
                }));
                var toExec = checkScriptAndPushIfInline(script);
                pushScriptToExec(i, cardano_ledger_ts_1.TxRedeemerTag.Cert, toExec);
            }
            return cert;
        });
        var _wits = withdrawals === null || withdrawals === void 0 ? void 0 : withdrawals.sort(function (_a, _b) {
            var fst = _a.withdrawal;
            var snd = _b.withdrawal;
            return (0, uint8array_utils_1.lexCompare)(fst.rewardAccount instanceof cardano_ledger_ts_1.Hash28 ?
                fst.rewardAccount.toBuffer() :
                fst.rewardAccount.credentials.toBuffer(), snd.rewardAccount instanceof cardano_ledger_ts_1.Hash28 ?
                snd.rewardAccount.toBuffer() :
                snd.rewardAccount.credentials.toBuffer());
        }).map(function (_a, i) {
            var withdrawal = _a.withdrawal, script = _a.script;
            if (script !== undef) {
                withdrawRedeemers.push(new cardano_ledger_ts_1.TxRedeemer({
                    data: (0, CanBeData_1.forceData)(script.redeemer),
                    index: i,
                    execUnits: dummyExecBudget.clone(),
                    tag: cardano_ledger_ts_1.TxRedeemerTag.Withdraw
                }));
                var toExec = checkScriptAndPushIfInline(script);
                pushScriptToExec(i, cardano_ledger_ts_1.TxRedeemerTag.Withdraw, toExec);
            }
            return withdrawal;
        });
        var i = 0;
        var _votingProcedures = Array.isArray(votingProcedures) ?
            new cardano_ledger_ts_1.VotingProcedures(votingProcedures === null || votingProcedures === void 0 ? void 0 : votingProcedures.map(function (_a) {
                var votingProcedure = _a.votingProcedure, script = _a.script;
                if (script !== undef) {
                    voteRedeemers.push(new cardano_ledger_ts_1.TxRedeemer({
                        data: (0, CanBeData_1.forceData)(script.redeemer),
                        index: i,
                        execUnits: dummyExecBudget.clone(),
                        tag: cardano_ledger_ts_1.TxRedeemerTag.Voting
                    }));
                    var toExec = checkScriptAndPushIfInline(script);
                    pushScriptToExec(i, cardano_ledger_ts_1.TxRedeemerTag.Voting, toExec);
                    i++;
                }
                return votingProcedure;
            })) : undef;
        var _proposalProcedures = Array.isArray(proposalProcedures) ?
            proposalProcedures.map(function (_a) {
                var proposalProcedure = _a.proposalProcedure, script = _a.script;
                if (script !== undef) {
                    proposeRedeemers.push(new cardano_ledger_ts_1.TxRedeemer({
                        data: (0, CanBeData_1.forceData)(script.redeemer),
                        index: i,
                        execUnits: dummyExecBudget.clone(),
                        tag: cardano_ledger_ts_1.TxRedeemerTag.Proposing
                    }));
                    var toExec = checkScriptAndPushIfInline(script);
                    pushScriptToExec(i, cardano_ledger_ts_1.TxRedeemerTag.Proposing, toExec);
                    i++;
                }
                return new cardano_ledger_ts_1.ProposalProcedure(proposalProcedure);
            }) : undef;
        i = 0;
        var auxData = undef;
        var _metadata = metadata;
        if (memo !== undef) {
            _metadata = new cardano_ledger_ts_1.TxMetadata(__assign(__assign({}, _metadata === null || _metadata === void 0 ? void 0 : _metadata.metadata), { 674: new cardano_ledger_ts_1.TxMetadatumMap([
                    {
                        k: new cardano_ledger_ts_1.TxMetadatumText("msg"),
                        v: new cardano_ledger_ts_1.TxMetadatumList(splitStringByByteLength(memo, 64)
                            .map(function (chunk) { return new cardano_ledger_ts_1.TxMetadatumText(chunk); }))
                    }
                ]) }));
        }
        if (_metadata !== undef) {
            auxData = new cardano_ledger_ts_1.AuxiliaryData({
                metadata: _metadata,
            });
        }
        var redeemers = spendRedeemers
            .concat(mintRedeemers)
            .concat(withdrawRedeemers)
            .concat(certRedeemers)
            .concat(voteRedeemers)
            .concat(proposeRedeemers);
        var dummyTxWitnesses = new cardano_ledger_ts_1.TxWitnessSet({
            vkeyWitnesses: vkeyWitnesses,
            bootstrapWitnesses: bootstrapWitnesses,
            datums: datums,
            redeemers: redeemers,
            nativeScripts: nativeScriptsWitnesses,
            plutusV1Scripts: plutusV1ScriptsWitnesses,
            plutusV2Scripts: plutusV2ScriptsWitnesses,
            plutusV3Scripts: plutusV3ScriptsWitnesses
        });
        var datumsScriptData = datums.length > 0 ?
            Array.from(cbor_1.Cbor.encode(new cbor_1.CborArray(datums.map(plutus_data_1.dataToCborObj))).toBuffer())
            : [];
        var languageViews = (0, cardano_costmodels_ts_1.costModelsToLanguageViewCbor)(this.protocolParamters.costModels, {
            mustHaveV1: _hasV1Scripts,
            mustHaveV2: _hasV2Scripts,
            mustHaveV3: _hasV3Scripts
        }).toBuffer();
        invalidBefore = invalidBefore === undef ? undef : (0, ints_1.forceBigUInt)(invalidBefore);
        // if( invalidAfter !== undef )
        // {
        //     if( invalidBefore === undef ) invalidBefore = 0;
        // }
        if ((0, ints_1.canBeUInteger)(invalidBefore) &&
            (0, ints_1.canBeUInteger)(invalidAfter)) {
            if (invalidBefore >= invalidAfter)
                throw new Error("invalid validity interval; invalidAfter: "
                    + invalidAfter.toString() +
                    "; was smaller (previous point in time) than invalidBefore:"
                    + invalidBefore.toString());
        }
        // assert collateral is present if needed
        if (scriptsToExec.filter(function (s) { return s.script.type !== "NativeScript"; }).length > 0) {
            if (!Array.isArray(collaterals) ||
                collaterals.length <= 0) {
                collaterals = this.findCollaterals(_inputs);
                collateralReturn = undef;
            }
            if (collaterals.length <= 0)
                throw new Error("collaterals missing, and no input could be used");
            var collateralValue = collaterals.reduce(function (accum, collateral) { return cardano_ledger_ts_1.Value.add(accum, collateral.resolved.value); }, cardano_ledger_ts_1.Value.zero);
            if (!cardano_ledger_ts_1.Value.isAdaOnly(collateralValue)) {
                if (!collateralReturn) {
                    var addr = collaterals[0].resolved.address;
                    collateralReturn = new cardano_ledger_ts_1.TxOut({
                        address: addr,
                        value: collateralValue
                    });
                    var minAda = this.getMinimumOutputLovelaces(collateralReturn);
                    collateralReturn = new cardano_ledger_ts_1.TxOut({
                        address: addr,
                        value: cardano_ledger_ts_1.Value.sub(collateralValue, cardano_ledger_ts_1.Value.lovelaces(collateralValue.lovelaces - minAda))
                    });
                }
                var realCollValue = cardano_ledger_ts_1.Value.sub(collateralValue, collateralReturn.value);
                if (!cardano_ledger_ts_1.Value.isAdaOnly(realCollValue))
                    throw new Error("total collateral value was including non-ADA value;\n" +
                        "total collateral value was: ".concat((0, stringify_1.stringify)(realCollValue.toJson(), undef, 2)));
            }
        }
        var dummyTx = new cardano_ledger_ts_1.Tx({
            body: new cardano_ledger_ts_1.TxBody({
                inputs: _inputs,
                outputs: dummyOuts,
                fee: dummyFee,
                mint: _mint,
                certs: _certs,
                withdrawals: _wits,
                refInputs: refIns.length === 0 ? undef : refIns.map(function (refIn) { return refIn instanceof cardano_ledger_ts_1.TxIn ? refIn : new cardano_ledger_ts_1.TxIn(refIn); }),
                // protocolUpdate: protocolUpdateProposal,
                requiredSigners: requiredSigners,
                collateralInputs: collaterals,
                collateralReturn: collateralReturn === undef ?
                    undef :
                    (0, txBuild_1.txBuildOutToTxOut)(collateralReturn),
                totCollateral: undef,
                validityIntervalStart: invalidBefore === undef ?
                    undef :
                    (0, ints_1.forceBigUInt)(invalidBefore),
                ttl: invalidAfter === undef ?
                    undef :
                    (0, ints_1.forceBigUInt)(invalidAfter),
                auxDataHash: auxData === null || auxData === void 0 ? void 0 : auxData.hash,
                scriptDataHash: getScriptDataHash(dummyTxWitnesses, languageViews),
                network: network,
                votingProcedures: _votingProcedures,
                proposalProcedures: _proposalProcedures,
                currentTreasuryValue: currentTreasuryValue,
                donation: paymentToTreasury,
            }),
            witnesses: dummyTxWitnesses,
            auxiliaryData: auxData,
            isScriptValid: isScriptValid
        });
        var minFee = this.calcMinFee(dummyTx, args.fee);
        var txOuts = new Array(outs.length + 1);
        outs.forEach(function (txO, i) { return txOuts[i] = _this.addMinLovelacesIfMissing(txO); });
        var changeOutput = new cardano_ledger_ts_1.TxOut({
            address: change.address,
            value: cardano_ledger_ts_1.Value.sub(totInputValue, cardano_ledger_ts_1.Value.add(requiredOutputValue, cardano_ledger_ts_1.Value.lovelaces(minFee))),
            datum: change.datum ? (change.datum instanceof cardano_ledger_ts_1.Hash32 ?
                change.datum :
                (0, CanBeData_1.forceData)(change.datum)) : undef,
            refScript: change.refScript
        });
        txOuts[txOuts.length - 1] = changeOutput;
        this.assertCorrectChangeOutput(changeOutput);
        var tx = new cardano_ledger_ts_1.Tx(__assign(__assign({}, dummyTx), { body: new cardano_ledger_ts_1.TxBody(__assign(__assign({}, dummyTx.body), { outputs: txOuts, fee: minFee })) }));
        return {
            tx: tx,
            scriptsToExec: scriptsToExec,
            minFee: minFee,
            datumsScriptData: datumsScriptData,
            languageViews: languageViews,
            totInputValue: totInputValue,
            requiredOutputValue: requiredOutputValue,
            outs: outs,
            change: change,
        };
    };
    TxBuilder.prototype.assertCorrectChangeOutput = function (changeOutput) {
        var e_1, _a, e_2, _b;
        if (changeOutput.value.lovelaces < 0)
            throw new Error("not enough input lovelaces to cover the output value and fee");
        try {
            for (var _c = __values(changeOutput.value), _d = _c.next(); !_d.done; _d = _c.next()) {
                var _e = _d.value, assets = _e.assets, policy = _e.policy;
                if (policy === "")
                    continue;
                try {
                    for (var assets_1 = (e_2 = void 0, __values(assets)), assets_1_1 = assets_1.next(); !assets_1_1.done; assets_1_1 = assets_1.next()) {
                        var _f = assets_1_1.value, quantity = _f.quantity, name_1 = _f.name;
                        if (quantity < 0) {
                            console.dir(changeOutput.value.toJson(), { depth: Infinity });
                            throw new Error("not enough ".concat(policy.toString(), ".").concat((0, uint8array_utils_1.toHex)(name_1), " in input to cover the total output"));
                        }
                    }
                }
                catch (e_2_1) { e_2 = { error: e_2_1 }; }
                finally {
                    try {
                        if (assets_1_1 && !assets_1_1.done && (_b = assets_1.return)) _b.call(assets_1);
                    }
                    finally { if (e_2) throw e_2.error; }
                }
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
            }
            finally { if (e_1) throw e_1.error; }
        }
    };
    TxBuilder.prototype.findCollaterals = function (utxos, targetCollateralLovelaces) {
        var e_3, _a, e_4, _b;
        var _c;
        if (targetCollateralLovelaces === void 0) { targetCollateralLovelaces = 10000000; }
        var grouped = {};
        var pkhs = [];
        try {
            for (var utxos_1 = __values(utxos), utxos_1_1 = utxos_1.next(); !utxos_1_1.done; utxos_1_1 = utxos_1.next()) {
                var u = utxos_1_1.value;
                var creds = u.resolved.address.paymentCreds;
                if (creds.type === cardano_ledger_ts_1.CredentialType.Script)
                    continue;
                var pkh = creds.hash.toString();
                if (!Array.isArray(grouped[pkh])) {
                    grouped[pkh] = [];
                    pkhs.push(pkh);
                }
                grouped[pkh].push(u);
            }
        }
        catch (e_3_1) { e_3 = { error: e_3_1 }; }
        finally {
            try {
                if (utxos_1_1 && !utxos_1_1.done && (_a = utxos_1.return)) _a.call(utxos_1);
            }
            finally { if (e_3) throw e_3.error; }
        }
        if (pkhs.length === 0)
            return [];
        for (var i = 0; i < pkhs.length; i++) {
            var pkh = pkhs[i];
            grouped[pkh] = grouped[pkh]
                .sort(function (a, b) { return -Number(a.resolved.value.lovelaces - b.resolved.value.lovelaces); })
                .slice(0, Number((_c = this.protocolParamters.maxCollateralInputs) !== null && _c !== void 0 ? _c : 3));
        }
        var maxPkh = pkhs.shift();
        var maxLove = grouped[maxPkh].reduce(function (a, b) { return a + b.resolved.value.lovelaces; }, BigInt(0));
        try {
            for (var pkhs_1 = __values(pkhs), pkhs_1_1 = pkhs_1.next(); !pkhs_1_1.done; pkhs_1_1 = pkhs_1.next()) {
                var pkh = pkhs_1_1.value;
                var love = grouped[pkh].reduce(function (a, b) { return a + b.resolved.value.lovelaces; }, BigInt(0));
                if (love > maxLove) {
                    maxPkh = pkh;
                    maxLove = love;
                }
            }
        }
        catch (e_4_1) { e_4 = { error: e_4_1 }; }
        finally {
            try {
                if (pkhs_1_1 && !pkhs_1_1.done && (_b = pkhs_1.return)) _b.call(pkhs_1);
            }
            finally { if (e_4) throw e_4.error; }
        }
        var collaterals = grouped[maxPkh];
        targetCollateralLovelaces = BigInt(targetCollateralLovelaces);
        if (targetCollateralLovelaces < 0)
            targetCollateralLovelaces = -targetCollateralLovelaces;
        var filtered = collaterals.filter(function (u) { return u.resolved.value.lovelaces >= targetCollateralLovelaces; });
        if (filtered.length === 0)
            return collaterals;
        // try to reduce the number of utxo collaterals and collateral value.
        return [filtered[filtered.length - 1]];
    };
    return TxBuilder;
}());
exports.TxBuilder = TxBuilder;
function eqUTxOByRef(a, b) {
    return a === b || a.utxoRef === b.utxoRef || (a.utxoRef.index === b.utxoRef.index &&
        a.utxoRef.id.toString() === b.utxoRef.id.toString());
}
function pushUniqueScript(arr, toPush) {
    var hashToPush = toPush.hash.toString();
    if (!arr.some(function (script) { return script.hash.toString() === hashToPush; }))
        arr.push(toPush);
}
function getCtx(scriptType, spendingPurpose, scriptInfo, redeemerData, txInfosV1, txInfosV2, txInfosV3) {
    if (scriptType === cardano_ledger_ts_1.ScriptType.PlutusV3) {
        return new plutus_data_1.DataConstr(0, [
            txInfosV3,
            redeemerData,
            scriptInfo
        ]);
    }
    else if (scriptType === cardano_ledger_ts_1.ScriptType.PlutusV2) {
        if (txInfosV2 === undefined)
            throw new Error("plutus script v2 included in a v3 transaction");
        return new plutus_data_1.DataConstr(0, [
            txInfosV2,
            spendingPurpose
        ]);
    }
    else if (scriptType === cardano_ledger_ts_1.ScriptType.PlutusV1) {
        if (txInfosV1 === undefined)
            throw new Error("plutus script v1 included in a v2 or v3 transaction");
        return new plutus_data_1.DataConstr(0, [
            txInfosV1,
            spendingPurpose
        ]);
    }
    else
        throw new Error("unexpected native script execution");
}
function onEvaluationResult(i, totExBudget, rdmr, result, budgetSpent, logs, callArgs, rdmrs, scritHashStr, onScriptResult, onScriptInvalid) {
    var _isScriptValid = true;
    if (typeof onScriptResult === "function")
        onScriptResult(rdmr.clone(), result, budgetSpent.clone(), logs.slice(), callArgs.map(function (d) { return d.clone(); }), scritHashStr);
    if (result instanceof uplc_1.ErrorUPLC ||
        (function (resultKeys) {
            return resultKeys.includes("msg") &&
                resultKeys.includes("addInfos");
        })(Object.keys(result))) {
        if (typeof onScriptInvalid === "function") {
            onScriptInvalid(rdmr.clone(), logs.slice(), callArgs.map(function (d) { return d.clone(); }));
            // _isScriptValid = false;
        }
        else {
            var callArgsStr = (callArgs
                .map(function (d, i) {
                return i.toString() + ": " + (0, plutus_data_1.dataToCbor)(d).toString();
            })
                .join("\n"));
            var errorMsg = result === null || result === void 0 ? void 0 : result.msg;
            var addInfosStr = "<error while serializing additional infos>";
            try {
                addInfosStr = (0, stringify_1.stringify)(result === null || result === void 0 ? void 0 : result.addInfos, function (k, v) {
                    if ((0, uint8array_utils_1.isUint8Array)(v))
                        return (0, uint8array_utils_1.toHex)(v);
                    if (typeof v === "bigint")
                        return v.toString();
                    return v;
                });
            }
            catch (_a) { }
            throw new Error("script '".concat(scritHashStr, "' consumed with ").concat((0, cardano_ledger_ts_1.txRedeemerTagToString)(rdmr.tag), " redemer ") +
                "and index '".concat(rdmr.index.toString(), "'\n\n") +
                "called with data arguments:\n".concat(callArgsStr, "\n\n") +
                "failed with \n" +
                "error message: ".concat(errorMsg, "\n") +
                "additional infos: ".concat(addInfosStr, "\n") +
                "script execution logs: [".concat(logs.toString(), "]\n"));
        }
    }
    rdmrs[i] = new cardano_ledger_ts_1.TxRedeemer(__assign(__assign({}, rdmr), { execUnits: budgetSpent }));
    totExBudget.add(budgetSpent);
    return _isScriptValid;
}
;
function findWitnessKey(map, n) {
    var _a;
    var res = (_a = map.map.find(function (_a) {
        var k = _a.k;
        return k instanceof cbor_1.CborUInt && Number(k.num) === n;
    })) === null || _a === void 0 ? void 0 : _a.v;
    if (!(res instanceof cbor_1.CborArray))
        return undefined;
    if (res.array.length === 0)
        return undefined;
    return res;
}
function getScriptDataHash(witnesses, languageViews) {
    var undef = void 0;
    var cbor = witnesses.toCborObj();
    if (!(cbor instanceof cbor_1.CborMap))
        return undef;
    var rdmrs = findWitnessKey(cbor, 5);
    var dats = findWitnessKey(cbor, 4);
    if (rdmrs === undef &&
        dats === undef)
        return undef;
    var scriptData;
    if (rdmrs === undef &&
        dats !== undef) {
        /* (Deprecated)
            ; Finally, note that in the case that a transaction includes datums but does not
            ; include any redeemers, the script data format becomes (in hex):
            ; [ 80 | datums | A0 ]
            ; corresponding to a CBOR empty list and an empty map (our apologies).
        */
        /* Post Babbage:
            ; [ A0 | datums | A0 ]
        */
        scriptData = new Uint8Array(__spreadArray(__spreadArray([
            0xa0
        ], __read(cbor_1.Cbor.encode(dats).toBuffer()), false), [
            0xa0
        ], false));
    }
    else {
        var rdmrsBuff = rdmrs ? cbor_1.Cbor.encode(rdmrs).toBuffer() : new Uint8Array([0x80]);
        var datsBuff = dats ? cbor_1.Cbor.encode(dats).toBuffer() : new Uint8Array([]);
        scriptData = new Uint8Array(rdmrsBuff.length + datsBuff.length + languageViews.length);
        scriptData.set(rdmrsBuff, 0);
        scriptData.set(datsBuff, rdmrsBuff.length);
        scriptData.set(languageViews, rdmrsBuff.length + datsBuff.length);
    }
    return new cardano_ledger_ts_1.ScriptDataHash(Uint8Array.from((0, crypto_1.blake2b_256)(scriptData)));
}
exports.getScriptDataHash = getScriptDataHash;
// Helper function to get byte length of a string using UTF-8 encoding
// const getByteLength = (str: string): number => {
//     return new TextEncoder().encode(str).length;
// };
function getByteLength(str) {
    return (0, uint8array_utils_1.fromUtf8)(str).length;
}
/**
 * Claude prompt (yes I'm lazy):
 *
 * write a typescript function that given a string and a number,
 * returns an array of strings whose length IN BYTES is at most the second parameter,
 * if a utf 8 char is present at the intersection
 * of the two chunks IT MUST STAY INTACT and it will be part of the following chunk.
 *
 * @example
 * const exampleString = "Hello, 🌍! This is a test.";
 * const maxChunkBytes = 10;
 * const result = splitStringByByteLength(exampleString, maxChunkBytes);
 * console.log(result); // ["Hello, ", "🌍! This", " is a test", "."]
 */
function splitStringByByteLength(input, maxByteLength) {
    var e_5, _a;
    if (maxByteLength === void 0) { maxByteLength = 64; }
    // If input is empty or max byte length is 0, return empty array
    if (!input || maxByteLength <= 0)
        return [];
    var chunks = [];
    var currentChunk = '';
    try {
        for (var input_1 = __values(input), input_1_1 = input_1.next(); !input_1_1.done; input_1_1 = input_1.next()) {
            var char = input_1_1.value;
            var potentialChunk = currentChunk + char;
            var potentialChunkByteLength = getByteLength(potentialChunk);
            // If adding this character would exceed max byte length
            if (potentialChunkByteLength > maxByteLength) {
                // Push current chunk and start a new one
                if (currentChunk) {
                    chunks.push(currentChunk);
                    currentChunk = '';
                }
            }
            // Add character to current chunk
            currentChunk += char;
        }
    }
    catch (e_5_1) { e_5 = { error: e_5_1 }; }
    finally {
        try {
            if (input_1_1 && !input_1_1.done && (_a = input_1.return)) _a.call(input_1);
        }
        finally { if (e_5) throw e_5.error; }
    }
    // Add final chunk if not empty
    if (currentChunk) {
        chunks.push(currentChunk);
    }
    return chunks;
}
exports.splitStringByByteLength = splitStringByByteLength;