Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@node-dlc/core

Package Overview
Dependencies
Maintainers
3
Versions
127
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@node-dlc/core - npm Package Compare versions

Comparing version 0.23.0 to 0.23.1

.nyc_output/a8bae97d-9104-4e1c-a1dd-9015c00a5dc3.json

60

__tests__/dlc/finance/CsoInfo.spec.ts

@@ -23,2 +23,3 @@ import { Value } from '@node-dlc/bitcoin';

LinearPayout,
roundDownToNearestMultiplier,
roundUpToNearestMultiplier,

@@ -635,6 +636,15 @@ } from '../../../lib';

// Fees are very high, so use dust threshold for max gain
const expectedMaxGainForContractSize = Value.fromSats(
const offerFees = Value.fromSats(
getFinalizerByCount(feePerByte, numOfferInputs, 3, numContracts)
.offerFees,
).addn(Value.fromSats(dustThreshold(feePerByte)));
);
const expectedMaxGainForContractSize_ = offerFees.addn(
Value.fromSats(dustThreshold(BigInt(feePerByte))),
);
const expectedMaxGainForContractSize = Value.fromSats(
roundUpToNearestMultiplier(
expectedMaxGainForContractSize_.sats,
BigInt(100),
),
);

@@ -649,3 +659,3 @@ const expectedMaxLossForContractSize = Value.fromSats(

const expectedNormalizedMaxGain = Value.fromSats(
roundUpToNearestMultiplier(
roundDownToNearestMultiplier(
(expectedMaxGainForContractSize.sats * BigInt(1e8)) /

@@ -657,6 +667,2 @@ contractSize.sats,

expect(actualNormalizedMaxGain.sats).to.equal(
expectedNormalizedMaxGain.sats,
);
expect(actualNormalizedMaxLoss.sats).to.equal(normalizedMaxLoss.sats);
expect(actualMaxGainForContractSize.sats).to.equal(

@@ -668,3 +674,7 @@ expectedMaxGainForContractSize.sats,

);
expect(minPayout).to.equal(BigInt(119900));
expect(actualNormalizedMaxLoss.sats).to.equal(normalizedMaxLoss.sats);
expect(actualNormalizedMaxGain.sats).to.equal(
expectedNormalizedMaxGain.sats,
); // TODO: Fix issue with this line
expect(minPayout).to.equal(BigInt(121200));
expect(maxPayout).to.equal(collateral.sats);

@@ -714,6 +724,15 @@ expect(actualContractSize.sats).to.equal(contractSize.sats);

// Fees are very high, so use dust threshold for max gain
const expectedMaxGainForContractSize = Value.fromSats(
const offerFees = Value.fromSats(
getFinalizerByCount(feePerByte, numOfferInputs, 3, numContracts)
.offerFees,
).addn(Value.fromSats(dustThreshold(feePerByte)));
);
const expectedMaxGainForContractSize_ = offerFees.addn(
Value.fromSats(dustThreshold(BigInt(feePerByte))),
);
const expectedMaxGainForContractSize = Value.fromSats(
roundUpToNearestMultiplier(
expectedMaxGainForContractSize_.sats,
BigInt(100),
),
);

@@ -728,3 +747,3 @@ const expectedMaxLossForContractSize = Value.fromSats(

const expectedNormalizedMaxGain = Value.fromSats(
roundUpToNearestMultiplier(
roundDownToNearestMultiplier(
(expectedMaxGainForContractSize.sats * BigInt(1e8)) /

@@ -746,3 +765,3 @@ contractSize.sats,

);
expect(minPayout).to.equal(BigInt(119900));
expect(minPayout).to.equal(BigInt(121200));
expect(maxPayout).to.equal(collateral.sats);

@@ -792,6 +811,15 @@ expect(actualContractSize.sats).to.equal(contractSize.sats);

// Fees are very high, so use dust threshold for max gain
const expectedMaxGainForContractSize = Value.fromSats(
const offerFees = Value.fromSats(
getFinalizerByCount(feePerByte, numOfferInputs, 3, numContracts)
.offerFees,
).addn(Value.fromSats(dustThreshold(feePerByte)));
);
const expectedMaxGainForContractSize_ = offerFees.addn(
Value.fromSats(dustThreshold(BigInt(feePerByte))),
);
const expectedMaxGainForContractSize = Value.fromSats(
roundUpToNearestMultiplier(
expectedMaxGainForContractSize_.sats,
BigInt(100),
),
);

@@ -806,3 +834,3 @@ const expectedMaxLossForContractSize = Value.fromSats(

const expectedNormalizedMaxGain = Value.fromSats(
roundUpToNearestMultiplier(
roundDownToNearestMultiplier(
(expectedMaxGainForContractSize.sats * BigInt(1e8)) /

@@ -824,3 +852,3 @@ contractSize.sats,

);
expect(minPayout).to.equal(BigInt(119900));
expect(minPayout).to.equal(BigInt(121200));
expect(maxPayout).to.equal(collateral.sats);

@@ -827,0 +855,0 @@ expect(actualContractSize.sats).to.equal(contractSize.sats);

@@ -1,1 +0,1 @@

{"processes":{"3c0d0f1a-a3da-4252-9234-f74a9f0bafe2":{"parent":null,"children":[]}},"files":{"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/CETCalculator.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/utils/BigIntUtils.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/finance/CoveredCall.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/HyperbolaPayoutCurve.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/utils/Precision.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/finance/ShortPut.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/index.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/finance/Builder.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/CoinSelect.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/TxFinalizer.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/finance/LinearPayout.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/PolynomialPayoutCurve.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/finance/LongCall.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/finance/LongPut.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/finance/CsoInfo.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/finance/OptionInfo.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/PayoutCurve.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/TxBuilder.ts":["3c0d0f1a-a3da-4252-9234-f74a9f0bafe2"]},"externalIds":{}}
{"processes":{"a8bae97d-9104-4e1c-a1dd-9015c00a5dc3":{"parent":null,"children":[]}},"files":{"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/CETCalculator.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/utils/BigIntUtils.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/finance/CoveredCall.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/HyperbolaPayoutCurve.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/utils/Precision.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/finance/ShortPut.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/index.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/finance/Builder.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/CoinSelect.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/TxFinalizer.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/finance/LinearPayout.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/PolynomialPayoutCurve.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/finance/LongCall.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/finance/LongPut.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/finance/CsoInfo.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/finance/OptionInfo.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/PayoutCurve.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"],"/Users/matthewblack/code/github.com/AtomicFinance/node-dlc/packages/core/lib/dlc/TxBuilder.ts":["a8bae97d-9104-4e1c-a1dd-9015c00a5dc3"]},"externalIds":{}}

@@ -42,2 +42,3 @@ import { Value } from '@node-dlc/bitcoin';

export declare const roundUpToNearestMultiplier: (num: bigint, multiplier: bigint) => bigint;
export declare const roundDownToNearestMultiplier: (num: bigint, multiplier: bigint) => bigint;
export declare type DlcParty = 'offeror' | 'acceptor' | 'neither';

@@ -44,0 +45,0 @@ /**

@@ -6,3 +6,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.buildRoundingIntervalsFromIntervals = exports.buildCustomStrategyOrderOffer = exports.buildLinearOrderOffer = exports.buildLongPutOrderOffer = exports.buildLongCallOrderOffer = exports.buildShortPutOrderOffer = exports.buildCoveredCallOrderOffer = exports.buildOptionOrderOffer = exports.buildOrderOffer = exports.getDigitDecompositionEventDescriptor = exports.computeRoundingModulus = exports.roundUpToNearestMultiplier = exports.roundToNearestMultiplier = exports.UNIT_MULTIPLIER = void 0;
exports.buildRoundingIntervalsFromIntervals = exports.buildCustomStrategyOrderOffer = exports.buildLinearOrderOffer = exports.buildLongPutOrderOffer = exports.buildLongCallOrderOffer = exports.buildShortPutOrderOffer = exports.buildCoveredCallOrderOffer = exports.buildOptionOrderOffer = exports.buildOrderOffer = exports.getDigitDecompositionEventDescriptor = exports.computeRoundingModulus = exports.roundDownToNearestMultiplier = exports.roundUpToNearestMultiplier = exports.roundToNearestMultiplier = exports.UNIT_MULTIPLIER = void 0;
const bitcoin_1 = require("@node-dlc/bitcoin");

@@ -59,2 +59,4 @@ const messaging_1 = require("@node-dlc/messaging");

exports.roundUpToNearestMultiplier = roundUpToNearestMultiplier;
const roundDownToNearestMultiplier = (num, multiplier) => num - (num % multiplier);
exports.roundDownToNearestMultiplier = roundDownToNearestMultiplier;
/**

@@ -61,0 +63,0 @@ * Compute rounding intervals for a linear or hyperbola payout curve

@@ -109,6 +109,3 @@ "use strict";

normalizedMaxLoss.sub(startOutcomeValue);
const maxGainForContractSize = bitcoin_1.Value.fromBitcoin(new decimal_js_1.default(normalizedMaxGain.bitcoin)
.times(contractSize.bitcoin)
.toDecimalPlaces(8 - Math.log10(Number(Builder_1.UNIT_MULTIPLIER[unit.toLowerCase()])))
.toNumber());
const maxGainForContractSize = bitcoin_1.Value.fromSats(Builder_1.roundUpToNearestMultiplier((normalizedMaxGain.sats * contractSize.sats) / BigInt(1e8), BigInt(Builder_1.UNIT_MULTIPLIER[unit.toLowerCase()])));
const maxLossForContractSize = bitcoin_1.Value.fromSats(Builder_1.roundUpToNearestMultiplier((normalizedMaxLoss.sats * contractSize.sats) / BigInt(1e8), BigInt(Builder_1.UNIT_MULTIPLIER[unit.toLowerCase()])));

@@ -115,0 +112,0 @@ const offerCollateral = collateral.clone();

@@ -9,1 +9,9 @@ import { DlcAcceptWithoutSigs, DlcOfferV0 } from '@node-dlc/messaging';

}
export declare class BatchDlcTxBuilder {
readonly dlcOffers: DlcOfferV0[];
readonly dlcAccepts: DlcAcceptWithoutSigs[];
constructor(dlcOffers: DlcOfferV0[], dlcAccepts: DlcAcceptWithoutSigs[]);
buildFundingTransaction(): Tx;
private ensureSameFundingInputs;
private arraysEqual;
}
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DlcTxBuilder = void 0;
exports.BatchDlcTxBuilder = exports.DlcTxBuilder = void 0;
const messaging_1 = require("@node-dlc/messaging");
const bitcoin_1 = require("@node-lightning/bitcoin");
const decimal_js_1 = __importDefault(require("decimal.js"));
const TxFinalizer_1 = require("./TxFinalizer");

@@ -13,22 +17,43 @@ class DlcTxBuilder {

buildFundingTransaction() {
const txBuilder = new BatchDlcTxBuilder([this.dlcOffer], [this.dlcAccept]);
return txBuilder.buildFundingTransaction();
}
}
exports.DlcTxBuilder = DlcTxBuilder;
class BatchDlcTxBuilder {
constructor(dlcOffers, dlcAccepts) {
this.dlcOffers = dlcOffers;
this.dlcAccepts = dlcAccepts;
}
buildFundingTransaction() {
const tx = new bitcoin_1.TxBuilder();
tx.version = 2;
tx.locktime = bitcoin_1.LockTime.zero();
const multisigScript = Buffer.compare(this.dlcOffer.fundingPubKey, this.dlcAccept.fundingPubKey) === -1
? bitcoin_1.Script.p2msLock(2, this.dlcOffer.fundingPubKey, this.dlcAccept.fundingPubKey)
: bitcoin_1.Script.p2msLock(2, this.dlcAccept.fundingPubKey, this.dlcOffer.fundingPubKey);
const witScript = bitcoin_1.Script.p2wshLock(multisigScript);
const offerInput = this.dlcOffer.offerCollateralSatoshis;
const acceptInput = this.dlcAccept.acceptCollateralSatoshis;
const totalInput = offerInput + acceptInput;
const finalizer = new TxFinalizer_1.DualFundingTxFinalizer(this.dlcOffer.fundingInputs, this.dlcOffer.payoutSPK, this.dlcOffer.changeSPK, this.dlcAccept.fundingInputs, this.dlcAccept.payoutSPK, this.dlcAccept.changeSPK, this.dlcOffer.feeRatePerVb);
this.dlcOffer.fundingInputs.forEach((input) => {
if (this.dlcOffers.length !== this.dlcAccepts.length)
throw Error('DlcOffers and DlcAccepts must be the same length');
if (this.dlcOffers.length === 0)
throw Error('DlcOffers must not be empty');
if (this.dlcAccepts.length === 0)
throw Error('DlcAccepts must not be empty');
// Ensure all DLC offers and accepts have the same funding inputs
this.ensureSameFundingInputs();
const multisigScripts = [];
for (let i = 0; i < this.dlcOffers.length; i++) {
const offer = this.dlcOffers[i];
const accept = this.dlcAccepts[i];
multisigScripts.push(Buffer.compare(offer.fundingPubKey, accept.fundingPubKey) === -1
? bitcoin_1.Script.p2msLock(2, offer.fundingPubKey, accept.fundingPubKey)
: bitcoin_1.Script.p2msLock(2, accept.fundingPubKey, offer.fundingPubKey));
}
const witScripts = multisigScripts.map((multisigScript) => bitcoin_1.Script.p2wshLock(multisigScript));
const finalizer = new TxFinalizer_1.DualFundingTxFinalizer(this.dlcOffers[0].fundingInputs, this.dlcOffers[0].payoutSPK, this.dlcOffers[0].changeSPK, this.dlcAccepts[0].fundingInputs, this.dlcAccepts[0].payoutSPK, this.dlcAccepts[0].changeSPK, this.dlcOffers[0].feeRatePerVb, this.dlcOffers.length);
this.dlcOffers[0].fundingInputs.forEach((input) => {
if (input.type !== messaging_1.MessageType.FundingInputV0)
throw Error('FundingInput must be V0');
});
const offerFundingInputs = this.dlcOffer.fundingInputs.map((input) => input);
const offerFundingInputs = this.dlcOffers[0].fundingInputs.map((input) => input);
const offerTotalFunding = offerFundingInputs.reduce((total, input) => {
return total + input.prevTx.outputs[input.prevTxVout].value.sats;
}, BigInt(0));
const acceptTotalFunding = this.dlcAccept.fundingInputs.reduce((total, input) => {
const acceptTotalFunding = this.dlcAccepts[0].fundingInputs.reduce((total, input) => {
return total + input.prevTx.outputs[input.prevTxVout].value.sats;

@@ -38,3 +63,3 @@ }, BigInt(0));

...offerFundingInputs,
...this.dlcAccept.fundingInputs,
...this.dlcAccepts[0].fundingInputs,
];

@@ -45,20 +70,41 @@ fundingInputs.sort((a, b) => Number(a.inputSerialId) - Number(b.inputSerialId));

});
const fundingValue = totalInput + finalizer.offerFutureFee + finalizer.acceptFutureFee;
const offerInput = this.dlcOffers.reduce((total, offer) => total + offer.offerCollateralSatoshis, BigInt(0));
const acceptInput = this.dlcAccepts.reduce((total, accept) => total + accept.acceptCollateralSatoshis, BigInt(0));
const totalInputs = this.dlcOffers.map((offer, i) => {
const offerInput = offer.offerCollateralSatoshis;
const acceptInput = this.dlcAccepts[i].acceptCollateralSatoshis;
return offerInput + acceptInput;
});
const fundingValues = totalInputs.map((totalInput) => {
const offerFutureFeePerOffer = new decimal_js_1.default(finalizer.offerFutureFee.toString())
.div(this.dlcOffers.length)
.ceil()
.toNumber();
const acceptFutureFeePerAccept = new decimal_js_1.default(finalizer.acceptFutureFee.toString())
.div(this.dlcAccepts.length)
.ceil()
.toNumber();
return (totalInput +
bitcoin_1.Value.fromSats(offerFutureFeePerOffer).sats +
bitcoin_1.Value.fromSats(acceptFutureFeePerAccept).sats);
});
const offerChangeValue = offerTotalFunding - offerInput - finalizer.offerFees;
const acceptChangeValue = acceptTotalFunding - acceptInput - finalizer.acceptFees;
const outputs = [];
outputs.push({
value: bitcoin_1.Value.fromSats(Number(fundingValue)),
script: witScript,
serialId: this.dlcOffer.fundOutputSerialId,
witScripts.forEach((witScript, i) => {
outputs.push({
value: bitcoin_1.Value.fromSats(Number(fundingValues[i])),
script: witScript,
serialId: this.dlcOffers[i].fundOutputSerialId,
});
});
outputs.push({
value: bitcoin_1.Value.fromSats(Number(offerChangeValue)),
script: bitcoin_1.Script.p2wpkhLock(this.dlcOffer.changeSPK.slice(2)),
serialId: this.dlcOffer.changeSerialId,
script: bitcoin_1.Script.p2wpkhLock(this.dlcOffers[0].changeSPK.slice(2)),
serialId: this.dlcOffers[0].changeSerialId,
});
outputs.push({
value: bitcoin_1.Value.fromSats(Number(acceptChangeValue)),
script: bitcoin_1.Script.p2wpkhLock(this.dlcAccept.changeSPK.slice(2)),
serialId: this.dlcAccept.changeSerialId,
script: bitcoin_1.Script.p2wpkhLock(this.dlcAccepts[0].changeSPK.slice(2)),
serialId: this.dlcAccepts[0].changeSerialId,
});

@@ -71,4 +117,31 @@ outputs.sort((a, b) => Number(a.serialId) - Number(b.serialId));

}
ensureSameFundingInputs() {
// Check for offers
const referenceOfferInputs = this.dlcOffers[0].fundingInputs.map((input) => input.serialize().toString('hex'));
for (let i = 1; i < this.dlcOffers.length; i++) {
const currentInputs = this.dlcOffers[i].fundingInputs.map((input) => input.serialize().toString('hex'));
if (!this.arraysEqual(referenceOfferInputs, currentInputs)) {
throw new Error(`Funding inputs for offer ${i} do not match the first offer's funding inputs.`);
}
}
// Check for accepts
const referenceAcceptInputs = this.dlcAccepts[0].fundingInputs.map((input) => input.serialize().toString('hex'));
for (let i = 1; i < this.dlcAccepts.length; i++) {
const currentInputs = this.dlcAccepts[i].fundingInputs.map((input) => input.serialize().toString('hex'));
if (!this.arraysEqual(referenceAcceptInputs, currentInputs)) {
throw new Error(`Funding inputs for accept ${i} do not match the first accept's funding inputs.`);
}
}
}
arraysEqual(arr1, arr2) {
if (arr1.length !== arr2.length)
return false;
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i])
return false;
}
return true;
}
}
exports.DlcTxBuilder = DlcTxBuilder;
exports.BatchDlcTxBuilder = BatchDlcTxBuilder;
//# sourceMappingURL=TxBuilder.js.map

@@ -5,2 +5,3 @@ "use strict";

const messaging_1 = require("@node-dlc/messaging");
const decimal_js_1 = require("decimal.js");
const BATCH_FUND_TX_BASE_WEIGHT = 42;

@@ -27,4 +28,8 @@ const FUNDING_OUTPUT_SIZE = 43;

const futureFeeWeight = 249 + 4 * payoutSPK.length;
const futureFeeVBytes = Math.ceil(futureFeeWeight / 4);
const futureFee = this.feeRate * BigInt(futureFeeVBytes) * BigInt(numContracts);
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

@@ -37,3 +42,3 @@ const inputWeight = inputs.reduce((total, input) => {

const weight = outputWeight + inputWeight;
const vbytes = Math.ceil(weight / 4);
const vbytes = new decimal_js_1.Decimal(weight).div(4).ceil().toNumber();
const fundingFee = this.feeRate * BigInt(vbytes);

@@ -89,3 +94,3 @@ return { futureFee, fundingFee };

const weight = 213 + outputWeight + inputWeight;
const vbytes = Math.ceil(weight / 4);
const vbytes = new decimal_js_1.Decimal(weight).div(4).ceil().toNumber();
const fee = this.feeRate * BigInt(vbytes);

@@ -92,0 +97,0 @@ return fee;

@@ -76,2 +76,7 @@ import { Value } from '@node-dlc/bitcoin';

export const roundDownToNearestMultiplier = (
num: bigint,
multiplier: bigint,
): bigint => num - (num % multiplier);
export type DlcParty = 'offeror' | 'acceptor' | 'neither';

@@ -78,0 +83,0 @@

@@ -199,9 +199,7 @@ import { Value } from '@node-dlc/bitcoin';

const maxGainForContractSize = Value.fromBitcoin(
new Decimal(normalizedMaxGain.bitcoin)
.times(contractSize.bitcoin)
.toDecimalPlaces(
8 - Math.log10(Number(UNIT_MULTIPLIER[unit.toLowerCase()])),
)
.toNumber(),
const maxGainForContractSize = Value.fromSats(
roundUpToNearestMultiplier(
(normalizedMaxGain.sats * contractSize.sats) / BigInt(1e8),
BigInt(UNIT_MULTIPLIER[unit.toLowerCase()]),
),
);

@@ -208,0 +206,0 @@

@@ -15,2 +15,3 @@ import {

} from '@node-lightning/bitcoin';
import Decimal from 'decimal.js';

@@ -26,2 +27,14 @@ import { DualFundingTxFinalizer } from './TxFinalizer';

public buildFundingTransaction(): Tx {
const txBuilder = new BatchDlcTxBuilder([this.dlcOffer], [this.dlcAccept]);
return txBuilder.buildFundingTransaction();
}
}
export class BatchDlcTxBuilder {
constructor(
readonly dlcOffers: DlcOfferV0[],
readonly dlcAccepts: DlcAcceptWithoutSigs[],
) {}
public buildFundingTransaction(): Tx {
const tx = new TxBuilder();

@@ -31,39 +44,43 @@ tx.version = 2;

const multisigScript =
Buffer.compare(
this.dlcOffer.fundingPubKey,
this.dlcAccept.fundingPubKey,
) === -1
? Script.p2msLock(
2,
this.dlcOffer.fundingPubKey,
this.dlcAccept.fundingPubKey,
)
: Script.p2msLock(
2,
this.dlcAccept.fundingPubKey,
this.dlcOffer.fundingPubKey,
);
const witScript = Script.p2wshLock(multisigScript);
if (this.dlcOffers.length !== this.dlcAccepts.length)
throw Error('DlcOffers and DlcAccepts must be the same length');
if (this.dlcOffers.length === 0) throw Error('DlcOffers must not be empty');
if (this.dlcAccepts.length === 0)
throw Error('DlcAccepts must not be empty');
const offerInput = this.dlcOffer.offerCollateralSatoshis;
const acceptInput = this.dlcAccept.acceptCollateralSatoshis;
// Ensure all DLC offers and accepts have the same funding inputs
this.ensureSameFundingInputs();
const totalInput = offerInput + acceptInput;
const multisigScripts: Script[] = [];
for (let i = 0; i < this.dlcOffers.length; i++) {
const offer = this.dlcOffers[i];
const accept = this.dlcAccepts[i];
multisigScripts.push(
Buffer.compare(offer.fundingPubKey, accept.fundingPubKey) === -1
? Script.p2msLock(2, offer.fundingPubKey, accept.fundingPubKey)
: Script.p2msLock(2, accept.fundingPubKey, offer.fundingPubKey),
);
}
const witScripts = multisigScripts.map((multisigScript) =>
Script.p2wshLock(multisigScript),
);
const finalizer = new DualFundingTxFinalizer(
this.dlcOffer.fundingInputs,
this.dlcOffer.payoutSPK,
this.dlcOffer.changeSPK,
this.dlcAccept.fundingInputs,
this.dlcAccept.payoutSPK,
this.dlcAccept.changeSPK,
this.dlcOffer.feeRatePerVb,
this.dlcOffers[0].fundingInputs,
this.dlcOffers[0].payoutSPK,
this.dlcOffers[0].changeSPK,
this.dlcAccepts[0].fundingInputs,
this.dlcAccepts[0].payoutSPK,
this.dlcAccepts[0].changeSPK,
this.dlcOffers[0].feeRatePerVb,
this.dlcOffers.length,
);
this.dlcOffer.fundingInputs.forEach((input) => {
this.dlcOffers[0].fundingInputs.forEach((input) => {
if (input.type !== MessageType.FundingInputV0)
throw Error('FundingInput must be V0');
});
const offerFundingInputs: FundingInputV0[] = this.dlcOffer.fundingInputs.map(
const offerFundingInputs: FundingInputV0[] = this.dlcOffers[0].fundingInputs.map(
(input) => input as FundingInputV0,

@@ -76,3 +93,3 @@ );

const acceptTotalFunding = this.dlcAccept.fundingInputs.reduce(
const acceptTotalFunding = this.dlcAccepts[0].fundingInputs.reduce(
(total, input) => {

@@ -86,3 +103,3 @@ return total + input.prevTx.outputs[input.prevTxVout].value.sats;

...offerFundingInputs,
...this.dlcAccept.fundingInputs,
...this.dlcAccepts[0].fundingInputs,
];

@@ -102,4 +119,38 @@

const fundingValue =
totalInput + finalizer.offerFutureFee + finalizer.acceptFutureFee;
const offerInput = this.dlcOffers.reduce(
(total, offer) => total + offer.offerCollateralSatoshis,
BigInt(0),
);
const acceptInput = this.dlcAccepts.reduce(
(total, accept) => total + accept.acceptCollateralSatoshis,
BigInt(0),
);
const totalInputs = this.dlcOffers.map((offer, i) => {
const offerInput = offer.offerCollateralSatoshis;
const acceptInput = this.dlcAccepts[i].acceptCollateralSatoshis;
return offerInput + acceptInput;
});
const fundingValues = totalInputs.map((totalInput) => {
const offerFutureFeePerOffer = new Decimal(
finalizer.offerFutureFee.toString(),
)
.div(this.dlcOffers.length)
.ceil()
.toNumber();
const acceptFutureFeePerAccept = new Decimal(
finalizer.acceptFutureFee.toString(),
)
.div(this.dlcAccepts.length)
.ceil()
.toNumber();
return (
totalInput +
Value.fromSats(offerFutureFeePerOffer).sats +
Value.fromSats(acceptFutureFeePerAccept).sats
);
});
const offerChangeValue =

@@ -111,16 +162,18 @@ offerTotalFunding - offerInput - finalizer.offerFees;

const outputs: Output[] = [];
outputs.push({
value: Value.fromSats(Number(fundingValue)),
script: witScript,
serialId: this.dlcOffer.fundOutputSerialId,
witScripts.forEach((witScript, i) => {
outputs.push({
value: Value.fromSats(Number(fundingValues[i])),
script: witScript,
serialId: this.dlcOffers[i].fundOutputSerialId,
});
});
outputs.push({
value: Value.fromSats(Number(offerChangeValue)),
script: Script.p2wpkhLock(this.dlcOffer.changeSPK.slice(2)),
serialId: this.dlcOffer.changeSerialId,
script: Script.p2wpkhLock(this.dlcOffers[0].changeSPK.slice(2)),
serialId: this.dlcOffers[0].changeSerialId,
});
outputs.push({
value: Value.fromSats(Number(acceptChangeValue)),
script: Script.p2wpkhLock(this.dlcAccept.changeSPK.slice(2)),
serialId: this.dlcAccept.changeSerialId,
script: Script.p2wpkhLock(this.dlcAccepts[0].changeSPK.slice(2)),
serialId: this.dlcAccepts[0].changeSerialId,
});

@@ -136,2 +189,42 @@

}
private ensureSameFundingInputs(): void {
// Check for offers
const referenceOfferInputs = this.dlcOffers[0].fundingInputs.map((input) =>
input.serialize().toString('hex'),
);
for (let i = 1; i < this.dlcOffers.length; i++) {
const currentInputs = this.dlcOffers[i].fundingInputs.map((input) =>
input.serialize().toString('hex'),
);
if (!this.arraysEqual(referenceOfferInputs, currentInputs)) {
throw new Error(
`Funding inputs for offer ${i} do not match the first offer's funding inputs.`,
);
}
}
// Check for accepts
const referenceAcceptInputs = this.dlcAccepts[0].fundingInputs.map(
(input) => input.serialize().toString('hex'),
);
for (let i = 1; i < this.dlcAccepts.length; i++) {
const currentInputs = this.dlcAccepts[i].fundingInputs.map((input) =>
input.serialize().toString('hex'),
);
if (!this.arraysEqual(referenceAcceptInputs, currentInputs)) {
throw new Error(
`Funding inputs for accept ${i} do not match the first accept's funding inputs.`,
);
}
}
}
private arraysEqual(arr1: string[], arr2: string[]): boolean {
if (arr1.length !== arr2.length) return false;
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) return false;
}
return true;
}
}

@@ -138,0 +231,0 @@

import { FundingInput, FundingInputV0, MessageType } from '@node-dlc/messaging';
import { Decimal } from 'decimal.js';

@@ -33,5 +34,8 @@ const BATCH_FUND_TX_BASE_WEIGHT = 42;

const futureFeeWeight = 249 + 4 * payoutSPK.length;
const futureFeeVBytes = Math.ceil(futureFeeWeight / 4);
const futureFee =
this.feeRate * BigInt(futureFeeVBytes) * BigInt(numContracts);
const futureFeeVBytes = new Decimal(futureFeeWeight)
.times(numContracts)
.div(4)
.ceil()
.toNumber();
const futureFee = this.feeRate * BigInt(futureFeeVBytes);

@@ -46,3 +50,3 @@ // https://github.com/discreetlogcontracts/dlcspecs/blob/8ee4bbe816c9881c832b1ce320b9f14c72e3506f/Transactions.md#expected-weight-of-the-funding-transaction

const weight = outputWeight + inputWeight;
const vbytes = Math.ceil(weight / 4);
const vbytes = new Decimal(weight).div(4).ceil().toNumber();
const fundingFee = this.feeRate * BigInt(vbytes);

@@ -120,3 +124,3 @@

const weight = 213 + outputWeight + inputWeight;
const vbytes = Math.ceil(weight / 4);
const vbytes = new Decimal(weight).div(4).ceil().toNumber();
const fee = this.feeRate * BigInt(vbytes);

@@ -123,0 +127,0 @@

{
"name": "@node-dlc/core",
"version": "0.23.0",
"version": "0.23.1",
"description": "DLC Core",

@@ -26,4 +26,4 @@ "scripts": {

"dependencies": {
"@node-dlc/bitcoin": "^0.23.0",
"@node-dlc/messaging": "^0.23.0",
"@node-dlc/bitcoin": "^0.23.1",
"@node-dlc/messaging": "^0.23.1",
"@node-lightning/core": "0.26.1",

@@ -40,3 +40,3 @@ "bignumber.js": "^9.0.1",

},
"gitHead": "896e77ea5caca60c20f2d9b40c10b1cfdda4f2be"
"gitHead": "d456ac896161a1e25e847c62f8996a23278cd36c"
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc