@atomicfinance/bitcoin-ddk-provider 4.1.12 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -85,7 +85,20 @@ import {
85
85
  import crypto from 'crypto';
86
86
  import { ECPairFactory, ECPairInterface } from 'ecpair';
87
87
 
88
- import { checkTypes, generateSerialId, outputsToPayouts } from './utils/Utils';
88
+ import {
89
+ checkTypes,
90
+ computeContractId,
91
+ createP2MSMultisig,
92
+ createP2WSHMultisig,
93
+ createP2WSHMultisigFromOrdered,
94
+ ensureBuffer,
95
+ ensureCompactSignature,
96
+ ensureDerSignature,
97
+ generateSerialId,
98
+ orderPubkeysLexicographically,
99
+ outputsToPayouts,
100
+ sortFundingInputsBySerialId,
101
+ } from './utils/Utils';
89
102
 
90
103
  const ECPair = ECPairFactory(ecc);
91
104
 
@@ -105,220 +118,6 @@ export default class BitcoinDdkProvider extends Provider {
105
118
  }
106
119
  }
107
120
 
108
- /**
109
- * Helper function to ensure we have a Buffer object
110
- * Handles cases where Buffer objects have been serialized/deserialized
111
- */
112
- private ensureBuffer(
113
- bufferLike: Buffer | { type: string; data: number[] } | any,
114
- ): Buffer {
115
- if (Buffer.isBuffer(bufferLike)) {
116
- return bufferLike;
117
- }
118
- if (bufferLike && bufferLike.type === 'Buffer' && bufferLike.data) {
119
- return Buffer.from(bufferLike.data);
120
- }
121
- return bufferLike;
122
- }
123
-
124
- /**
125
- * Detect if signature is in compact format (64 bytes) or DER format
126
- * and convert compact to DER if needed, adding SIGHASH_ALL flag
127
- */
128
- private ensureDerSignature(signature: Buffer): Buffer {
129
- // If signature is 64 bytes, it's likely compact format (32-byte r + 32-byte s)
130
- if (signature.length === 64) {
131
- // Convert compact signature to DER format
132
- const r = signature.slice(0, 32);
133
- const s = signature.slice(32, 64);
134
-
135
- // Create DER encoding manually
136
- // DER format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
137
-
138
- // Remove leading zeros from r and s, but keep at least one byte
139
- let rBytes = r;
140
- while (
141
- rBytes.length > 1 &&
142
- rBytes[0] === 0x00 &&
143
- (rBytes[1] & 0x80) === 0
144
- ) {
145
- rBytes = rBytes.slice(1);
146
- }
147
-
148
- let sBytes = s;
149
- while (
150
- sBytes.length > 1 &&
151
- sBytes[0] === 0x00 &&
152
- (sBytes[1] & 0x80) === 0
153
- ) {
154
- sBytes = sBytes.slice(1);
155
- }
156
-
157
- // Add padding byte if high bit is set (to keep numbers positive)
158
- if ((rBytes[0] & 0x80) !== 0) {
159
- rBytes = Buffer.concat([Buffer.from([0x00]), rBytes]);
160
- }
161
- if ((sBytes[0] & 0x80) !== 0) {
162
- sBytes = Buffer.concat([Buffer.from([0x00]), sBytes]);
163
- }
164
-
165
- const totalLength = 2 + rBytes.length + 2 + sBytes.length;
166
-
167
- const derSignature = Buffer.concat([
168
- Buffer.from([0x30, totalLength]), // SEQUENCE tag and total length
169
- Buffer.from([0x02, rBytes.length]), // INTEGER tag and R length
170
- rBytes,
171
- Buffer.from([0x02, sBytes.length]), // INTEGER tag and S length
172
- sBytes,
173
- Buffer.from([0x01]), // SIGHASH_ALL flag
174
- ]);
175
-
176
- return derSignature;
177
- }
178
-
179
- // If it's already DER format, check if it has SIGHASH flag
180
- if (signature.length > 0 && signature[0] === 0x30) {
181
- // Check if it already has a SIGHASH flag (last byte should be 0x01 for SIGHASH_ALL)
182
- if (signature[signature.length - 1] !== 0x01) {
183
- // Add SIGHASH_ALL flag
184
- return Buffer.concat([signature, Buffer.from([0x01])]);
185
- }
186
- return signature;
187
- }
188
-
189
- // For other formats, return as-is
190
- return signature;
191
- }
192
-
193
- /**
194
- * Detect if signature is in DER format and convert to compact format (64 bytes)
195
- * by extracting r and s values, removing SIGHASH flag if present
196
- */
197
- private ensureCompactSignature(signature: Buffer): Buffer {
198
- // If signature is already 64 bytes, it's likely already compact format
199
- if (signature.length === 64) {
200
- return signature;
201
- }
202
-
203
- // Check if it's DER format (starts with 0x30)
204
- if (signature.length > 6 && signature[0] === 0x30) {
205
- let derSig = signature;
206
-
207
- // Remove SIGHASH flag if present (last byte is typically 0x01 for SIGHASH_ALL)
208
- if (signature[signature.length - 1] === 0x01) {
209
- derSig = signature.slice(0, -1);
210
- }
211
-
212
- // Parse DER format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
213
- if (derSig[0] !== 0x30) {
214
- throw new Error('Invalid DER signature: missing SEQUENCE tag');
215
- }
216
-
217
- const totalLength = derSig[1];
218
- if (derSig.length < totalLength + 2) {
219
- throw new Error('Invalid DER signature: length mismatch');
220
- }
221
-
222
- let offset = 2;
223
-
224
- // Parse R value
225
- if (derSig[offset] !== 0x02) {
226
- throw new Error('Invalid DER signature: missing INTEGER tag for R');
227
- }
228
- offset++;
229
-
230
- const rLength = derSig[offset];
231
- offset++;
232
-
233
- if (offset + rLength > derSig.length) {
234
- throw new Error(
235
- 'Invalid DER signature: R length exceeds signature length',
236
- );
237
- }
238
-
239
- let rBytes = derSig.slice(offset, offset + rLength);
240
- offset += rLength;
241
-
242
- // Parse S value
243
- if (derSig[offset] !== 0x02) {
244
- throw new Error('Invalid DER signature: missing INTEGER tag for S');
245
- }
246
- offset++;
247
-
248
- const sLength = derSig[offset];
249
- offset++;
250
-
251
- if (offset + sLength > derSig.length) {
252
- throw new Error(
253
- 'Invalid DER signature: S length exceeds signature length',
254
- );
255
- }
256
-
257
- let sBytes = derSig.slice(offset, offset + sLength);
258
-
259
- // Remove leading zero padding from r and s (DER may pad to prevent negative interpretation)
260
- while (rBytes.length > 1 && rBytes[0] === 0x00) {
261
- rBytes = rBytes.slice(1);
262
- }
263
- while (sBytes.length > 1 && sBytes[0] === 0x00) {
264
- sBytes = sBytes.slice(1);
265
- }
266
-
267
- // Pad to 32 bytes each (compact format requires exactly 32 bytes for r and s)
268
- while (rBytes.length < 32) {
269
- rBytes = Buffer.concat([Buffer.from([0x00]), rBytes]);
270
- }
271
- while (sBytes.length < 32) {
272
- sBytes = Buffer.concat([Buffer.from([0x00]), sBytes]);
273
- }
274
-
275
- if (rBytes.length !== 32 || sBytes.length !== 32) {
276
- throw new Error('Invalid signature values: r or s exceeds 32 bytes');
277
- }
278
-
279
- // Combine r and s into 64-byte compact format
280
- return Buffer.concat([rBytes, sBytes]);
281
- }
282
-
283
- // For other formats, throw error as we can't convert
284
- throw new Error(
285
- 'Unable to convert signature to compact format: unknown format',
286
- );
287
- }
288
-
289
- /**
290
- * Compute contract ID from fund transaction ID, output index, and temporary contract ID
291
- * Matches the Rust implementation in rust-dlc
292
- */
293
- private computeContractId(
294
- fundTxId: Buffer,
295
- fundOutputIndex: number,
296
- temporaryContractId: Buffer,
297
- ): Buffer {
298
- if (fundTxId.length !== 32) {
299
- throw new Error('Fund transaction ID must be 32 bytes');
300
- }
301
- if (temporaryContractId.length !== 32) {
302
- throw new Error('Temporary contract ID must be 32 bytes');
303
- }
304
- if (fundOutputIndex > 0xffff) {
305
- throw new Error('Fund output index must fit in 16 bits');
306
- }
307
-
308
- const result = Buffer.alloc(32);
309
-
310
- // XOR fund_tx_id with temporary_id, with byte order reversal for fund_tx_id
311
- for (let i = 0; i < 32; i++) {
312
- result[i] = fundTxId[31 - i] ^ temporaryContractId[i];
313
- }
314
-
315
- // XOR the fund output index into the last two bytes
316
- result[30] ^= (fundOutputIndex >> 8) & 0xff; // High byte
317
- result[31] ^= fundOutputIndex & 0xff; // Low byte
318
-
319
- return result;
320
- }
321
-
322
121
  /**
323
122
  * Create refund signature using PSBT method instead of DDK
324
123
  */
@@ -338,22 +137,11 @@ export default class BitcoinDdkProvider extends Provider {
338
137
  );
339
138
  }
340
139
 
341
- // Create the same funding script as createDlcClose
342
- const fundingPubKeys =
343
- Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
344
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
345
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
346
-
347
- const p2ms = payments.p2ms({
348
- m: 2,
349
- pubkeys: fundingPubKeys,
140
+ const paymentVariant = createP2WSHMultisig(
141
+ dlcOffer.fundingPubkey,
142
+ dlcAccept.fundingPubkey,
350
143
  network,
351
- });
352
-
353
- const paymentVariant = payments.p2wsh({
354
- redeem: p2ms,
355
- network,
356
- });
144
+ );
357
145
 
358
146
  // Add the funding input with sequence from refund transaction
359
147
  psbt.addInput({
@@ -404,7 +192,7 @@ export default class BitcoinDdkProvider extends Provider {
404
192
  const derSignature = partialSig.signature;
405
193
 
406
194
  // Convert DER signature to compact format
407
- return this.ensureCompactSignature(derSignature);
195
+ return ensureCompactSignature(derSignature);
408
196
  }
409
197
 
410
198
  /**
@@ -962,12 +750,19 @@ export default class BitcoinDdkProvider extends Provider {
962
750
  }),
963
751
  );
964
752
 
965
- // Calculate input amounts
966
- const localInputAmount = localRegularInputs.reduce<number>(
753
+ // Calculate input amounts (including both regular inputs and DLC inputs)
754
+ const localRegularInputAmount = localRegularInputs.reduce<number>(
967
755
  (prev, cur) => prev + cur.amount.GetSatoshiAmount(),
968
756
  0,
969
757
  );
970
758
 
759
+ const localDlcInputAmount = localDlcInputs.reduce<number>(
760
+ (prev, cur) => prev + Number(cur.fundAmount),
761
+ 0,
762
+ );
763
+
764
+ const localInputAmount = localRegularInputAmount + localDlcInputAmount;
765
+
971
766
  const remoteInputAmount = remoteInputs.reduce<number>(
972
767
  (prev, cur) => prev + cur.amount.GetSatoshiAmount(),
973
768
  0,
@@ -1046,7 +841,7 @@ export default class BitcoinDdkProvider extends Provider {
1046
841
  BigInt(dlcOffer.feeRatePerVb),
1047
842
  0,
1048
843
  dlcOffer.cetLocktime,
1049
- dlcOffer.fundOutputSerialId,
844
+ BigInt(dlcOffer.fundOutputSerialId),
1050
845
  );
1051
846
  } else {
1052
847
  // Use regular DLC transactions when no DLC inputs
@@ -1280,19 +1075,12 @@ export default class BitcoinDdkProvider extends Provider {
1280
1075
 
1281
1076
  const cetsHex = dlcTxs.cets.map((cet) => cet.serialize().toString('hex'));
1282
1077
 
1283
- // Create the correct P2WSH multisig funding script
1284
- const fundingPubKeys =
1285
- Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
1286
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
1287
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
1288
-
1289
- const p2ms = payments.p2ms({
1290
- m: 2,
1291
- pubkeys: fundingPubKeys,
1292
- network,
1293
- });
1294
-
1295
1078
  // We need the redeem script (multisig), not the P2WSH output
1079
+ const p2ms = createP2MSMultisig(
1080
+ dlcOffer.fundingPubkey,
1081
+ dlcAccept.fundingPubkey,
1082
+ network,
1083
+ );
1296
1084
  const fundingSPK = p2ms.output!;
1297
1085
 
1298
1086
  // For finding the private key, we still need the individual P2WPKH address
@@ -1499,16 +1287,12 @@ export default class BitcoinDdkProvider extends Provider {
1499
1287
 
1500
1288
  // Create the correct P2WSH multisig funding script for verification
1501
1289
  const network = await this.getConnectedNetwork();
1502
- const verifyFundingPubKeys =
1503
- Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
1504
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
1505
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
1506
-
1507
- const verifyP2ms = payments.p2ms({
1508
- m: 2,
1509
- pubkeys: verifyFundingPubKeys,
1290
+
1291
+ const verifyP2ms = createP2MSMultisig(
1292
+ dlcOffer.fundingPubkey,
1293
+ dlcAccept.fundingPubkey,
1510
1294
  network,
1511
- });
1295
+ );
1512
1296
 
1513
1297
  // We need the redeem script (multisig), not the P2WSH output
1514
1298
  const fundingSPK = verifyP2ms.output!;
@@ -1658,7 +1442,7 @@ export default class BitcoinDdkProvider extends Provider {
1658
1442
  }
1659
1443
  }
1660
1444
 
1661
- private async CreateFundingSigsAlt(
1445
+ private async CreateFundingSigs(
1662
1446
  dlcOffer: DlcOffer,
1663
1447
  dlcAccept: DlcAccept,
1664
1448
  dlcTxs: DlcTransactions,
@@ -1670,14 +1454,10 @@ export default class BitcoinDdkProvider extends Provider {
1670
1454
  const network = await this.getConnectedNetwork();
1671
1455
  const psbt = new Psbt({ network });
1672
1456
 
1673
- // Combine all funding inputs from both parties
1674
- const allFundingInputs = [
1457
+ const allFundingInputs = sortFundingInputsBySerialId([
1675
1458
  ...dlcOffer.fundingInputs,
1676
1459
  ...dlcAccept.fundingInputs,
1677
- ];
1678
-
1679
- // Sort by inputSerialId to reconstruct proper transaction order
1680
- allFundingInputs.sort((a, b) => Number(a.inputSerialId - b.inputSerialId));
1460
+ ]);
1681
1461
 
1682
1462
  // Add all inputs to PSBT with proper witnessUtxo
1683
1463
  for (const fundingInput of allFundingInputs) {
@@ -1756,11 +1536,42 @@ export default class BitcoinDdkProvider extends Provider {
1756
1536
 
1757
1537
  // Check if this is a DLC input (2-of-2 multisig from previous DLC)
1758
1538
  if (fundingInput.dlcInput) {
1759
- // For DLC inputs, we'll need to handle 2-of-2 multisig signing differently
1760
- // For now, throw an error as this requires implementing multisig support
1761
- throw new Error(
1762
- 'DLC input signing not yet implemented in CreateFundingSigsAlt',
1539
+ const paymentVariant = createP2WSHMultisig(
1540
+ fundingInput.dlcInput.localFundPubkey,
1541
+ fundingInput.dlcInput.remoteFundPubkey,
1542
+ network,
1763
1543
  );
1544
+
1545
+ const redeemScript = paymentVariant.redeem.output;
1546
+
1547
+ // Find the correct private key for this DLC input
1548
+ const dlcPrivKey = await this.findDlcFundingPrivateKey(
1549
+ fundingInput.dlcInput.localFundPubkey.toString('hex'),
1550
+ fundingInput.dlcInput.remoteFundPubkey.toString('hex'),
1551
+ );
1552
+ const keyPair = ECPair.fromPrivateKey(Buffer.from(dlcPrivKey, 'hex'));
1553
+
1554
+ // Update the input with the witnessScript before signing
1555
+ psbt.updateInput(inputIndex, {
1556
+ witnessScript: redeemScript,
1557
+ });
1558
+
1559
+ psbt.signInput(inputIndex, keyPair);
1560
+
1561
+ const inputData = psbt.data.inputs[inputIndex];
1562
+ const partialSigs = inputData.partialSig;
1563
+ if (!partialSigs || partialSigs.length === 0) {
1564
+ throw new Error(
1565
+ `No signatures found for input ${inputIndex} after signing`,
1566
+ );
1567
+ }
1568
+
1569
+ const sigWitness = new ScriptWitnessV0();
1570
+ sigWitness.witness = ensureBuffer(partialSigs[0].signature);
1571
+ const pubKeyWitness = new ScriptWitnessV0();
1572
+ pubKeyWitness.witness = keyPair.publicKey;
1573
+
1574
+ witnessElements.push([sigWitness, pubKeyWitness]);
1764
1575
  } else {
1765
1576
  // For P2WPKH inputs, use PSBT signing
1766
1577
  psbt.signInput(inputIndex, keyPair);
@@ -1776,7 +1587,7 @@ export default class BitcoinDdkProvider extends Provider {
1776
1587
 
1777
1588
  // For P2WPKH, create witness manually: [signature, publicKey]
1778
1589
  const sigWitness = new ScriptWitnessV0();
1779
- sigWitness.witness = this.ensureBuffer(partialSigs[0].signature);
1590
+ sigWitness.witness = ensureBuffer(partialSigs[0].signature);
1780
1591
  const pubKeyWitness = new ScriptWitnessV0();
1781
1592
  pubKeyWitness.witness = keyPair.publicKey;
1782
1593
 
@@ -1792,7 +1603,7 @@ export default class BitcoinDdkProvider extends Provider {
1792
1603
  return fundingSignatures;
1793
1604
  }
1794
1605
 
1795
- private async VerifyFundingSigsAlt(
1606
+ private async VerifyFundingSigs(
1796
1607
  dlcOffer: DlcOffer,
1797
1608
  dlcAccept: DlcAccept,
1798
1609
  dlcSign: DlcSign,
@@ -1811,12 +1622,10 @@ export default class BitcoinDdkProvider extends Provider {
1811
1622
  ? dlcAccept.fundingInputs
1812
1623
  : dlcOffer.fundingInputs;
1813
1624
 
1814
- // Combine all inputs to get the correct ordering
1815
- const allFundingInputs = [
1625
+ const allFundingInputs = sortFundingInputsBySerialId([
1816
1626
  ...dlcOffer.fundingInputs,
1817
1627
  ...dlcAccept.fundingInputs,
1818
- ];
1819
- allFundingInputs.sort((a, b) => Number(a.inputSerialId - b.inputSerialId));
1628
+ ]);
1820
1629
 
1821
1630
  // Compare transaction IDs
1822
1631
  const dlcFundTxId = dlcTxs.fundTx.txId.toString();
@@ -1830,10 +1639,21 @@ export default class BitcoinDdkProvider extends Provider {
1830
1639
  // Add all inputs (needed for proper sighash calculation)
1831
1640
  for (const input of allFundingInputs) {
1832
1641
  const prevOutput = input.prevTx.outputs[input.prevTxVout];
1642
+
1643
+ // Use sequence from the original transaction to ensure consistency with CreateFundingTx
1644
+ const originalInput = transaction.ins.find(
1645
+ (txInput) =>
1646
+ txInput.hash.reverse().toString('hex') ===
1647
+ input.prevTx.txId.toString() && txInput.index === input.prevTxVout,
1648
+ );
1649
+ const sequenceValue = originalInput
1650
+ ? originalInput.sequence
1651
+ : Number(input.sequence);
1652
+
1833
1653
  psbt.addInput({
1834
1654
  hash: input.prevTx.txId.toString(),
1835
1655
  index: input.prevTxVout,
1836
- sequence: 0,
1656
+ sequence: sequenceValue,
1837
1657
  witnessUtxo: {
1838
1658
  script: Buffer.from(prevOutput.scriptPubKey.serialize().subarray(1)),
1839
1659
  value: Number(prevOutput.value.sats),
@@ -1859,8 +1679,61 @@ export default class BitcoinDdkProvider extends Provider {
1859
1679
  // Add the funding signatures to the PSBT as partial signatures
1860
1680
  let witnessIndex = 0;
1861
1681
  for (const fundingInput of signingPartyInputs) {
1862
- // Skip DLC inputs for now (same as CreateFundingSigsAlt)
1682
+ // Handle DLC inputs with multisig verification
1863
1683
  if (fundingInput.dlcInput) {
1684
+ const dlcInput = fundingInput.dlcInput;
1685
+
1686
+ const paymentVariant = createP2WSHMultisig(
1687
+ dlcInput.localFundPubkey,
1688
+ dlcInput.remoteFundPubkey,
1689
+ network,
1690
+ );
1691
+
1692
+ // Find this input's index in the sorted transaction
1693
+ const inputIndex = allFundingInputs.findIndex(
1694
+ (input) =>
1695
+ input.prevTx.txId.toString() ===
1696
+ fundingInput.prevTx.txId.toString() &&
1697
+ input.prevTxVout === fundingInput.prevTxVout,
1698
+ );
1699
+
1700
+ if (inputIndex === -1) {
1701
+ throw new Error(
1702
+ `DLC input not found in transaction: ${fundingInput.prevTx.txId.toString()}:${fundingInput.prevTxVout}`,
1703
+ );
1704
+ }
1705
+
1706
+ // Update the input with the witnessScript for verification
1707
+ psbt.updateInput(inputIndex, {
1708
+ witnessScript: paymentVariant.redeem.output,
1709
+ });
1710
+
1711
+ // Add the DLC signature from witness elements if available
1712
+ if (witnessIndex < dlcSign.fundingSignatures.witnessElements.length) {
1713
+ const witnessElement =
1714
+ dlcSign.fundingSignatures.witnessElements[witnessIndex];
1715
+ const signature = witnessElement[0].witness;
1716
+ const publicKey = witnessElement[1].witness;
1717
+
1718
+ psbt.updateInput(inputIndex, {
1719
+ partialSig: [
1720
+ {
1721
+ pubkey: publicKey,
1722
+ signature: ensureDerSignature(signature),
1723
+ },
1724
+ ],
1725
+ });
1726
+
1727
+ // Verify the signature for this DLC input
1728
+ psbt.validateSignaturesOfInput(
1729
+ inputIndex,
1730
+ (pubkey: Buffer, msghash: Buffer, signature: Buffer) => {
1731
+ return ecc.verify(msghash, pubkey, signature);
1732
+ },
1733
+ );
1734
+
1735
+ witnessIndex++;
1736
+ }
1864
1737
  continue;
1865
1738
  }
1866
1739
 
@@ -1893,7 +1766,7 @@ export default class BitcoinDdkProvider extends Provider {
1893
1766
  partialSig: [
1894
1767
  {
1895
1768
  pubkey: publicKey,
1896
- signature: this.ensureDerSignature(signature),
1769
+ signature: ensureDerSignature(signature),
1897
1770
  },
1898
1771
  ],
1899
1772
  });
@@ -1909,6 +1782,114 @@ export default class BitcoinDdkProvider extends Provider {
1909
1782
  }
1910
1783
  }
1911
1784
 
1785
+ /**
1786
+ * Get sighash for funding transaction inputs
1787
+ * @param dlcOffer DLC Offer
1788
+ * @param dlcAccept DLC Accept
1789
+ * @param dlcTxs DLC Transactions
1790
+ * @param inputIndex Index of the input to get sighash for (optional, if not provided returns all)
1791
+ * @returns Array of sighashes for each input or single sighash if inputIndex specified
1792
+ */
1793
+ async getFundingTransactionSighash(
1794
+ dlcOffer: DlcOffer,
1795
+ dlcAccept: DlcAccept,
1796
+ dlcTxs: DlcTransactions,
1797
+ inputIndex?: number,
1798
+ ): Promise<string[] | string> {
1799
+ // Use the detailed function to ensure consistency
1800
+ const details = await this.getFundingTransactionSighashDetails(
1801
+ dlcOffer,
1802
+ dlcAccept,
1803
+ dlcTxs,
1804
+ );
1805
+
1806
+ if (inputIndex !== undefined) {
1807
+ // Return sighash for specific input
1808
+ if (inputIndex >= details.length) {
1809
+ throw new Error(
1810
+ `Input index ${inputIndex} out of range. Total inputs: ${details.length}`,
1811
+ );
1812
+ }
1813
+ return details[inputIndex].sighash;
1814
+ } else {
1815
+ // Return sighashes for all inputs
1816
+ return details.map((detail) => detail.sighash);
1817
+ }
1818
+ }
1819
+
1820
+ /**
1821
+ * Get detailed sighash information for funding transaction (useful for debugging)
1822
+ * @param dlcOffer DLC Offer
1823
+ * @param dlcAccept DLC Accept
1824
+ * @param dlcTxs DLC Transactions
1825
+ * @returns Detailed sighash information for each input
1826
+ */
1827
+ async getFundingTransactionSighashDetails(
1828
+ dlcOffer: DlcOffer,
1829
+ dlcAccept: DlcAccept,
1830
+ dlcTxs: DlcTransactions,
1831
+ ): Promise<
1832
+ Array<{
1833
+ inputIndex: number;
1834
+ txid: string;
1835
+ vout: number;
1836
+ sequence: number;
1837
+ scriptPubKey: string;
1838
+ value: number;
1839
+ sighash: string;
1840
+ }>
1841
+ > {
1842
+ const transaction = btTransaction.fromBuffer(
1843
+ Buffer.from(dlcTxs.fundTx.serialize()),
1844
+ );
1845
+
1846
+ const allFundingInputs = sortFundingInputsBySerialId([
1847
+ ...dlcOffer.fundingInputs,
1848
+ ...dlcAccept.fundingInputs,
1849
+ ]);
1850
+
1851
+ // Calculate detailed sighash information
1852
+ const details = [];
1853
+
1854
+ for (let i = 0; i < allFundingInputs.length; i++) {
1855
+ const input = allFundingInputs[i];
1856
+ const prevOutput = input.prevTx.outputs[input.prevTxVout];
1857
+
1858
+ const originalInput = transaction.ins.find(
1859
+ (txInput) =>
1860
+ txInput.hash.reverse().toString('hex') ===
1861
+ input.prevTx.txId.toString() && txInput.index === input.prevTxVout,
1862
+ );
1863
+ const sequenceValue = originalInput
1864
+ ? originalInput.sequence
1865
+ : Number(input.sequence);
1866
+
1867
+ const script = Buffer.from(
1868
+ prevOutput.scriptPubKey.serialize().subarray(1),
1869
+ );
1870
+ const value = Number(prevOutput.value.sats);
1871
+
1872
+ const sighash = transaction.hashForWitnessV0(
1873
+ i,
1874
+ script,
1875
+ value,
1876
+ btTransaction.SIGHASH_ALL,
1877
+ );
1878
+
1879
+ details.push({
1880
+ inputIndex: i,
1881
+ txid: input.prevTx.txId.toString(),
1882
+ vout: input.prevTxVout,
1883
+ sequence: sequenceValue,
1884
+ scriptPubKey: script.toString('hex'),
1885
+ value: value,
1886
+ sighash: sighash.toString('hex'),
1887
+ });
1888
+ }
1889
+
1890
+ return details;
1891
+ }
1892
+
1912
1893
  private async VerifyRefundSignatureAlt(
1913
1894
  dlcOffer: DlcOffer,
1914
1895
  dlcAccept: DlcAccept,
@@ -1926,7 +1907,7 @@ export default class BitcoinDdkProvider extends Provider {
1926
1907
  : dlcSign.refundSignature;
1927
1908
 
1928
1909
  // Ensure signature is in DER format (convert from compact if needed)
1929
- const refundSignature = this.ensureDerSignature(rawRefundSignature);
1910
+ const refundSignature = ensureDerSignature(rawRefundSignature);
1930
1911
 
1931
1912
  const signingPubkey = isOfferer
1932
1913
  ? dlcAccept.fundingPubkey
@@ -1942,22 +1923,11 @@ export default class BitcoinDdkProvider extends Provider {
1942
1923
  // Create a PSBT for the refund transaction verification using same approach as createDlcClose
1943
1924
  const psbt = new Psbt({ network });
1944
1925
 
1945
- // Create the same funding script as createDlcClose
1946
- const fundingPubKeys =
1947
- Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
1948
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
1949
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
1950
-
1951
- const p2ms = payments.p2ms({
1952
- m: 2,
1953
- pubkeys: fundingPubKeys,
1954
- network,
1955
- });
1956
-
1957
- const paymentVariant = payments.p2wsh({
1958
- redeem: p2ms,
1926
+ const paymentVariant = createP2WSHMultisig(
1927
+ dlcOffer.fundingPubkey,
1928
+ dlcAccept.fundingPubkey,
1959
1929
  network,
1960
- });
1930
+ );
1961
1931
 
1962
1932
  // Add the funding input with sequence from refund transaction
1963
1933
  psbt.addInput({
@@ -2011,23 +1981,11 @@ export default class BitcoinDdkProvider extends Provider {
2011
1981
  ): Buffer {
2012
1982
  const network = this.getBitcoinJsNetwork();
2013
1983
 
2014
- // Sort funding pubkeys in lexicographical order as per DLC spec
2015
- const fundingPubKeys =
2016
- Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
2017
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
2018
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
2019
-
2020
- // Create 2-of-2 multisig script
2021
- const p2ms = payments.p2ms({
2022
- m: 2,
2023
- pubkeys: fundingPubKeys,
1984
+ const paymentVariant = createP2WSHMultisig(
1985
+ dlcOffer.fundingPubkey,
1986
+ dlcAccept.fundingPubkey,
2024
1987
  network,
2025
- });
2026
-
2027
- const paymentVariant = payments.p2wsh({
2028
- redeem: p2ms,
2029
- network,
2030
- });
1988
+ );
2031
1989
 
2032
1990
  return paymentVariant.redeem!.output!;
2033
1991
  }
@@ -2042,27 +2000,20 @@ export default class BitcoinDdkProvider extends Provider {
2042
2000
  const network = await this.getConnectedNetwork();
2043
2001
  const psbt = new Psbt({ network });
2044
2002
 
2045
- // Combine and sort all funding inputs by serial ID (same as CreateFundingSigsAlt)
2046
- const allFundingInputs = [
2003
+ const allFundingInputs = sortFundingInputsBySerialId([
2047
2004
  ...dlcOffer.fundingInputs,
2048
2005
  ...dlcAccept.fundingInputs,
2049
- ];
2050
- allFundingInputs.sort((a, b) => Number(a.inputSerialId - b.inputSerialId));
2006
+ ]);
2051
2007
 
2052
2008
  // Create a map of input txid:vout to witness elements
2053
2009
  const witnessMap = new Map<string, ScriptWitnessV0[]>();
2054
2010
 
2055
- // Map witness elements correctly - CreateFundingSigsAlt only creates witness elements
2011
+ // Map witness elements correctly - CreateFundingSigs only creates witness elements
2056
2012
  // for the party's own inputs, so we need to map them correctly
2057
2013
 
2058
- // For dlcSign (offerer's signatures), map to offerer's inputs only
2014
+ // For dlcSign (offerer's signatures), map to offerer's inputs (including DLC inputs)
2059
2015
  let offererWitnessIndex = 0;
2060
2016
  dlcOffer.fundingInputs.forEach((fundingInput) => {
2061
- // Skip DLC inputs for now as in CreateFundingSigsAlt
2062
- if (fundingInput.dlcInput) {
2063
- return;
2064
- }
2065
-
2066
2017
  if (
2067
2018
  offererWitnessIndex < dlcSign.fundingSignatures.witnessElements.length
2068
2019
  ) {
@@ -2075,14 +2026,9 @@ export default class BitcoinDdkProvider extends Provider {
2075
2026
  }
2076
2027
  });
2077
2028
 
2078
- // For fundingSignatures (accepter's signatures), map to accepter's inputs only
2029
+ // For fundingSignatures (accepter's signatures), map to accepter's inputs (including DLC inputs)
2079
2030
  let accepterWitnessIndex = 0;
2080
2031
  dlcAccept.fundingInputs.forEach((fundingInput) => {
2081
- // Skip DLC inputs for now as in CreateFundingSigsAlt
2082
- if (fundingInput.dlcInput) {
2083
- return;
2084
- }
2085
-
2086
2032
  if (accepterWitnessIndex < fundingSignatures.witnessElements.length) {
2087
2033
  const key = `${fundingInput.prevTx.txId.toString()}:${fundingInput.prevTxVout}`;
2088
2034
  witnessMap.set(
@@ -2101,13 +2047,13 @@ export default class BitcoinDdkProvider extends Provider {
2101
2047
  for (const fundingInput of allFundingInputs) {
2102
2048
  const prevOut = fundingInput.prevTx.outputs[fundingInput.prevTxVout];
2103
2049
 
2104
- // Use same script handling as CreateFundingSigsAlt
2050
+ // Use same script handling as CreateFundingSigs
2105
2051
  const witnessUtxo = {
2106
2052
  script: Buffer.from(prevOut.scriptPubKey.serialize().subarray(1)),
2107
2053
  value: Number(prevOut.value.sats),
2108
2054
  };
2109
2055
 
2110
- // Use sequence from the original transaction (same as CreateFundingSigsAlt)
2056
+ // Use sequence from the original transaction (same as CreateFundingSigs)
2111
2057
  const originalInput = originalTransaction.ins.find(
2112
2058
  (input) =>
2113
2059
  input.hash.reverse().toString('hex') ===
@@ -2145,13 +2091,76 @@ export default class BitcoinDdkProvider extends Provider {
2145
2091
 
2146
2092
  const witnessElements = witnessMap.get(inputKey);
2147
2093
  if (witnessElements && witnessElements.length === 2) {
2148
- // Skip DLC inputs for now as requested
2094
+ // Handle DLC inputs with multisig finalization
2149
2095
  if (fundingInput.dlcInput) {
2096
+ const dlcInput = fundingInput.dlcInput;
2097
+
2098
+ // Create the multisig script for finalization
2099
+ // Use lexicographic ordering to match DDK library behavior
2100
+ const orderedPubkeys = orderPubkeysLexicographically(
2101
+ dlcInput.localFundPubkey,
2102
+ dlcInput.remoteFundPubkey,
2103
+ );
2104
+
2105
+ const paymentVariant = createP2WSHMultisigFromOrdered(
2106
+ orderedPubkeys,
2107
+ network,
2108
+ );
2109
+
2110
+ // Update the input with the witnessScript
2111
+ psbt.updateInput(inputIndex, {
2112
+ witnessScript: paymentVariant.redeem.output,
2113
+ });
2114
+
2115
+ // Sign this DLC input ourselves
2116
+ const dlcPrivKey = await this.findDlcFundingPrivateKey(
2117
+ dlcInput.localFundPubkey.toString('hex'),
2118
+ dlcInput.remoteFundPubkey.toString('hex'),
2119
+ );
2120
+ const keyPair = ECPair.fromPrivateKey(Buffer.from(dlcPrivKey, 'hex'));
2121
+
2122
+ psbt.signInput(inputIndex, keyPair);
2123
+
2124
+ // Get our signature and determine which pubkey it corresponds to
2125
+ const ourPartialSig = psbt.data.inputs[inputIndex].partialSig[0];
2126
+ const ourSig = ensureDerSignature(ourPartialSig.signature);
2127
+ const ourPubkey = ourPartialSig.pubkey;
2128
+
2129
+ // Get the other party's signature from witnessElements
2130
+ const otherPartySig = ensureDerSignature(witnessElements[0].witness);
2131
+
2132
+ // Order signatures according to the lexicographic pubkey order used in the multisig script
2133
+ let sig1: Buffer, sig2: Buffer;
2134
+
2135
+ if (Buffer.compare(ourPubkey, orderedPubkeys[0]) === 0) {
2136
+ // Our signature corresponds to the first pubkey in lexicographic order
2137
+ sig1 = ourSig;
2138
+ sig2 = otherPartySig;
2139
+ } else {
2140
+ // Our signature corresponds to the second pubkey in lexicographic order
2141
+ sig1 = otherPartySig;
2142
+ sig2 = ourSig;
2143
+ }
2144
+
2145
+ // Finalize with proper multisig witness: [OP_0, sig1, sig2, redeemScript]
2146
+ psbt.finalizeInput(inputIndex, () => ({
2147
+ finalScriptSig: Buffer.alloc(0),
2148
+ finalScriptWitness: Buffer.concat([
2149
+ Buffer.from([0x04]), // witness stack count
2150
+ Buffer.from([0x00]), // OP_0 (Bitcoin multisig bug)
2151
+ Buffer.from([sig1.length]),
2152
+ sig1,
2153
+ Buffer.from([sig2.length]),
2154
+ sig2,
2155
+ Buffer.from([paymentVariant.redeem.output.length]),
2156
+ paymentVariant.redeem.output,
2157
+ ]),
2158
+ }));
2150
2159
  continue;
2151
2160
  }
2152
2161
 
2153
2162
  // For P2WPKH inputs, finalize the input with witness data
2154
- const signature = this.ensureDerSignature(witnessElements[0].witness);
2163
+ const signature = ensureDerSignature(witnessElements[0].witness);
2155
2164
  const publicKey = witnessElements[1].witness;
2156
2165
 
2157
2166
  // Try a simpler approach - let bitcoinjs-lib handle witness construction
@@ -2255,15 +2264,13 @@ Payout Group not found',
2255
2264
  }
2256
2265
 
2257
2266
  async FindOutcomeIndexFromHyperbolaPayoutCurvePiece(
2258
- _dlcOffer: DlcOffer,
2267
+ dlcOffer: DlcOffer,
2259
2268
  contractDescriptor: NumericalDescriptor,
2260
2269
  contractOraclePairIndex: number,
2261
2270
  hyperbolaPayoutCurvePiece: HyperbolaPayoutCurvePiece,
2262
2271
  oracleAttestation: OracleAttestation,
2263
2272
  outcome: bigint,
2264
2273
  ): Promise<FindOutcomeResponse> {
2265
- const { dlcOffer } = checkTypes({ _dlcOffer });
2266
-
2267
2274
  const hyperbolaCurve = HyperbolaPayoutCurve.fromPayoutCurvePiece(
2268
2275
  hyperbolaPayoutCurvePiece,
2269
2276
  );
@@ -2793,21 +2800,15 @@ Payout Group not found even with brute force search',
2793
2800
 
2794
2801
  const network = await this.getConnectedNetwork();
2795
2802
 
2796
- const fundingPubKeys =
2797
- Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
2798
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
2799
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
2800
-
2801
- const p2ms = payments.p2ms({
2802
- m: 2,
2803
- pubkeys: fundingPubKeys,
2804
- network,
2805
- });
2803
+ const fundingPubKeys = orderPubkeysLexicographically(
2804
+ dlcOffer.fundingPubkey,
2805
+ dlcAccept.fundingPubkey,
2806
+ );
2806
2807
 
2807
- const paymentVariant = payments.p2wsh({
2808
- redeem: p2ms,
2808
+ const paymentVariant = createP2WSHMultisigFromOrdered(
2809
+ fundingPubKeys,
2809
2810
  network,
2810
- });
2811
+ );
2811
2812
 
2812
2813
  const sigHashRequestPromises: Promise<string>[] = [];
2813
2814
  const sigHashes = [];
@@ -2904,21 +2905,15 @@ Payout Group not found even with brute force search',
2904
2905
 
2905
2906
  const network = await this.getConnectedNetwork();
2906
2907
 
2907
- const fundingPubKeys =
2908
- Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
2909
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
2910
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
2911
-
2912
- const p2ms = payments.p2ms({
2913
- m: 2,
2914
- pubkeys: fundingPubKeys,
2915
- network,
2916
- });
2908
+ const fundingPubKeys = orderPubkeysLexicographically(
2909
+ dlcOffer.fundingPubkey,
2910
+ dlcAccept.fundingPubkey,
2911
+ );
2917
2912
 
2918
- const paymentVariant = payments.p2wsh({
2919
- redeem: p2ms,
2913
+ const paymentVariant = createP2WSHMultisigFromOrdered(
2914
+ fundingPubKeys,
2920
2915
  network,
2921
- });
2916
+ );
2922
2917
 
2923
2918
  const pubkey = isOfferer ? dlcAccept.fundingPubkey : dlcOffer.fundingPubkey;
2924
2919
 
@@ -3087,13 +3082,7 @@ Payout Group not found even with brute force search',
3087
3082
  ),
3088
3083
  );
3089
3084
 
3090
- const fundingInputs: FundingInput[] = _fundingInputs.map(
3091
- (input) => input as FundingInput,
3092
- );
3093
-
3094
- fundingInputs.sort(
3095
- (a, b) => Number(a.inputSerialId) - Number(b.inputSerialId),
3096
- );
3085
+ const fundingInputs = sortFundingInputsBySerialId(_fundingInputs);
3097
3086
 
3098
3087
  const fundOutputSerialId = generateSerialId();
3099
3088
 
@@ -3283,10 +3272,8 @@ Payout Group not found even with brute force search',
3283
3272
  ),
3284
3273
  );
3285
3274
 
3286
- fundingInputs = _fundingInputs.map((input) => input as FundingInput);
3287
-
3288
- fundingInputs.sort(
3289
- (a, b) => Number(a.inputSerialId) - Number(b.inputSerialId),
3275
+ fundingInputs = sortFundingInputsBySerialId(
3276
+ _fundingInputs.map((input) => input as FundingInput),
3290
3277
  );
3291
3278
  }
3292
3279
 
@@ -3371,7 +3358,7 @@ Payout Group not found even with brute force search',
3371
3358
 
3372
3359
  const _dlcTransactions = dlcTransactions;
3373
3360
 
3374
- const contractId = this.computeContractId(
3361
+ const contractId = computeContractId(
3375
3362
  _dlcTransactions.fundTx.txId.serialize(),
3376
3363
  _dlcTransactions.fundTxVout,
3377
3364
  dlcOffer.temporaryContractId,
@@ -3427,7 +3414,7 @@ Payout Group not found even with brute force search',
3427
3414
  true,
3428
3415
  );
3429
3416
 
3430
- const fundingSignatures = await this.CreateFundingSigsAlt(
3417
+ const fundingSignatures = await this.CreateFundingSigs(
3431
3418
  dlcOffer,
3432
3419
  dlcAccept,
3433
3420
  dlcTransactions,
@@ -3436,24 +3423,12 @@ Payout Group not found even with brute force search',
3436
3423
 
3437
3424
  const dlcTxs = dlcTransactions;
3438
3425
 
3439
- const contractId = this.computeContractId(
3426
+ const contractId = computeContractId(
3440
3427
  dlcTxs.fundTx.txId.serialize(),
3441
3428
  dlcTxs.fundTxVout,
3442
3429
  dlcOffer.temporaryContractId,
3443
3430
  );
3444
3431
 
3445
- assert(
3446
- Buffer.compare(
3447
- contractId,
3448
- this.computeContractId(
3449
- dlcTxs.fundTx.txId.serialize(),
3450
- dlcTxs.fundTxVout,
3451
- dlcOffer.temporaryContractId,
3452
- ),
3453
- ) === 0,
3454
- 'contractId must be the xor of funding txid, fundingOutputIndex and the tempContractId',
3455
- );
3456
-
3457
3432
  dlcTxs.contractId = contractId;
3458
3433
 
3459
3434
  dlcSign.contractId = contractId;
@@ -3480,6 +3455,17 @@ Payout Group not found even with brute force search',
3480
3455
  ): Promise<Tx> {
3481
3456
  let messagesList: Messages[] = [];
3482
3457
 
3458
+ const contractId = computeContractId(
3459
+ dlcTxs.fundTx.txId.serialize(),
3460
+ dlcTxs.fundTxVout,
3461
+ dlcOffer.temporaryContractId,
3462
+ );
3463
+
3464
+ assert(
3465
+ Buffer.compare(contractId, dlcSign.contractId) === 0,
3466
+ `Finalize Dlc Sign Contract ID mismatch: ${contractId.toString('hex')} !== ${dlcSign.contractId.toString('hex')}`,
3467
+ );
3468
+
3483
3469
  if (
3484
3470
  dlcOffer.contractInfo.type === MessageType.SingleContractInfo &&
3485
3471
  (dlcOffer.contractInfo as SingleContractInfo).contractDescriptor.type ===
@@ -3507,15 +3493,9 @@ Payout Group not found even with brute force search',
3507
3493
  false,
3508
3494
  );
3509
3495
 
3510
- await this.VerifyFundingSigsAlt(
3511
- dlcOffer,
3512
- dlcAccept,
3513
- dlcSign,
3514
- dlcTxs,
3515
- false,
3516
- );
3496
+ await this.VerifyFundingSigs(dlcOffer, dlcAccept, dlcSign, dlcTxs, false);
3517
3497
 
3518
- const fundingSignatures = await this.CreateFundingSigsAlt(
3498
+ const fundingSignatures = await this.CreateFundingSigs(
3519
3499
  dlcOffer,
3520
3500
  dlcAccept,
3521
3501
  dlcTxs,
@@ -3590,22 +3570,15 @@ Payout Group not found even with brute force search',
3590
3570
  );
3591
3571
  }
3592
3572
 
3593
- // Create the same funding script as createDlcClose
3594
- const fundingPubKeys =
3595
- Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
3596
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
3597
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
3598
-
3599
- const p2ms = payments.p2ms({
3600
- m: 2,
3601
- pubkeys: fundingPubKeys,
3602
- network,
3603
- });
3573
+ const fundingPubKeys = orderPubkeysLexicographically(
3574
+ dlcOffer.fundingPubkey,
3575
+ dlcAccept.fundingPubkey,
3576
+ );
3604
3577
 
3605
- const paymentVariant = payments.p2wsh({
3606
- redeem: p2ms,
3578
+ const paymentVariant = createP2WSHMultisigFromOrdered(
3579
+ fundingPubKeys,
3607
3580
  network,
3608
- });
3581
+ );
3609
3582
 
3610
3583
  // Add the funding input with sequence from refund transaction
3611
3584
  psbt.addInput({
@@ -3643,13 +3616,13 @@ Payout Group not found even with brute force search',
3643
3616
  // This is the offerer's pubkey, use dlcSign.refundSignature
3644
3617
  partialSigs.push({
3645
3618
  pubkey: pubkey,
3646
- signature: this.ensureDerSignature(dlcSign.refundSignature),
3619
+ signature: ensureDerSignature(dlcSign.refundSignature),
3647
3620
  });
3648
3621
  } else if (Buffer.compare(pubkey, dlcAccept.fundingPubkey) === 0) {
3649
3622
  // This is the accepter's pubkey, use dlcAccept.refundSignature
3650
3623
  partialSigs.push({
3651
3624
  pubkey: pubkey,
3652
- signature: this.ensureDerSignature(dlcAccept.refundSignature),
3625
+ signature: ensureDerSignature(dlcAccept.refundSignature),
3653
3626
  });
3654
3627
  }
3655
3628
  }
@@ -3722,21 +3695,11 @@ Payout Group not found even with brute force search',
3722
3695
  const network = await this.getConnectedNetwork();
3723
3696
  const psbt = new Psbt({ network });
3724
3697
 
3725
- const fundingPubKeys =
3726
- Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
3727
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
3728
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
3729
-
3730
- const p2ms = payments.p2ms({
3731
- m: 2,
3732
- pubkeys: fundingPubKeys,
3698
+ const paymentVariant = createP2WSHMultisig(
3699
+ dlcOffer.fundingPubkey,
3700
+ dlcAccept.fundingPubkey,
3733
3701
  network,
3734
- });
3735
-
3736
- const paymentVariant = payments.p2wsh({
3737
- redeem: p2ms,
3738
- network,
3739
- });
3702
+ );
3740
3703
 
3741
3704
  // Initiate and build PSBT
3742
3705
  let inputs: Input[] = _inputs;
@@ -3914,9 +3877,7 @@ Payout Group not found even with brute force search',
3914
3877
 
3915
3878
  // Extract close signature from psbt and decode it to only extract r and s values
3916
3879
  const closeSignature = await script.signature.decode(
3917
- this.ensureBuffer(
3918
- psbt.data.inputs[fundingInputIndex].partialSig[0].signature,
3919
- ),
3880
+ ensureBuffer(psbt.data.inputs[fundingInputIndex].partialSig[0].signature),
3920
3881
  ).signature;
3921
3882
 
3922
3883
  // Extract funding signatures from psbt
@@ -3928,9 +3889,9 @@ Payout Group not found even with brute force search',
3928
3889
  const witnessElements: ScriptWitnessV0[][] = [];
3929
3890
  for (let i = 0; i < inputSigs.length; i++) {
3930
3891
  const sigWitness = new ScriptWitnessV0();
3931
- sigWitness.witness = this.ensureBuffer(inputSigs[i].signature);
3892
+ sigWitness.witness = ensureBuffer(inputSigs[i].signature);
3932
3893
  const pubKeyWitness = new ScriptWitnessV0();
3933
- pubKeyWitness.witness = this.ensureBuffer(inputSigs[i].pubkey);
3894
+ pubKeyWitness.witness = ensureBuffer(inputSigs[i].pubkey);
3934
3895
  witnessElements.push([sigWitness, pubKeyWitness]);
3935
3896
  }
3936
3897
  const fundingSignatures = new FundingSignatures();
@@ -4176,21 +4137,11 @@ Payout Group not found even with brute force search',
4176
4137
  const network = await this.getConnectedNetwork();
4177
4138
  const psbt = new Psbt({ network });
4178
4139
 
4179
- const fundingPubKeys =
4180
- Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
4181
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
4182
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
4183
-
4184
- const p2ms = payments.p2ms({
4185
- m: 2,
4186
- pubkeys: fundingPubKeys,
4187
- network,
4188
- });
4189
-
4190
- const paymentVariant = payments.p2wsh({
4191
- redeem: p2ms,
4140
+ const paymentVariant = createP2WSHMultisig(
4141
+ dlcOffer.fundingPubkey,
4142
+ dlcAccept.fundingPubkey,
4192
4143
  network,
4193
- });
4144
+ );
4194
4145
 
4195
4146
  // Make temporary array to hold all inputs and then sort them
4196
4147
  // this method can be improved later
@@ -4524,16 +4475,10 @@ Payout Group not found even with brute force search',
4524
4475
  const network = await this.getConnectedNetwork();
4525
4476
 
4526
4477
  // Create 2-of-2 multisig payment using deterministic ordering
4527
- const p2ms = payments.p2ms({
4528
- m: 2,
4529
- pubkeys: orderedPubkeys,
4478
+ const paymentVariant = createP2WSHMultisigFromOrdered(
4479
+ orderedPubkeys,
4530
4480
  network,
4531
- });
4532
-
4533
- const paymentVariant = payments.p2wsh({
4534
- redeem: p2ms,
4535
- network,
4536
- });
4481
+ );
4537
4482
 
4538
4483
  const multisigAddress = paymentVariant.address!;
4539
4484