@atomicfinance/bitcoin-ddk-provider 4.1.13 → 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.
@@ -65,169 +65,6 @@ class BitcoinDdkProvider extends provider_1.default {
65
65
  await (0, utils_1.sleep)(10);
66
66
  }
67
67
  }
68
- /**
69
- * Helper function to ensure we have a Buffer object
70
- * Handles cases where Buffer objects have been serialized/deserialized
71
- */
72
- ensureBuffer(bufferLike) {
73
- if (Buffer.isBuffer(bufferLike)) {
74
- return bufferLike;
75
- }
76
- if (bufferLike && bufferLike.type === 'Buffer' && bufferLike.data) {
77
- return Buffer.from(bufferLike.data);
78
- }
79
- return bufferLike;
80
- }
81
- /**
82
- * Detect if signature is in compact format (64 bytes) or DER format
83
- * and convert compact to DER if needed, adding SIGHASH_ALL flag
84
- */
85
- ensureDerSignature(signature) {
86
- // If signature is 64 bytes, it's likely compact format (32-byte r + 32-byte s)
87
- if (signature.length === 64) {
88
- // Convert compact signature to DER format
89
- const r = signature.slice(0, 32);
90
- const s = signature.slice(32, 64);
91
- // Create DER encoding manually
92
- // DER format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
93
- // Remove leading zeros from r and s, but keep at least one byte
94
- let rBytes = r;
95
- while (rBytes.length > 1 &&
96
- rBytes[0] === 0x00 &&
97
- (rBytes[1] & 0x80) === 0) {
98
- rBytes = rBytes.slice(1);
99
- }
100
- let sBytes = s;
101
- while (sBytes.length > 1 &&
102
- sBytes[0] === 0x00 &&
103
- (sBytes[1] & 0x80) === 0) {
104
- sBytes = sBytes.slice(1);
105
- }
106
- // Add padding byte if high bit is set (to keep numbers positive)
107
- if ((rBytes[0] & 0x80) !== 0) {
108
- rBytes = Buffer.concat([Buffer.from([0x00]), rBytes]);
109
- }
110
- if ((sBytes[0] & 0x80) !== 0) {
111
- sBytes = Buffer.concat([Buffer.from([0x00]), sBytes]);
112
- }
113
- const totalLength = 2 + rBytes.length + 2 + sBytes.length;
114
- const derSignature = Buffer.concat([
115
- Buffer.from([0x30, totalLength]), // SEQUENCE tag and total length
116
- Buffer.from([0x02, rBytes.length]), // INTEGER tag and R length
117
- rBytes,
118
- Buffer.from([0x02, sBytes.length]), // INTEGER tag and S length
119
- sBytes,
120
- Buffer.from([0x01]), // SIGHASH_ALL flag
121
- ]);
122
- return derSignature;
123
- }
124
- // If it's already DER format, check if it has SIGHASH flag
125
- if (signature.length > 0 && signature[0] === 0x30) {
126
- // Check if it already has a SIGHASH flag (last byte should be 0x01 for SIGHASH_ALL)
127
- if (signature[signature.length - 1] !== 0x01) {
128
- // Add SIGHASH_ALL flag
129
- return Buffer.concat([signature, Buffer.from([0x01])]);
130
- }
131
- return signature;
132
- }
133
- // For other formats, return as-is
134
- return signature;
135
- }
136
- /**
137
- * Detect if signature is in DER format and convert to compact format (64 bytes)
138
- * by extracting r and s values, removing SIGHASH flag if present
139
- */
140
- ensureCompactSignature(signature) {
141
- // If signature is already 64 bytes, it's likely already compact format
142
- if (signature.length === 64) {
143
- return signature;
144
- }
145
- // Check if it's DER format (starts with 0x30)
146
- if (signature.length > 6 && signature[0] === 0x30) {
147
- let derSig = signature;
148
- // Remove SIGHASH flag if present (last byte is typically 0x01 for SIGHASH_ALL)
149
- if (signature[signature.length - 1] === 0x01) {
150
- derSig = signature.slice(0, -1);
151
- }
152
- // Parse DER format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
153
- if (derSig[0] !== 0x30) {
154
- throw new Error('Invalid DER signature: missing SEQUENCE tag');
155
- }
156
- const totalLength = derSig[1];
157
- if (derSig.length < totalLength + 2) {
158
- throw new Error('Invalid DER signature: length mismatch');
159
- }
160
- let offset = 2;
161
- // Parse R value
162
- if (derSig[offset] !== 0x02) {
163
- throw new Error('Invalid DER signature: missing INTEGER tag for R');
164
- }
165
- offset++;
166
- const rLength = derSig[offset];
167
- offset++;
168
- if (offset + rLength > derSig.length) {
169
- throw new Error('Invalid DER signature: R length exceeds signature length');
170
- }
171
- let rBytes = derSig.slice(offset, offset + rLength);
172
- offset += rLength;
173
- // Parse S value
174
- if (derSig[offset] !== 0x02) {
175
- throw new Error('Invalid DER signature: missing INTEGER tag for S');
176
- }
177
- offset++;
178
- const sLength = derSig[offset];
179
- offset++;
180
- if (offset + sLength > derSig.length) {
181
- throw new Error('Invalid DER signature: S length exceeds signature length');
182
- }
183
- let sBytes = derSig.slice(offset, offset + sLength);
184
- // Remove leading zero padding from r and s (DER may pad to prevent negative interpretation)
185
- while (rBytes.length > 1 && rBytes[0] === 0x00) {
186
- rBytes = rBytes.slice(1);
187
- }
188
- while (sBytes.length > 1 && sBytes[0] === 0x00) {
189
- sBytes = sBytes.slice(1);
190
- }
191
- // Pad to 32 bytes each (compact format requires exactly 32 bytes for r and s)
192
- while (rBytes.length < 32) {
193
- rBytes = Buffer.concat([Buffer.from([0x00]), rBytes]);
194
- }
195
- while (sBytes.length < 32) {
196
- sBytes = Buffer.concat([Buffer.from([0x00]), sBytes]);
197
- }
198
- if (rBytes.length !== 32 || sBytes.length !== 32) {
199
- throw new Error('Invalid signature values: r or s exceeds 32 bytes');
200
- }
201
- // Combine r and s into 64-byte compact format
202
- return Buffer.concat([rBytes, sBytes]);
203
- }
204
- // For other formats, throw error as we can't convert
205
- throw new Error('Unable to convert signature to compact format: unknown format');
206
- }
207
- /**
208
- * Compute contract ID from fund transaction ID, output index, and temporary contract ID
209
- * Matches the Rust implementation in rust-dlc
210
- */
211
- computeContractId(fundTxId, fundOutputIndex, temporaryContractId) {
212
- if (fundTxId.length !== 32) {
213
- throw new Error('Fund transaction ID must be 32 bytes');
214
- }
215
- if (temporaryContractId.length !== 32) {
216
- throw new Error('Temporary contract ID must be 32 bytes');
217
- }
218
- if (fundOutputIndex > 0xffff) {
219
- throw new Error('Fund output index must fit in 16 bits');
220
- }
221
- const result = Buffer.alloc(32);
222
- // XOR fund_tx_id with temporary_id, with byte order reversal for fund_tx_id
223
- for (let i = 0; i < 32; i++) {
224
- result[i] = fundTxId[31 - i] ^ temporaryContractId[i];
225
- }
226
- // XOR the fund output index into the last two bytes
227
- result[30] ^= (fundOutputIndex >> 8) & 0xff; // High byte
228
- result[31] ^= fundOutputIndex & 0xff; // Low byte
229
- return result;
230
- }
231
68
  /**
232
69
  * Create refund signature using PSBT method instead of DDK
233
70
  */
@@ -238,19 +75,7 @@ class BitcoinDdkProvider extends provider_1.default {
238
75
  if (Number(dlcTxs.refundTx.locktime) !== dlcOffer.refundLocktime) {
239
76
  throw new Error(`Refund transaction locktime ${dlcTxs.refundTx.locktime} does not match expected ${dlcOffer.refundLocktime}`);
240
77
  }
241
- // Create the same funding script as createDlcClose
242
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
243
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
244
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
245
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
246
- m: 2,
247
- pubkeys: fundingPubKeys,
248
- network,
249
- });
250
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
251
- redeem: p2ms,
252
- network,
253
- });
78
+ const paymentVariant = (0, Utils_1.createP2WSHMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
254
79
  // Add the funding input with sequence from refund transaction
255
80
  psbt.addInput({
256
81
  hash: dlcTxs.fundTx.txId.serialize(),
@@ -283,7 +108,7 @@ class BitcoinDdkProvider extends provider_1.default {
283
108
  const partialSig = psbt.data.inputs[0].partialSig[0];
284
109
  const derSignature = partialSig.signature;
285
110
  // Convert DER signature to compact format
286
- return this.ensureCompactSignature(derSignature);
111
+ return (0, Utils_1.ensureCompactSignature)(derSignature);
287
112
  }
288
113
  /**
289
114
  * Find private key for DLC funding pubkey by deriving wallet addresses
@@ -632,8 +457,10 @@ class BitcoinDdkProvider extends provider_1.default {
632
457
  const input = await this.fundingInputToInput(fundingInput, false);
633
458
  return input.toUtxo();
634
459
  }));
635
- // Calculate input amounts
636
- const localInputAmount = localRegularInputs.reduce((prev, cur) => prev + cur.amount.GetSatoshiAmount(), 0);
460
+ // Calculate input amounts (including both regular inputs and DLC inputs)
461
+ const localRegularInputAmount = localRegularInputs.reduce((prev, cur) => prev + cur.amount.GetSatoshiAmount(), 0);
462
+ const localDlcInputAmount = localDlcInputs.reduce((prev, cur) => prev + Number(cur.fundAmount), 0);
463
+ const localInputAmount = localRegularInputAmount + localDlcInputAmount;
637
464
  const remoteInputAmount = remoteInputs.reduce((prev, cur) => prev + cur.amount.GetSatoshiAmount(), 0);
638
465
  let payouts = [];
639
466
  let messagesList = [];
@@ -688,7 +515,7 @@ class BitcoinDdkProvider extends provider_1.default {
688
515
  let dlcTxs;
689
516
  if (hasDlcInputs) {
690
517
  // Use spliced DLC transactions when DLC inputs are present
691
- dlcTxs = await this._ddk.createSplicedDlcTransactions(outcomes, localParams, remoteParams, dlcOffer.refundLocktime, BigInt(dlcOffer.feeRatePerVb), 0, dlcOffer.cetLocktime, dlcOffer.fundOutputSerialId);
518
+ dlcTxs = await this._ddk.createSplicedDlcTransactions(outcomes, localParams, remoteParams, dlcOffer.refundLocktime, BigInt(dlcOffer.feeRatePerVb), 0, dlcOffer.cetLocktime, BigInt(dlcOffer.fundOutputSerialId));
692
519
  }
693
520
  else {
694
521
  // Use regular DLC transactions when no DLC inputs
@@ -860,16 +687,8 @@ class BitcoinDdkProvider extends provider_1.default {
860
687
  async CreateCetAdaptorAndRefundSigs(dlcOffer, dlcAccept, dlcTxs, messagesList, isOfferer) {
861
688
  const network = await this.getConnectedNetwork();
862
689
  const cetsHex = dlcTxs.cets.map((cet) => cet.serialize().toString('hex'));
863
- // Create the correct P2WSH multisig funding script
864
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
865
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
866
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
867
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
868
- m: 2,
869
- pubkeys: fundingPubKeys,
870
- network,
871
- });
872
690
  // We need the redeem script (multisig), not the P2WSH output
691
+ const p2ms = (0, Utils_1.createP2MSMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
873
692
  const fundingSPK = p2ms.output;
874
693
  // For finding the private key, we still need the individual P2WPKH address
875
694
  const individualFundingSPK = bitcoin_1.Script.p2wpkhLock((0, crypto_1.hash160)(isOfferer ? dlcOffer.fundingPubkey : dlcAccept.fundingPubkey))
@@ -980,14 +799,7 @@ class BitcoinDdkProvider extends provider_1.default {
980
799
  });
981
800
  // Create the correct P2WSH multisig funding script for verification
982
801
  const network = await this.getConnectedNetwork();
983
- const verifyFundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
984
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
985
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
986
- const verifyP2ms = bitcoinjs_lib_1.payments.p2ms({
987
- m: 2,
988
- pubkeys: verifyFundingPubKeys,
989
- network,
990
- });
802
+ const verifyP2ms = (0, Utils_1.createP2MSMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
991
803
  // We need the redeem script (multisig), not the P2WSH output
992
804
  const fundingSPK = verifyP2ms.output;
993
805
  const ddkOracleInfo = {
@@ -1070,17 +882,14 @@ class BitcoinDdkProvider extends provider_1.default {
1070
882
  }
1071
883
  }
1072
884
  }
1073
- async CreateFundingSigsAlt(dlcOffer, dlcAccept, dlcTxs, isOfferer) {
885
+ async CreateFundingSigs(dlcOffer, dlcAccept, dlcTxs, isOfferer) {
1074
886
  const transaction = bitcoinjs_lib_1.Transaction.fromBuffer(Buffer.from(dlcTxs.fundTx.serialize()));
1075
887
  const network = await this.getConnectedNetwork();
1076
888
  const psbt = new bitcoinjs_lib_1.Psbt({ network });
1077
- // Combine all funding inputs from both parties
1078
- const allFundingInputs = [
889
+ const allFundingInputs = (0, Utils_1.sortFundingInputsBySerialId)([
1079
890
  ...dlcOffer.fundingInputs,
1080
891
  ...dlcAccept.fundingInputs,
1081
- ];
1082
- // Sort by inputSerialId to reconstruct proper transaction order
1083
- allFundingInputs.sort((a, b) => Number(a.inputSerialId - b.inputSerialId));
892
+ ]);
1084
893
  // Add all inputs to PSBT with proper witnessUtxo
1085
894
  for (const fundingInput of allFundingInputs) {
1086
895
  const prevOut = fundingInput.prevTx.outputs[fundingInput.prevTxVout];
@@ -1137,9 +946,26 @@ class BitcoinDdkProvider extends provider_1.default {
1137
946
  const keyPair = ECPair.fromPrivateKey(Buffer.from(privKey, 'hex'));
1138
947
  // Check if this is a DLC input (2-of-2 multisig from previous DLC)
1139
948
  if (fundingInput.dlcInput) {
1140
- // For DLC inputs, we'll need to handle 2-of-2 multisig signing differently
1141
- // For now, throw an error as this requires implementing multisig support
1142
- throw new Error('DLC input signing not yet implemented in CreateFundingSigsAlt');
949
+ const paymentVariant = (0, Utils_1.createP2WSHMultisig)(fundingInput.dlcInput.localFundPubkey, fundingInput.dlcInput.remoteFundPubkey, network);
950
+ const redeemScript = paymentVariant.redeem.output;
951
+ // Find the correct private key for this DLC input
952
+ const dlcPrivKey = await this.findDlcFundingPrivateKey(fundingInput.dlcInput.localFundPubkey.toString('hex'), fundingInput.dlcInput.remoteFundPubkey.toString('hex'));
953
+ const keyPair = ECPair.fromPrivateKey(Buffer.from(dlcPrivKey, 'hex'));
954
+ // Update the input with the witnessScript before signing
955
+ psbt.updateInput(inputIndex, {
956
+ witnessScript: redeemScript,
957
+ });
958
+ psbt.signInput(inputIndex, keyPair);
959
+ const inputData = psbt.data.inputs[inputIndex];
960
+ const partialSigs = inputData.partialSig;
961
+ if (!partialSigs || partialSigs.length === 0) {
962
+ throw new Error(`No signatures found for input ${inputIndex} after signing`);
963
+ }
964
+ const sigWitness = new messaging_1.ScriptWitnessV0();
965
+ sigWitness.witness = (0, Utils_1.ensureBuffer)(partialSigs[0].signature);
966
+ const pubKeyWitness = new messaging_1.ScriptWitnessV0();
967
+ pubKeyWitness.witness = keyPair.publicKey;
968
+ witnessElements.push([sigWitness, pubKeyWitness]);
1143
969
  }
1144
970
  else {
1145
971
  // For P2WPKH inputs, use PSBT signing
@@ -1152,7 +978,7 @@ class BitcoinDdkProvider extends provider_1.default {
1152
978
  }
1153
979
  // For P2WPKH, create witness manually: [signature, publicKey]
1154
980
  const sigWitness = new messaging_1.ScriptWitnessV0();
1155
- sigWitness.witness = this.ensureBuffer(partialSigs[0].signature);
981
+ sigWitness.witness = (0, Utils_1.ensureBuffer)(partialSigs[0].signature);
1156
982
  const pubKeyWitness = new messaging_1.ScriptWitnessV0();
1157
983
  pubKeyWitness.witness = keyPair.publicKey;
1158
984
  witnessElements.push([sigWitness, pubKeyWitness]);
@@ -1164,7 +990,7 @@ class BitcoinDdkProvider extends provider_1.default {
1164
990
  fundingSignatures.witnessElements = witnessElements;
1165
991
  return fundingSignatures;
1166
992
  }
1167
- async VerifyFundingSigsAlt(dlcOffer, dlcAccept, dlcSign, dlcTxs, isOfferer) {
993
+ async VerifyFundingSigs(dlcOffer, dlcAccept, dlcSign, dlcTxs, isOfferer) {
1168
994
  const network = await this.getConnectedNetwork();
1169
995
  const transaction = bitcoinjs_lib_1.Transaction.fromBuffer(Buffer.from(dlcTxs.fundTx.serialize()));
1170
996
  // Get the party whose signatures we're verifying
@@ -1173,12 +999,10 @@ class BitcoinDdkProvider extends provider_1.default {
1173
999
  const signingPartyInputs = isOfferer
1174
1000
  ? dlcAccept.fundingInputs
1175
1001
  : dlcOffer.fundingInputs;
1176
- // Combine all inputs to get the correct ordering
1177
- const allFundingInputs = [
1002
+ const allFundingInputs = (0, Utils_1.sortFundingInputsBySerialId)([
1178
1003
  ...dlcOffer.fundingInputs,
1179
1004
  ...dlcAccept.fundingInputs,
1180
- ];
1181
- allFundingInputs.sort((a, b) => Number(a.inputSerialId - b.inputSerialId));
1005
+ ]);
1182
1006
  // Compare transaction IDs
1183
1007
  const dlcFundTxId = dlcTxs.fundTx.txId.toString();
1184
1008
  const psbtBuilderTxId = transaction.getId();
@@ -1218,8 +1042,40 @@ class BitcoinDdkProvider extends provider_1.default {
1218
1042
  // Add the funding signatures to the PSBT as partial signatures
1219
1043
  let witnessIndex = 0;
1220
1044
  for (const fundingInput of signingPartyInputs) {
1221
- // Skip DLC inputs for now (same as CreateFundingSigsAlt)
1045
+ // Handle DLC inputs with multisig verification
1222
1046
  if (fundingInput.dlcInput) {
1047
+ const dlcInput = fundingInput.dlcInput;
1048
+ const paymentVariant = (0, Utils_1.createP2WSHMultisig)(dlcInput.localFundPubkey, dlcInput.remoteFundPubkey, network);
1049
+ // Find this input's index in the sorted transaction
1050
+ const inputIndex = allFundingInputs.findIndex((input) => input.prevTx.txId.toString() ===
1051
+ fundingInput.prevTx.txId.toString() &&
1052
+ input.prevTxVout === fundingInput.prevTxVout);
1053
+ if (inputIndex === -1) {
1054
+ throw new Error(`DLC input not found in transaction: ${fundingInput.prevTx.txId.toString()}:${fundingInput.prevTxVout}`);
1055
+ }
1056
+ // Update the input with the witnessScript for verification
1057
+ psbt.updateInput(inputIndex, {
1058
+ witnessScript: paymentVariant.redeem.output,
1059
+ });
1060
+ // Add the DLC signature from witness elements if available
1061
+ if (witnessIndex < dlcSign.fundingSignatures.witnessElements.length) {
1062
+ const witnessElement = dlcSign.fundingSignatures.witnessElements[witnessIndex];
1063
+ const signature = witnessElement[0].witness;
1064
+ const publicKey = witnessElement[1].witness;
1065
+ psbt.updateInput(inputIndex, {
1066
+ partialSig: [
1067
+ {
1068
+ pubkey: publicKey,
1069
+ signature: (0, Utils_1.ensureDerSignature)(signature),
1070
+ },
1071
+ ],
1072
+ });
1073
+ // Verify the signature for this DLC input
1074
+ psbt.validateSignaturesOfInput(inputIndex, (pubkey, msghash, signature) => {
1075
+ return ecc.verify(msghash, pubkey, signature);
1076
+ });
1077
+ witnessIndex++;
1078
+ }
1223
1079
  continue;
1224
1080
  }
1225
1081
  if (witnessIndex >= dlcSign.fundingSignatures.witnessElements.length) {
@@ -1239,7 +1095,7 @@ class BitcoinDdkProvider extends provider_1.default {
1239
1095
  partialSig: [
1240
1096
  {
1241
1097
  pubkey: publicKey,
1242
- signature: this.ensureDerSignature(signature),
1098
+ signature: (0, Utils_1.ensureDerSignature)(signature),
1243
1099
  },
1244
1100
  ],
1245
1101
  });
@@ -1281,12 +1137,10 @@ class BitcoinDdkProvider extends provider_1.default {
1281
1137
  */
1282
1138
  async getFundingTransactionSighashDetails(dlcOffer, dlcAccept, dlcTxs) {
1283
1139
  const transaction = bitcoinjs_lib_1.Transaction.fromBuffer(Buffer.from(dlcTxs.fundTx.serialize()));
1284
- // Combine all inputs to get the correct ordering
1285
- const allFundingInputs = [
1140
+ const allFundingInputs = (0, Utils_1.sortFundingInputsBySerialId)([
1286
1141
  ...dlcOffer.fundingInputs,
1287
1142
  ...dlcAccept.fundingInputs,
1288
- ];
1289
- allFundingInputs.sort((a, b) => Number(a.inputSerialId - b.inputSerialId));
1143
+ ]);
1290
1144
  // Calculate detailed sighash information
1291
1145
  const details = [];
1292
1146
  for (let i = 0; i < allFundingInputs.length; i++) {
@@ -1321,7 +1175,7 @@ class BitcoinDdkProvider extends provider_1.default {
1321
1175
  ? dlcAccept.refundSignature
1322
1176
  : dlcSign.refundSignature;
1323
1177
  // Ensure signature is in DER format (convert from compact if needed)
1324
- const refundSignature = this.ensureDerSignature(rawRefundSignature);
1178
+ const refundSignature = (0, Utils_1.ensureDerSignature)(rawRefundSignature);
1325
1179
  const signingPubkey = isOfferer
1326
1180
  ? dlcAccept.fundingPubkey
1327
1181
  : dlcOffer.fundingPubkey;
@@ -1331,19 +1185,7 @@ class BitcoinDdkProvider extends provider_1.default {
1331
1185
  }
1332
1186
  // Create a PSBT for the refund transaction verification using same approach as createDlcClose
1333
1187
  const psbt = new bitcoinjs_lib_1.Psbt({ network });
1334
- // Create the same funding script as createDlcClose
1335
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
1336
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
1337
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
1338
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
1339
- m: 2,
1340
- pubkeys: fundingPubKeys,
1341
- network,
1342
- });
1343
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
1344
- redeem: p2ms,
1345
- network,
1346
- });
1188
+ const paymentVariant = (0, Utils_1.createP2WSHMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
1347
1189
  // Add the funding input with sequence from refund transaction
1348
1190
  psbt.addInput({
1349
1191
  hash: dlcTxs.fundTx.txId.serialize(),
@@ -1380,55 +1222,32 @@ class BitcoinDdkProvider extends provider_1.default {
1380
1222
  }
1381
1223
  CreateFundingScript(dlcOffer, dlcAccept) {
1382
1224
  const network = this.getBitcoinJsNetwork();
1383
- // Sort funding pubkeys in lexicographical order as per DLC spec
1384
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
1385
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
1386
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
1387
- // Create 2-of-2 multisig script
1388
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
1389
- m: 2,
1390
- pubkeys: fundingPubKeys,
1391
- network,
1392
- });
1393
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
1394
- redeem: p2ms,
1395
- network,
1396
- });
1225
+ const paymentVariant = (0, Utils_1.createP2WSHMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
1397
1226
  return paymentVariant.redeem.output;
1398
1227
  }
1399
1228
  async CreateFundingTx(dlcOffer, dlcAccept, dlcSign, dlcTxs, fundingSignatures) {
1400
1229
  const network = await this.getConnectedNetwork();
1401
1230
  const psbt = new bitcoinjs_lib_1.Psbt({ network });
1402
- // Combine and sort all funding inputs by serial ID (same as CreateFundingSigsAlt)
1403
- const allFundingInputs = [
1231
+ const allFundingInputs = (0, Utils_1.sortFundingInputsBySerialId)([
1404
1232
  ...dlcOffer.fundingInputs,
1405
1233
  ...dlcAccept.fundingInputs,
1406
- ];
1407
- allFundingInputs.sort((a, b) => Number(a.inputSerialId - b.inputSerialId));
1234
+ ]);
1408
1235
  // Create a map of input txid:vout to witness elements
1409
1236
  const witnessMap = new Map();
1410
- // Map witness elements correctly - CreateFundingSigsAlt only creates witness elements
1237
+ // Map witness elements correctly - CreateFundingSigs only creates witness elements
1411
1238
  // for the party's own inputs, so we need to map them correctly
1412
- // For dlcSign (offerer's signatures), map to offerer's inputs only
1239
+ // For dlcSign (offerer's signatures), map to offerer's inputs (including DLC inputs)
1413
1240
  let offererWitnessIndex = 0;
1414
1241
  dlcOffer.fundingInputs.forEach((fundingInput) => {
1415
- // Skip DLC inputs for now as in CreateFundingSigsAlt
1416
- if (fundingInput.dlcInput) {
1417
- return;
1418
- }
1419
1242
  if (offererWitnessIndex < dlcSign.fundingSignatures.witnessElements.length) {
1420
1243
  const key = `${fundingInput.prevTx.txId.toString()}:${fundingInput.prevTxVout}`;
1421
1244
  witnessMap.set(key, dlcSign.fundingSignatures.witnessElements[offererWitnessIndex]);
1422
1245
  offererWitnessIndex++;
1423
1246
  }
1424
1247
  });
1425
- // For fundingSignatures (accepter's signatures), map to accepter's inputs only
1248
+ // For fundingSignatures (accepter's signatures), map to accepter's inputs (including DLC inputs)
1426
1249
  let accepterWitnessIndex = 0;
1427
1250
  dlcAccept.fundingInputs.forEach((fundingInput) => {
1428
- // Skip DLC inputs for now as in CreateFundingSigsAlt
1429
- if (fundingInput.dlcInput) {
1430
- return;
1431
- }
1432
1251
  if (accepterWitnessIndex < fundingSignatures.witnessElements.length) {
1433
1252
  const key = `${fundingInput.prevTx.txId.toString()}:${fundingInput.prevTxVout}`;
1434
1253
  witnessMap.set(key, fundingSignatures.witnessElements[accepterWitnessIndex]);
@@ -1439,12 +1258,12 @@ class BitcoinDdkProvider extends provider_1.default {
1439
1258
  const originalTransaction = bitcoinjs_lib_1.Transaction.fromBuffer(Buffer.from(dlcTxs.fundTx.serialize()));
1440
1259
  for (const fundingInput of allFundingInputs) {
1441
1260
  const prevOut = fundingInput.prevTx.outputs[fundingInput.prevTxVout];
1442
- // Use same script handling as CreateFundingSigsAlt
1261
+ // Use same script handling as CreateFundingSigs
1443
1262
  const witnessUtxo = {
1444
1263
  script: Buffer.from(prevOut.scriptPubKey.serialize().subarray(1)),
1445
1264
  value: Number(prevOut.value.sats),
1446
1265
  };
1447
- // Use sequence from the original transaction (same as CreateFundingSigsAlt)
1266
+ // Use sequence from the original transaction (same as CreateFundingSigs)
1448
1267
  const originalInput = originalTransaction.ins.find((input) => input.hash.reverse().toString('hex') ===
1449
1268
  fundingInput.prevTx.txId.toString() &&
1450
1269
  input.index === fundingInput.prevTxVout);
@@ -1471,12 +1290,57 @@ class BitcoinDdkProvider extends provider_1.default {
1471
1290
  const inputKey = `${fundingInput.prevTx.txId.toString()}:${fundingInput.prevTxVout}`;
1472
1291
  const witnessElements = witnessMap.get(inputKey);
1473
1292
  if (witnessElements && witnessElements.length === 2) {
1474
- // Skip DLC inputs for now as requested
1293
+ // Handle DLC inputs with multisig finalization
1475
1294
  if (fundingInput.dlcInput) {
1295
+ const dlcInput = fundingInput.dlcInput;
1296
+ // Create the multisig script for finalization
1297
+ // Use lexicographic ordering to match DDK library behavior
1298
+ const orderedPubkeys = (0, Utils_1.orderPubkeysLexicographically)(dlcInput.localFundPubkey, dlcInput.remoteFundPubkey);
1299
+ const paymentVariant = (0, Utils_1.createP2WSHMultisigFromOrdered)(orderedPubkeys, network);
1300
+ // Update the input with the witnessScript
1301
+ psbt.updateInput(inputIndex, {
1302
+ witnessScript: paymentVariant.redeem.output,
1303
+ });
1304
+ // Sign this DLC input ourselves
1305
+ const dlcPrivKey = await this.findDlcFundingPrivateKey(dlcInput.localFundPubkey.toString('hex'), dlcInput.remoteFundPubkey.toString('hex'));
1306
+ const keyPair = ECPair.fromPrivateKey(Buffer.from(dlcPrivKey, 'hex'));
1307
+ psbt.signInput(inputIndex, keyPair);
1308
+ // Get our signature and determine which pubkey it corresponds to
1309
+ const ourPartialSig = psbt.data.inputs[inputIndex].partialSig[0];
1310
+ const ourSig = (0, Utils_1.ensureDerSignature)(ourPartialSig.signature);
1311
+ const ourPubkey = ourPartialSig.pubkey;
1312
+ // Get the other party's signature from witnessElements
1313
+ const otherPartySig = (0, Utils_1.ensureDerSignature)(witnessElements[0].witness);
1314
+ // Order signatures according to the lexicographic pubkey order used in the multisig script
1315
+ let sig1, sig2;
1316
+ if (Buffer.compare(ourPubkey, orderedPubkeys[0]) === 0) {
1317
+ // Our signature corresponds to the first pubkey in lexicographic order
1318
+ sig1 = ourSig;
1319
+ sig2 = otherPartySig;
1320
+ }
1321
+ else {
1322
+ // Our signature corresponds to the second pubkey in lexicographic order
1323
+ sig1 = otherPartySig;
1324
+ sig2 = ourSig;
1325
+ }
1326
+ // Finalize with proper multisig witness: [OP_0, sig1, sig2, redeemScript]
1327
+ psbt.finalizeInput(inputIndex, () => ({
1328
+ finalScriptSig: Buffer.alloc(0),
1329
+ finalScriptWitness: Buffer.concat([
1330
+ Buffer.from([0x04]), // witness stack count
1331
+ Buffer.from([0x00]), // OP_0 (Bitcoin multisig bug)
1332
+ Buffer.from([sig1.length]),
1333
+ sig1,
1334
+ Buffer.from([sig2.length]),
1335
+ sig2,
1336
+ Buffer.from([paymentVariant.redeem.output.length]),
1337
+ paymentVariant.redeem.output,
1338
+ ]),
1339
+ }));
1476
1340
  continue;
1477
1341
  }
1478
1342
  // For P2WPKH inputs, finalize the input with witness data
1479
- const signature = this.ensureDerSignature(witnessElements[0].witness);
1343
+ const signature = (0, Utils_1.ensureDerSignature)(witnessElements[0].witness);
1480
1344
  const publicKey = witnessElements[1].witness;
1481
1345
  // Try a simpler approach - let bitcoinjs-lib handle witness construction
1482
1346
  psbt.finalizeInput(inputIndex, () => ({
@@ -1537,8 +1401,7 @@ Payout Group found but incorrect group index');
1537
1401
  Payout Group not found');
1538
1402
  return { index: payoutIndexOffset + index, groupLength };
1539
1403
  }
1540
- async FindOutcomeIndexFromHyperbolaPayoutCurvePiece(_dlcOffer, contractDescriptor, contractOraclePairIndex, hyperbolaPayoutCurvePiece, oracleAttestation, outcome) {
1541
- const { dlcOffer } = (0, Utils_1.checkTypes)({ _dlcOffer });
1404
+ async FindOutcomeIndexFromHyperbolaPayoutCurvePiece(dlcOffer, contractDescriptor, contractOraclePairIndex, hyperbolaPayoutCurvePiece, oracleAttestation, outcome) {
1542
1405
  const hyperbolaCurve = core_2.HyperbolaPayoutCurve.fromPayoutCurvePiece(hyperbolaPayoutCurvePiece);
1543
1406
  const clampBN = (val) => bignumber_js_1.default.max(0, bignumber_js_1.default.min(val, dlcOffer.contractInfo.totalCollateral.toString()));
1544
1407
  const payout = clampBN(hyperbolaCurve.getPayout(outcome));
@@ -1836,18 +1699,8 @@ Payout Group not found even with brute force search');
1836
1699
  _dlcTxs,
1837
1700
  });
1838
1701
  const network = await this.getConnectedNetwork();
1839
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
1840
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
1841
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
1842
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
1843
- m: 2,
1844
- pubkeys: fundingPubKeys,
1845
- network,
1846
- });
1847
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
1848
- redeem: p2ms,
1849
- network,
1850
- });
1702
+ const fundingPubKeys = (0, Utils_1.orderPubkeysLexicographically)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey);
1703
+ const paymentVariant = (0, Utils_1.createP2WSHMultisigFromOrdered)(fundingPubKeys, network);
1851
1704
  const sigHashRequestPromises = [];
1852
1705
  const sigHashes = [];
1853
1706
  for (let i = 0; i < rawCloseTxs.length; i++) {
@@ -1906,18 +1759,8 @@ Payout Group not found even with brute force search');
1906
1759
  });
1907
1760
  const dlcCloses = _dlcCloses.map((_dlcClose) => (0, Utils_1.checkTypes)({ _dlcClose }).dlcClose);
1908
1761
  const network = await this.getConnectedNetwork();
1909
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
1910
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
1911
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
1912
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
1913
- m: 2,
1914
- pubkeys: fundingPubKeys,
1915
- network,
1916
- });
1917
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
1918
- redeem: p2ms,
1919
- network,
1920
- });
1762
+ const fundingPubKeys = (0, Utils_1.orderPubkeysLexicographically)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey);
1763
+ const paymentVariant = (0, Utils_1.createP2WSHMultisigFromOrdered)(fundingPubKeys, network);
1921
1764
  const pubkey = isOfferer ? dlcAccept.fundingPubkey : dlcOffer.fundingPubkey;
1922
1765
  const sigsValidity = [];
1923
1766
  for (let i = 0; i < rawCloseTxs.length; i++) {
@@ -2028,8 +1871,7 @@ Payout Group not found even with brute force search');
2028
1871
  changeSerialId = initResult.changeSerialId;
2029
1872
  }
2030
1873
  _fundingInputs.forEach((input) => (0, assert_1.default)(input.type === messaging_1.MessageType.FundingInput, 'FundingInput must be V0'));
2031
- const fundingInputs = _fundingInputs.map((input) => input);
2032
- fundingInputs.sort((a, b) => Number(a.inputSerialId) - Number(b.inputSerialId));
1874
+ const fundingInputs = (0, Utils_1.sortFundingInputsBySerialId)(_fundingInputs);
2033
1875
  const fundOutputSerialId = (0, Utils_1.generateSerialId)();
2034
1876
  (0, assert_1.default)(changeSerialId !== fundOutputSerialId, 'changeSerialId cannot equal the fundOutputSerialId');
2035
1877
  dlcOffer.contractFlags = Buffer.from('00', 'hex');
@@ -2143,8 +1985,7 @@ Payout Group not found even with brute force search');
2143
1985
  changeSerialId = initResult.changeSerialId;
2144
1986
  const _fundingInputs = initResult.fundingInputs;
2145
1987
  _fundingInputs.forEach((input) => (0, assert_1.default)(input.type === messaging_1.MessageType.FundingInput, 'FundingInput must be V0'));
2146
- fundingInputs = _fundingInputs.map((input) => input);
2147
- fundingInputs.sort((a, b) => Number(a.inputSerialId) - Number(b.inputSerialId));
1988
+ fundingInputs = (0, Utils_1.sortFundingInputsBySerialId)(_fundingInputs.map((input) => input));
2148
1989
  }
2149
1990
  (0, assert_1.default)(Buffer.compare(dlcOffer.fundingPubkey, fundingPubKey) !== 0, 'DlcOffer and DlcAccept FundingPubKey cannot be the same');
2150
1991
  const dlcAccept = new messaging_1.DlcAccept();
@@ -2182,7 +2023,7 @@ Payout Group not found even with brute force search');
2182
2023
  const { dlcTransactions, messagesList } = await this.createDlcTxs(dlcOffer, dlcAccept);
2183
2024
  const { cetSignatures, refundSignature } = await this.CreateCetAdaptorAndRefundSigs(dlcOffer, dlcAccept, dlcTransactions, messagesList, false);
2184
2025
  const _dlcTransactions = dlcTransactions;
2185
- const contractId = this.computeContractId(_dlcTransactions.fundTx.txId.serialize(), _dlcTransactions.fundTxVout, dlcOffer.temporaryContractId);
2026
+ const contractId = (0, Utils_1.computeContractId)(_dlcTransactions.fundTx.txId.serialize(), _dlcTransactions.fundTxVout, dlcOffer.temporaryContractId);
2186
2027
  _dlcTransactions.contractId = contractId;
2187
2028
  dlcAccept.cetAdaptorSignatures = cetSignatures;
2188
2029
  dlcAccept.refundSignature = refundSignature;
@@ -2202,10 +2043,9 @@ Payout Group not found even with brute force search');
2202
2043
  const { dlcTransactions, messagesList } = await this.createDlcTxs(dlcOffer, dlcAccept);
2203
2044
  await this.VerifyCetAdaptorAndRefundSigs(dlcOffer, dlcAccept, dlcSign, dlcTransactions, messagesList, true);
2204
2045
  const { cetSignatures, refundSignature } = await this.CreateCetAdaptorAndRefundSigs(dlcOffer, dlcAccept, dlcTransactions, messagesList, true);
2205
- const fundingSignatures = await this.CreateFundingSigsAlt(dlcOffer, dlcAccept, dlcTransactions, true);
2046
+ const fundingSignatures = await this.CreateFundingSigs(dlcOffer, dlcAccept, dlcTransactions, true);
2206
2047
  const dlcTxs = dlcTransactions;
2207
- const contractId = this.computeContractId(dlcTxs.fundTx.txId.serialize(), dlcTxs.fundTxVout, dlcOffer.temporaryContractId);
2208
- (0, assert_1.default)(Buffer.compare(contractId, this.computeContractId(dlcTxs.fundTx.txId.serialize(), dlcTxs.fundTxVout, dlcOffer.temporaryContractId)) === 0, 'contractId must be the xor of funding txid, fundingOutputIndex and the tempContractId');
2048
+ const contractId = (0, Utils_1.computeContractId)(dlcTxs.fundTx.txId.serialize(), dlcTxs.fundTxVout, dlcOffer.temporaryContractId);
2209
2049
  dlcTxs.contractId = contractId;
2210
2050
  dlcSign.contractId = contractId;
2211
2051
  dlcSign.cetAdaptorSignatures = cetSignatures;
@@ -2223,6 +2063,8 @@ Payout Group not found even with brute force search');
2223
2063
  */
2224
2064
  async finalizeDlcSign(dlcOffer, dlcAccept, dlcSign, dlcTxs) {
2225
2065
  let messagesList = [];
2066
+ const contractId = (0, Utils_1.computeContractId)(dlcTxs.fundTx.txId.serialize(), dlcTxs.fundTxVout, dlcOffer.temporaryContractId);
2067
+ (0, assert_1.default)(Buffer.compare(contractId, dlcSign.contractId) === 0, `Finalize Dlc Sign Contract ID mismatch: ${contractId.toString('hex')} !== ${dlcSign.contractId.toString('hex')}`);
2226
2068
  if (dlcOffer.contractInfo.type === messaging_1.MessageType.SingleContractInfo &&
2227
2069
  dlcOffer.contractInfo.contractDescriptor.type ===
2228
2070
  messaging_1.MessageType.SingleContractInfo) {
@@ -2237,8 +2079,8 @@ Payout Group not found even with brute force search');
2237
2079
  messagesList = oracleEventMessagesList;
2238
2080
  }
2239
2081
  await this.VerifyCetAdaptorAndRefundSigs(dlcOffer, dlcAccept, dlcSign, dlcTxs, messagesList, false);
2240
- await this.VerifyFundingSigsAlt(dlcOffer, dlcAccept, dlcSign, dlcTxs, false);
2241
- const fundingSignatures = await this.CreateFundingSigsAlt(dlcOffer, dlcAccept, dlcTxs, false);
2082
+ await this.VerifyFundingSigs(dlcOffer, dlcAccept, dlcSign, dlcTxs, false);
2083
+ const fundingSignatures = await this.CreateFundingSigs(dlcOffer, dlcAccept, dlcTxs, false);
2242
2084
  const fundTx = await this.CreateFundingTx(dlcOffer, dlcAccept, dlcSign, dlcTxs, fundingSignatures);
2243
2085
  return fundTx;
2244
2086
  }
@@ -2273,19 +2115,8 @@ Payout Group not found even with brute force search');
2273
2115
  if (Number(dlcTxs.refundTx.locktime) !== dlcOffer.refundLocktime) {
2274
2116
  throw new Error(`Refund transaction locktime ${dlcTxs.refundTx.locktime} does not match expected ${dlcOffer.refundLocktime}`);
2275
2117
  }
2276
- // Create the same funding script as createDlcClose
2277
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
2278
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
2279
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
2280
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
2281
- m: 2,
2282
- pubkeys: fundingPubKeys,
2283
- network,
2284
- });
2285
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
2286
- redeem: p2ms,
2287
- network,
2288
- });
2118
+ const fundingPubKeys = (0, Utils_1.orderPubkeysLexicographically)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey);
2119
+ const paymentVariant = (0, Utils_1.createP2WSHMultisigFromOrdered)(fundingPubKeys, network);
2289
2120
  // Add the funding input with sequence from refund transaction
2290
2121
  psbt.addInput({
2291
2122
  hash: dlcTxs.fundTx.txId.serialize(),
@@ -2315,14 +2146,14 @@ Payout Group not found even with brute force search');
2315
2146
  // This is the offerer's pubkey, use dlcSign.refundSignature
2316
2147
  partialSigs.push({
2317
2148
  pubkey: pubkey,
2318
- signature: this.ensureDerSignature(dlcSign.refundSignature),
2149
+ signature: (0, Utils_1.ensureDerSignature)(dlcSign.refundSignature),
2319
2150
  });
2320
2151
  }
2321
2152
  else if (Buffer.compare(pubkey, dlcAccept.fundingPubkey) === 0) {
2322
2153
  // This is the accepter's pubkey, use dlcAccept.refundSignature
2323
2154
  partialSigs.push({
2324
2155
  pubkey: pubkey,
2325
- signature: this.ensureDerSignature(dlcAccept.refundSignature),
2156
+ signature: (0, Utils_1.ensureDerSignature)(dlcAccept.refundSignature),
2326
2157
  });
2327
2158
  }
2328
2159
  }
@@ -2372,18 +2203,7 @@ Payout Group not found even with brute force search');
2372
2203
  isOfferer = await this.isOfferer(dlcOffer, dlcAccept);
2373
2204
  const network = await this.getConnectedNetwork();
2374
2205
  const psbt = new bitcoinjs_lib_1.Psbt({ network });
2375
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
2376
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
2377
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
2378
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
2379
- m: 2,
2380
- pubkeys: fundingPubKeys,
2381
- network,
2382
- });
2383
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
2384
- redeem: p2ms,
2385
- network,
2386
- });
2206
+ const paymentVariant = (0, Utils_1.createP2WSHMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
2387
2207
  // Initiate and build PSBT
2388
2208
  let inputs = _inputs;
2389
2209
  if (!_inputs) {
@@ -2483,7 +2303,7 @@ Payout Group not found even with brute force search');
2483
2303
  return ecc.verify(msghash, pubkey, signature);
2484
2304
  });
2485
2305
  // Extract close signature from psbt and decode it to only extract r and s values
2486
- const closeSignature = await bitcoinjs_lib_1.script.signature.decode(this.ensureBuffer(psbt.data.inputs[fundingInputIndex].partialSig[0].signature)).signature;
2306
+ const closeSignature = await bitcoinjs_lib_1.script.signature.decode((0, Utils_1.ensureBuffer)(psbt.data.inputs[fundingInputIndex].partialSig[0].signature)).signature;
2487
2307
  // Extract funding signatures from psbt
2488
2308
  const inputSigs = psbt.data.inputs
2489
2309
  .filter((input) => input !== fundingInputIndex)
@@ -2492,9 +2312,9 @@ Payout Group not found even with brute force search');
2492
2312
  const witnessElements = [];
2493
2313
  for (let i = 0; i < inputSigs.length; i++) {
2494
2314
  const sigWitness = new messaging_1.ScriptWitnessV0();
2495
- sigWitness.witness = this.ensureBuffer(inputSigs[i].signature);
2315
+ sigWitness.witness = (0, Utils_1.ensureBuffer)(inputSigs[i].signature);
2496
2316
  const pubKeyWitness = new messaging_1.ScriptWitnessV0();
2497
- pubKeyWitness.witness = this.ensureBuffer(inputSigs[i].pubkey);
2317
+ pubKeyWitness.witness = (0, Utils_1.ensureBuffer)(inputSigs[i].pubkey);
2498
2318
  witnessElements.push([sigWitness, pubKeyWitness]);
2499
2319
  }
2500
2320
  const fundingSignatures = new messaging_1.FundingSignatures();
@@ -2624,18 +2444,7 @@ Payout Group not found even with brute force search');
2624
2444
  dlcClose.validate();
2625
2445
  const network = await this.getConnectedNetwork();
2626
2446
  const psbt = new bitcoinjs_lib_1.Psbt({ network });
2627
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
2628
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
2629
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
2630
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
2631
- m: 2,
2632
- pubkeys: fundingPubKeys,
2633
- network,
2634
- });
2635
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
2636
- redeem: p2ms,
2637
- network,
2638
- });
2447
+ const paymentVariant = (0, Utils_1.createP2WSHMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
2639
2448
  // Make temporary array to hold all inputs and then sort them
2640
2449
  // this method can be improved later
2641
2450
  const psbtInputs = [];
@@ -2862,15 +2671,7 @@ Payout Group not found even with brute force search');
2862
2671
  : [remotePubkey, localPubkey];
2863
2672
  const network = await this.getConnectedNetwork();
2864
2673
  // Create 2-of-2 multisig payment using deterministic ordering
2865
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
2866
- m: 2,
2867
- pubkeys: orderedPubkeys,
2868
- network,
2869
- });
2870
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
2871
- redeem: p2ms,
2872
- network,
2873
- });
2674
+ const paymentVariant = (0, Utils_1.createP2WSHMultisigFromOrdered)(orderedPubkeys, network);
2874
2675
  const multisigAddress = paymentVariant.address;
2875
2676
  // Verify this matches the actual funding output address
2876
2677
  const actualFundingOutput = tx.outputs[dlcInputInfo.fundVout];