@node-dlc/core
Version:
146 lines • 6.67 kB
JavaScript
"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