UNPKG

@node-dlc/core

Version:
146 lines 6.67 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getFinalizerByCount = exports.getFinalizer = exports.DualClosingTxFinalizer = exports.DualFundingTxFinalizer = void 0; const messaging_1 = require("@node-dlc/messaging"); const decimal_js_1 = require("decimal.js"); const BATCH_FUND_TX_BASE_WEIGHT = 42; const FUNDING_OUTPUT_SIZE = 43; class DualFundingTxFinalizer { constructor(offerInputs, offerPayoutSPK, offerChangeSPK, acceptInputs, acceptPayoutSPK, acceptChangeSPK, feeRate, numContracts = 1) { this.offerInputs = offerInputs; this.offerPayoutSPK = offerPayoutSPK; this.offerChangeSPK = offerChangeSPK; this.acceptInputs = acceptInputs; this.acceptPayoutSPK = acceptPayoutSPK; this.acceptChangeSPK = acceptChangeSPK; this.feeRate = feeRate; this.numContracts = numContracts; } computeFees(_inputs, payoutSPK, changeSPK, numContracts) { // If no inputs, return zero fees (matches C++ layer behavior for single-funded DLCs) if (_inputs.length === 0) { return { futureFee: BigInt(0), fundingFee: BigInt(0) }; } _inputs.forEach((input) => { if (input.type !== messaging_1.MessageType.FundingInput) { console.error('input', input); throw new Error('Input is not a funding input'); } }); const inputs = _inputs.map((input) => input); // https://github.com/discreetlogcontracts/dlcspecs/blob/8ee4bbe816c9881c832b1ce320b9f14c72e3506f/Transactions.md#expected-weight-of-the-contract-execution-or-refund-transaction const futureFeeWeight = 249 + 4 * payoutSPK.length; const futureFeeVBytes = new decimal_js_1.Decimal(futureFeeWeight) .times(numContracts) .div(4) .ceil() .toNumber(); const futureFee = this.feeRate * BigInt(futureFeeVBytes); // https://github.com/discreetlogcontracts/dlcspecs/blob/8ee4bbe816c9881c832b1ce320b9f14c72e3506f/Transactions.md#expected-weight-of-the-funding-transaction const inputWeight = inputs.reduce((total, input) => { return total + 164 + input.maxWitnessLen + input.scriptSigLength(); }, 0); const contractWeight = (BATCH_FUND_TX_BASE_WEIGHT + FUNDING_OUTPUT_SIZE * numContracts * 4) / 2; const outputWeight = 36 + 4 * changeSPK.length + contractWeight; const weight = outputWeight + inputWeight; const vbytes = new decimal_js_1.Decimal(weight).div(4).ceil().toNumber(); const fundingFee = this.feeRate * BigInt(vbytes); return { futureFee, fundingFee }; } getOfferFees() { return this.computeFees(this.offerInputs, this.offerPayoutSPK, this.offerChangeSPK, this.numContracts); } getAcceptFees() { return this.computeFees(this.acceptInputs, this.acceptPayoutSPK, this.acceptChangeSPK, this.numContracts); } get offerFees() { const { futureFee, fundingFee } = this.getOfferFees(); return futureFee + fundingFee; } get offerFutureFee() { return this.getOfferFees().futureFee; } get offerFundingFee() { return this.getOfferFees().fundingFee; } get acceptFees() { const { futureFee, fundingFee } = this.getAcceptFees(); return futureFee + fundingFee; } get acceptFutureFee() { return this.getAcceptFees().futureFee; } get acceptFundingFee() { return this.getAcceptFees().fundingFee; } } exports.DualFundingTxFinalizer = DualFundingTxFinalizer; class DualClosingTxFinalizer { constructor(initiatorInputs, offerPayoutSPK, acceptPayoutSPK, feeRate) { this.initiatorInputs = initiatorInputs; this.offerPayoutSPK = offerPayoutSPK; this.acceptPayoutSPK = acceptPayoutSPK; this.feeRate = feeRate; } computeFees(payoutSPK, _inputs = []) { _inputs.forEach((input) => { if (input.type !== messaging_1.MessageType.FundingInput) throw new Error('Input is not a funding input'); }); const inputs = _inputs.map((input) => input); // https://gist.github.com/matthewjablack/08c36baa513af9377508111405b22e03 const inputWeight = inputs.reduce((total, input) => { return total + 164 + input.maxWitnessLen + input.scriptSigLength(); }, 0); const outputWeight = 36 + 4 * payoutSPK.length; const weight = 213 + outputWeight + inputWeight; const vbytes = new decimal_js_1.Decimal(weight).div(4).ceil().toNumber(); const fee = this.feeRate * BigInt(vbytes); return fee; } getOfferInitiatorFees() { return this.computeFees(this.offerPayoutSPK, this.initiatorInputs); } getOfferReciprocatorFees() { return this.computeFees(this.offerPayoutSPK); } getAcceptInitiatorFees() { return this.computeFees(this.acceptPayoutSPK, this.initiatorInputs); } getAcceptReciprocatorFees() { return this.computeFees(this.acceptPayoutSPK); } get offerInitiatorFees() { return this.getOfferInitiatorFees(); } get offerReciprocatorFees() { return this.getOfferReciprocatorFees(); } get acceptInitiatorFees() { return this.getAcceptInitiatorFees(); } get acceptReciprocatorFees() { return this.getAcceptReciprocatorFees(); } } exports.DualClosingTxFinalizer = DualClosingTxFinalizer; const getFinalizer = (feeRate, offerInputs, acceptInputs, numContracts) => { const input = new messaging_1.FundingInput(); input.maxWitnessLen = 108; input.redeemScript = Buffer.from('', 'hex'); const fakeSPK = Buffer.from('0014663117d27e78eb432505180654e603acb30e8a4a', 'hex'); offerInputs = offerInputs || Array.from({ length: 1 }, () => input); acceptInputs = acceptInputs || Array.from({ length: 1 }, () => input); return new DualFundingTxFinalizer(offerInputs, fakeSPK, fakeSPK, acceptInputs, fakeSPK, fakeSPK, feeRate, numContracts); }; exports.getFinalizer = getFinalizer; const getFinalizerByCount = (feeRate, numOfferInputs, numAcceptInputs, numContracts) => { const input = new messaging_1.FundingInput(); input.maxWitnessLen = 108; input.redeemScript = Buffer.from('', 'hex'); const offerInputs = Array.from({ length: numOfferInputs }, () => input); const acceptInputs = Array.from({ length: numAcceptInputs }, () => input); return (0, exports.getFinalizer)(feeRate, offerInputs, acceptInputs, numContracts); }; exports.getFinalizerByCount = getFinalizerByCount; //# sourceMappingURL=TxFinalizer.js.map