@atomicfinance/bitcoin-dlc-provider 3.3.1 → 3.4.1

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.
@@ -6,8 +6,11 @@ import {
6
6
  AddSignaturesToRefundTxResponse,
7
7
  AddSignatureToFundTransactionRequest,
8
8
  AddSignatureToFundTransactionResponse,
9
- bitcoin,
10
9
  CalculateEcSignatureRequest,
10
+ CreateBatchDlcTransactionsRequest,
11
+ CreateBatchDlcTransactionsResponse,
12
+ CreateBatchFundTransactionRequest,
13
+ CreateBatchFundTransactionResponse,
11
14
  CreateCetAdaptorSignatureRequest,
12
15
  CreateCetAdaptorSignatureResponse,
13
16
  CreateCetAdaptorSignaturesRequest,
@@ -104,11 +107,10 @@ import {
104
107
  asyncForEach,
105
108
  checkTypes,
106
109
  generateSerialId,
110
+ generateSerialIds,
107
111
  outputsToPayouts,
108
112
  } from './utils/Utils';
109
113
 
110
- const ESTIMATED_SIZE = 312;
111
-
112
114
  export default class BitcoinDlcProvider
113
115
  extends Provider
114
116
  implements Partial<DlcProvider> {
@@ -163,26 +165,27 @@ export default class BitcoinDlcProvider
163
165
  }
164
166
 
165
167
  async GetInputsForAmount(
166
- amount: bigint,
168
+ amounts: bigint[],
167
169
  feeRatePerVb: bigint,
168
170
  fixedInputs: Input[] = [],
169
171
  ): Promise<Input[]> {
170
- if (amount === BigInt(0)) return [];
171
- const targets: bitcoin.OutputTarget[] = [
172
- {
173
- address: BurnAddress,
174
- value: Number(amount) + ESTIMATED_SIZE * (Number(feeRatePerVb) - 1),
175
- },
176
- ];
172
+ if (amounts.length === 0) return [];
173
+
174
+ const fixedUtxos = fixedInputs.map((input) => input.toUtxo());
175
+
177
176
  let inputs: Input[];
178
177
  try {
179
- const inputsForAmount: InputsForAmountResponse = await this.getMethod(
180
- 'getInputsForAmount',
181
- )(targets, Number(feeRatePerVb), fixedInputs);
178
+ const inputsForAmount: InputsForDualAmountResponse = await this.getMethod(
179
+ 'getInputsForDualFunding',
180
+ )(amounts, feeRatePerVb, fixedUtxos);
181
+
182
182
  inputs = inputsForAmount.inputs;
183
183
  } catch (e) {
184
+ const errorMessage = e instanceof Error ? e.message : 'Unknown error';
184
185
  if (fixedInputs.length === 0) {
185
- throw Error('Not enough balance getInputsForAmount');
186
+ throw Error(
187
+ `Not enough balance getInputsForAmount. Error: ${errorMessage}`,
188
+ );
186
189
  } else {
187
190
  inputs = fixedInputs;
188
191
  }
@@ -221,7 +224,7 @@ export default class BitcoinDlcProvider
221
224
  throw Error('Address reuse');
222
225
 
223
226
  const inputs: Input[] = await this.GetInputsForAmount(
224
- collateral,
227
+ [collateral],
225
228
  feeRatePerVb,
226
229
  fixedInputs,
227
230
  );
@@ -244,6 +247,69 @@ export default class BitcoinDlcProvider
244
247
  };
245
248
  }
246
249
 
250
+ private async BatchInitialize(
251
+ collaterals: bigint[],
252
+ feeRatePerVb: bigint,
253
+ fixedInputs: Input[],
254
+ ): Promise<BatchInitializeResponse> {
255
+ const network = await this.getConnectedNetwork();
256
+
257
+ const inputs: Input[] = await this.GetInputsForAmount(
258
+ collaterals,
259
+ feeRatePerVb,
260
+ fixedInputs,
261
+ );
262
+
263
+ const fundingInputs: FundingInput[] = await Promise.all(
264
+ inputs.map(async (input) => {
265
+ return this.inputToFundingInput(input);
266
+ }),
267
+ );
268
+
269
+ const initializeResponses: BatchBaseInitializeResponse[] = [];
270
+
271
+ const changeSerialId: bigint = generateSerialId();
272
+
273
+ const changeAddress: Address = await this.client.wallet.getUnusedAddress(
274
+ true,
275
+ );
276
+ const changeSPK: Buffer = address.toOutputScript(
277
+ changeAddress.address,
278
+ network,
279
+ );
280
+
281
+ for (let i = 0; i < collaterals.length; i++) {
282
+ const payoutAddress: Address = await this.client.wallet.getUnusedAddress(
283
+ false,
284
+ );
285
+ const payoutSPK: Buffer = address.toOutputScript(
286
+ payoutAddress.address,
287
+ network,
288
+ );
289
+
290
+ const fundingAddress: Address = await this.client.wallet.getUnusedAddress(
291
+ false,
292
+ );
293
+ const fundingPubKey: Buffer = Buffer.from(
294
+ fundingAddress.publicKey,
295
+ 'hex',
296
+ );
297
+
298
+ if (fundingAddress.address === payoutAddress.address)
299
+ throw Error('Address reuse');
300
+
301
+ const payoutSerialId: bigint = generateSerialId();
302
+
303
+ initializeResponses.push({
304
+ fundingPubKey,
305
+ payoutSPK,
306
+ payoutSerialId,
307
+ });
308
+ }
309
+
310
+ return { fundingInputs, initializeResponses, changeSerialId, changeSPK };
311
+ }
312
+
247
313
  /**
248
314
  * TODO: Add GetPayoutFromOutcomes
249
315
  *
@@ -563,6 +629,150 @@ export default class BitcoinDlcProvider
563
629
  return { dlcTransactions, messagesList };
564
630
  }
565
631
 
632
+ public async createBatchDlcTxs(
633
+ _dlcOffers: DlcOffer[],
634
+ _dlcAccepts: DlcAccept[],
635
+ ): Promise<CreateBatchDlcTxsResponse> {
636
+ const dlcOffers = _dlcOffers.map((dlcOffer) => {
637
+ return checkTypes({ _dlcOffer: dlcOffer }).dlcOffer;
638
+ });
639
+ const dlcAccepts = _dlcAccepts.map((dlcAccept) => {
640
+ return checkTypes({ _dlcAccept: dlcAccept }).dlcAccept;
641
+ });
642
+
643
+ const localFundPubkeys = dlcOffers.map((dlcOffer) =>
644
+ dlcOffer.fundingPubKey.toString('hex'),
645
+ );
646
+ const remoteFundPubkeys = dlcAccepts.map((dlcAccept) =>
647
+ dlcAccept.fundingPubKey.toString('hex'),
648
+ );
649
+ const localFinalScriptPubkeys = dlcOffers.map((dlcOffer) =>
650
+ dlcOffer.payoutSPK.toString('hex'),
651
+ );
652
+ const remoteFinalScriptPubkeys = dlcAccepts.map((dlcAccept) =>
653
+ dlcAccept.payoutSPK.toString('hex'),
654
+ );
655
+ const localChangeScriptPubkey = dlcOffers[0].changeSPK.toString('hex');
656
+ const remoteChangeScriptPubkey = dlcAccepts[0].changeSPK.toString('hex');
657
+
658
+ const localInputs: Utxo[] = await Promise.all(
659
+ dlcOffers[0].fundingInputs.map(async (fundingInput) => {
660
+ const input = await this.fundingInputToInput(fundingInput, false);
661
+ return input.toUtxo();
662
+ }),
663
+ );
664
+
665
+ const remoteInputs: Utxo[] = await Promise.all(
666
+ dlcAccepts[0].fundingInputs.map(async (fundingInput) => {
667
+ const input = await this.fundingInputToInput(fundingInput, false);
668
+ return input.toUtxo();
669
+ }),
670
+ );
671
+
672
+ const localInputAmount = localInputs.reduce<number>(
673
+ (prev, cur) => prev + cur.amount.GetSatoshiAmount(),
674
+ 0,
675
+ );
676
+
677
+ const remoteInputAmount = remoteInputs.reduce<number>(
678
+ (prev, cur) => prev + cur.amount.GetSatoshiAmount(),
679
+ 0,
680
+ );
681
+
682
+ const localPayouts: (bigint | number)[] = [];
683
+ const remotePayouts: (bigint | number)[] = [];
684
+ const numPayouts: (bigint | number)[] = [];
685
+
686
+ const nestedMessagesList: Messages[][] = [];
687
+
688
+ // loop through all dlc offers, get payouts, and add to localPayouts and remotePayouts
689
+ for (const dlcOffer of dlcOffers) {
690
+ const payoutResponses = this.GetPayouts(dlcOffer);
691
+ const { payouts, messagesList } = this.FlattenPayouts(payoutResponses);
692
+ const tempLocalPayouts = payouts.map((payout) => payout.local);
693
+ const tempRemotePayouts = payouts.map((payout) => payout.remote);
694
+ localPayouts.push(...tempLocalPayouts);
695
+ remotePayouts.push(...tempRemotePayouts);
696
+ numPayouts.push(tempLocalPayouts.length);
697
+ nestedMessagesList.push(messagesList);
698
+ }
699
+
700
+ const batchDlcTxRequest: CreateBatchDlcTransactionsRequest = {
701
+ localPayouts,
702
+ remotePayouts,
703
+ numPayouts,
704
+ localFundPubkeys,
705
+ localFinalScriptPubkeys,
706
+ remoteFundPubkeys,
707
+ remoteFinalScriptPubkeys,
708
+ localInputAmount,
709
+ localCollateralAmounts: dlcOffers.map(
710
+ (dlcOffer) => dlcOffer.offerCollateralSatoshis,
711
+ ),
712
+ localPayoutSerialIds: dlcOffers.map(
713
+ (dlcOffer) => dlcOffer.payoutSerialId,
714
+ ),
715
+ localChangeSerialId: dlcOffers[0].changeSerialId,
716
+ remoteInputAmount,
717
+ remoteCollateralAmounts: dlcAccepts.map(
718
+ (dlcAccept) => dlcAccept.acceptCollateralSatoshis,
719
+ ),
720
+ remotePayoutSerialIds: dlcAccepts.map(
721
+ (dlcAccept) => dlcAccept.payoutSerialId,
722
+ ),
723
+ remoteChangeSerialId: dlcAccepts[0].changeSerialId,
724
+ refundLocktimes: dlcOffers.map((dlcOffer) => dlcOffer.refundLocktime),
725
+ localInputs,
726
+ remoteInputs,
727
+ localChangeScriptPubkey,
728
+ remoteChangeScriptPubkey,
729
+ feeRate: Number(dlcOffers[0].feeRatePerVb),
730
+ cetLockTime: dlcOffers[0].cetLocktime,
731
+ fundOutputSerialIds: dlcOffers.map(
732
+ (dlcOffer) => dlcOffer.fundOutputSerialId,
733
+ ),
734
+ };
735
+
736
+ const dlcTxs = await this.CreateBatchDlcTransactions(batchDlcTxRequest);
737
+
738
+ const dlcTransactionsList: DlcTransactionsV0[] = [];
739
+
740
+ let start = 0;
741
+ for (let i = 0; i < dlcTxs.refundTxHexList.length; i++) {
742
+ const dlcTransactions = new DlcTransactionsV0();
743
+
744
+ dlcTransactions.fundTx = Tx.decode(
745
+ StreamReader.fromHex(dlcTxs.fundTxHex),
746
+ );
747
+
748
+ dlcTransactions.fundTxVout = [
749
+ BigInt(dlcOffers[i].changeSerialId),
750
+ BigInt(dlcAccepts[i].changeSerialId),
751
+ ...dlcOffers.map((dlcOffer) => dlcOffer.fundOutputSerialId),
752
+ ]
753
+ .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))
754
+ .findIndex(
755
+ (j) => BigInt(j) === BigInt(dlcOffers[i].fundOutputSerialId),
756
+ );
757
+
758
+ dlcTransactions.refundTx = Tx.decode(
759
+ StreamReader.fromHex(dlcTxs.refundTxHexList[i]),
760
+ );
761
+
762
+ // slice cetsHexList based on numPayouts
763
+ const end = start + Number(numPayouts[i]);
764
+ const cetsHexList = dlcTxs.cetsHexList.slice(start, end);
765
+ start = end;
766
+ dlcTransactions.cets = cetsHexList.map((cetHex) => {
767
+ return Tx.decode(StreamReader.fromHex(cetHex));
768
+ });
769
+
770
+ dlcTransactionsList.push(dlcTransactions);
771
+ }
772
+
773
+ return { dlcTransactionsList, nestedMessagesList };
774
+ }
775
+
566
776
  private GenerateEnumMessages(oracleEvent: OracleEventV0): Messages[] {
567
777
  throw Error('Only DigitDecomposition Oracle Events supported');
568
778
  }
@@ -1873,6 +2083,109 @@ Payout Group found but incorrect group index',
1873
2083
  return dlcOffer;
1874
2084
  }
1875
2085
 
2086
+ async batchCreateDlcOffer(
2087
+ contractInfos: ContractInfo[],
2088
+ offerCollaterals: bigint[],
2089
+ feeRatePerVb: bigint,
2090
+ cetLocktime: number,
2091
+ refundLocktimes: number[],
2092
+ fixedInputs?: Input[],
2093
+ ): Promise<DlcOffer[]> {
2094
+ if (
2095
+ contractInfos.length !== offerCollaterals.length ||
2096
+ contractInfos.length !== refundLocktimes.length
2097
+ ) {
2098
+ throw new Error(
2099
+ 'The number of contractInfos, offerCollateralSatoshis, and refundLocktimes must be the same',
2100
+ );
2101
+ }
2102
+
2103
+ const dlcOffers: DlcOfferV0[] = [];
2104
+
2105
+ for (let i = 0; i < contractInfos.length; i++) {
2106
+ contractInfos[i].validate();
2107
+ }
2108
+
2109
+ const network = await this.getConnectedNetwork();
2110
+
2111
+ const {
2112
+ fundingInputs: _fundingInputs,
2113
+ changeSPK,
2114
+ changeSerialId,
2115
+ initializeResponses,
2116
+ } = await this.BatchInitialize(offerCollaterals, feeRatePerVb, fixedInputs);
2117
+
2118
+ _fundingInputs.forEach((input) =>
2119
+ assert(
2120
+ input.type === MessageType.FundingInputV0,
2121
+ 'FundingInput must be V0',
2122
+ ),
2123
+ );
2124
+
2125
+ const fundingInputs: FundingInputV0[] = _fundingInputs.map(
2126
+ (input) => input as FundingInputV0,
2127
+ );
2128
+
2129
+ fundingInputs.sort(
2130
+ (a, b) => Number(a.inputSerialId) - Number(b.inputSerialId),
2131
+ );
2132
+
2133
+ const fundOutputsSerialIds = generateSerialIds(contractInfos.length);
2134
+
2135
+ for (let i = 0; i < contractInfos.length; i++) {
2136
+ const contractInfo = contractInfos[i];
2137
+ const offerCollateralSatoshis = offerCollaterals[i];
2138
+ const fundOutputSerialId = fundOutputsSerialIds[i];
2139
+ const { fundingPubKey, payoutSPK, payoutSerialId } = initializeResponses[
2140
+ i
2141
+ ];
2142
+ const refundLocktime = refundLocktimes[i];
2143
+
2144
+ const dlcOffer = new DlcOfferV0();
2145
+
2146
+ dlcOffer.contractFlags = Buffer.from('00', 'hex');
2147
+ dlcOffer.chainHash = chainHashFromNetwork(network);
2148
+ dlcOffer.contractInfo = contractInfo;
2149
+ dlcOffer.fundingPubKey = fundingPubKey;
2150
+ dlcOffer.payoutSPK = payoutSPK;
2151
+ dlcOffer.payoutSerialId = payoutSerialId;
2152
+ dlcOffer.offerCollateralSatoshis = offerCollateralSatoshis;
2153
+ dlcOffer.fundingInputs = fundingInputs;
2154
+ dlcOffer.changeSPK = changeSPK;
2155
+ dlcOffer.changeSerialId = changeSerialId;
2156
+ dlcOffer.fundOutputSerialId = fundOutputSerialId;
2157
+ dlcOffer.feeRatePerVb = feeRatePerVb;
2158
+ dlcOffer.cetLocktime = cetLocktime;
2159
+ dlcOffer.refundLocktime = refundLocktime;
2160
+
2161
+ assert(
2162
+ (() => {
2163
+ const finalizer = new DualFundingTxFinalizer(
2164
+ dlcOffer.fundingInputs,
2165
+ dlcOffer.payoutSPK,
2166
+ dlcOffer.changeSPK,
2167
+ null,
2168
+ null,
2169
+ null,
2170
+ dlcOffer.feeRatePerVb,
2171
+ );
2172
+ const funding = fundingInputs.reduce((total, input) => {
2173
+ return total + input.prevTx.outputs[input.prevTxVout].value.sats;
2174
+ }, BigInt(0));
2175
+
2176
+ return funding >= offerCollateralSatoshis + finalizer.offerFees;
2177
+ })(),
2178
+ 'fundingInputs for dlcOffer must be greater than offerCollateralSatoshis plus offerFees',
2179
+ );
2180
+
2181
+ dlcOffer.validate();
2182
+
2183
+ dlcOffers.push(dlcOffer);
2184
+ }
2185
+
2186
+ return dlcOffers;
2187
+ }
2188
+
1876
2189
  /**
1877
2190
  * Accept DLC Offer
1878
2191
  * @param _dlcOffer Dlc Offer Message
@@ -2019,6 +2332,168 @@ Payout Group found but incorrect group index',
2019
2332
  return { dlcAccept, dlcTransactions: _dlcTransactions };
2020
2333
  }
2021
2334
 
2335
+ async batchAcceptDlcOffer(
2336
+ _dlcOffers: DlcOffer[],
2337
+ fixedInputs?: Input[],
2338
+ ): Promise<BatchAcceptDlcOfferResponse> {
2339
+ const dlcOffers = _dlcOffers.map((_dlcOffer) => {
2340
+ const { dlcOffer } = checkTypes({ _dlcOffer });
2341
+ dlcOffer.validate();
2342
+ return dlcOffer;
2343
+ });
2344
+
2345
+ const acceptCollaterals = dlcOffers.map(
2346
+ (dlcOffer) =>
2347
+ dlcOffer.contractInfo.totalCollateral -
2348
+ dlcOffer.offerCollateralSatoshis,
2349
+ );
2350
+
2351
+ const {
2352
+ fundingInputs: _fundingInputs,
2353
+ changeSPK,
2354
+ changeSerialId,
2355
+ initializeResponses,
2356
+ } = await this.BatchInitialize(
2357
+ acceptCollaterals,
2358
+ dlcOffers[0].feeRatePerVb,
2359
+ fixedInputs,
2360
+ );
2361
+
2362
+ // Check that none of the funding pubkeys are the same between the
2363
+ // dlcOffers and the dlcAccepts (from initializeResponses)
2364
+ dlcOffers.forEach((dlcOffer) => {
2365
+ initializeResponses.forEach((initializeResponse) => {
2366
+ assert(
2367
+ Buffer.compare(
2368
+ dlcOffer.fundingPubKey,
2369
+ initializeResponse.fundingPubKey,
2370
+ ) !== 0,
2371
+ 'DlcOffer and DlcAccept FundingPubKey cannot be the same',
2372
+ );
2373
+ });
2374
+ });
2375
+
2376
+ _fundingInputs.forEach((input) =>
2377
+ assert(
2378
+ input.type === MessageType.FundingInputV0,
2379
+ 'FundingInput must be V0',
2380
+ ),
2381
+ );
2382
+
2383
+ const fundingInputs: FundingInputV0[] = _fundingInputs.map(
2384
+ (input) => input as FundingInputV0,
2385
+ );
2386
+
2387
+ fundingInputs.sort(
2388
+ (a, b) => Number(a.inputSerialId) - Number(b.inputSerialId),
2389
+ );
2390
+
2391
+ const dlcAccepts: DlcAcceptV0[] = [];
2392
+
2393
+ initializeResponses.forEach((initializeResponse, i) => {
2394
+ const dlcOffer = dlcOffers[i];
2395
+ const dlcAccept = new DlcAcceptV0();
2396
+
2397
+ const { fundingPubKey, payoutSPK, payoutSerialId } = initializeResponse;
2398
+
2399
+ dlcAccept.tempContractId = sha256(dlcOffers[i].serialize());
2400
+ dlcAccept.acceptCollateralSatoshis = acceptCollaterals[i];
2401
+ dlcAccept.fundingPubKey = fundingPubKey;
2402
+ dlcAccept.payoutSPK = payoutSPK;
2403
+ dlcAccept.payoutSerialId = payoutSerialId;
2404
+ dlcAccept.fundingInputs = fundingInputs;
2405
+ dlcAccept.changeSPK = changeSPK;
2406
+ dlcAccept.changeSerialId = changeSerialId;
2407
+
2408
+ assert(
2409
+ dlcAccept.changeSerialId !== dlcOffer.fundOutputSerialId,
2410
+ 'changeSerialId cannot equal the fundOutputSerialId',
2411
+ );
2412
+
2413
+ assert(
2414
+ dlcOffer.payoutSerialId !== dlcAccept.payoutSerialId,
2415
+ 'offer.payoutSerialId cannot equal accept.payoutSerialId',
2416
+ );
2417
+
2418
+ assert(
2419
+ (() => {
2420
+ const ids = [
2421
+ dlcOffer.changeSerialId,
2422
+ dlcAccept.changeSerialId,
2423
+ dlcOffer.fundOutputSerialId,
2424
+ ];
2425
+ return new Set(ids).size === ids.length;
2426
+ })(),
2427
+ 'offer.changeSerialID, accept.changeSerialId and fundOutputSerialId must be unique',
2428
+ );
2429
+
2430
+ dlcAccept.validate();
2431
+
2432
+ assert(
2433
+ (() => {
2434
+ const finalizer = new DualFundingTxFinalizer(
2435
+ dlcOffer.fundingInputs,
2436
+ dlcOffer.payoutSPK,
2437
+ dlcOffer.changeSPK,
2438
+ dlcAccept.fundingInputs,
2439
+ dlcAccept.payoutSPK,
2440
+ dlcAccept.changeSPK,
2441
+ dlcOffer.feeRatePerVb,
2442
+ );
2443
+ const funding = fundingInputs.reduce((total, input) => {
2444
+ return total + input.prevTx.outputs[input.prevTxVout].value.sats;
2445
+ }, BigInt(0));
2446
+
2447
+ return funding >= acceptCollaterals[i] + finalizer.acceptFees;
2448
+ })(),
2449
+ 'fundingInputs for dlcAccept must be greater than acceptCollateralSatoshis plus acceptFees',
2450
+ );
2451
+
2452
+ dlcAccepts.push(dlcAccept);
2453
+ });
2454
+
2455
+ const {
2456
+ dlcTransactionsList,
2457
+ nestedMessagesList,
2458
+ } = await this.createBatchDlcTxs(dlcOffers, dlcAccepts);
2459
+
2460
+ for (let i = 0; i < dlcAccepts.length; i++) {
2461
+ const dlcOffer = dlcOffers[i];
2462
+ const dlcAccept = dlcAccepts[i];
2463
+ const dlcTransactions = dlcTransactionsList[i];
2464
+ const messagesList = nestedMessagesList[i];
2465
+
2466
+ const {
2467
+ cetSignatures,
2468
+ refundSignature,
2469
+ } = await this.CreateCetAdaptorAndRefundSigs(
2470
+ dlcOffer,
2471
+ dlcAccept,
2472
+ dlcTransactions,
2473
+ messagesList,
2474
+ false,
2475
+ );
2476
+
2477
+ assert(
2478
+ dlcTransactions.type === MessageType.DlcTransactionsV0,
2479
+ 'DlcTransactions must be V0',
2480
+ );
2481
+ const _dlcTransactions = dlcTransactions as DlcTransactionsV0;
2482
+
2483
+ const contractId = xor(
2484
+ _dlcTransactions.fundTx.txId.serialize(),
2485
+ dlcAccept.tempContractId,
2486
+ );
2487
+ _dlcTransactions.contractId = contractId;
2488
+
2489
+ dlcAccepts[i].cetSignatures = cetSignatures;
2490
+ dlcAccepts[i].refundSignature = refundSignature;
2491
+ dlcAccepts[i].negotiationFields = new NegotiationFieldsV0();
2492
+ }
2493
+
2494
+ return { dlcAccepts, dlcTransactionsList };
2495
+ }
2496
+
2022
2497
  /**
2023
2498
  * Sign Dlc Accept Message
2024
2499
  * @param _dlcOffer Dlc Offer Message
@@ -2100,6 +2575,84 @@ Payout Group found but incorrect group index',
2100
2575
  return { dlcSign, dlcTransactions: dlcTxs };
2101
2576
  }
2102
2577
 
2578
+ async batchSignDlcAccept(
2579
+ _dlcOffers: DlcOffer[],
2580
+ _dlcAccepts: DlcAccept[],
2581
+ ): Promise<BatchSignDlcAcceptResponse> {
2582
+ const dlcOffers = _dlcOffers.map((_dlcOffer) => {
2583
+ const { dlcOffer } = checkTypes({ _dlcOffer });
2584
+ dlcOffer.validate();
2585
+ return dlcOffer;
2586
+ });
2587
+
2588
+ const dlcAccepts = _dlcAccepts.map((_dlcAccept) => {
2589
+ const { dlcAccept } = checkTypes({ _dlcAccept });
2590
+ dlcAccept.validate();
2591
+ return dlcAccept;
2592
+ });
2593
+
2594
+ const {
2595
+ dlcTransactionsList,
2596
+ nestedMessagesList,
2597
+ } = await this.createBatchDlcTxs(dlcOffers, dlcAccepts);
2598
+
2599
+ const dlcSigns: DlcSignV0[] = [];
2600
+
2601
+ const fundingSignatures = await this.CreateFundingSigs(
2602
+ dlcOffers[0],
2603
+ dlcAccepts[0],
2604
+ dlcTransactionsList[0],
2605
+ true,
2606
+ );
2607
+
2608
+ for (let i = 0; i < dlcAccepts.length; i++) {
2609
+ const dlcOffer = dlcOffers[i];
2610
+ const dlcAccept = dlcAccepts[i];
2611
+ const dlcTransactions = dlcTransactionsList[i];
2612
+ const messagesList = nestedMessagesList[i];
2613
+
2614
+ const dlcSign = new DlcSignV0();
2615
+
2616
+ await this.VerifyCetAdaptorAndRefundSigs(
2617
+ dlcOffer,
2618
+ dlcAccept,
2619
+ dlcSign,
2620
+ dlcTransactions,
2621
+ messagesList,
2622
+ true,
2623
+ );
2624
+
2625
+ const {
2626
+ cetSignatures,
2627
+ refundSignature,
2628
+ } = await this.CreateCetAdaptorAndRefundSigs(
2629
+ dlcOffer,
2630
+ dlcAccept,
2631
+ dlcTransactions,
2632
+ messagesList,
2633
+ true,
2634
+ );
2635
+
2636
+ const dlcTxs = dlcTransactions as DlcTransactionsV0;
2637
+
2638
+ const contractId = xor(
2639
+ dlcTxs.fundTx.txId.serialize(),
2640
+ dlcAccept.tempContractId,
2641
+ );
2642
+
2643
+ dlcTxs.contractId = contractId;
2644
+
2645
+ dlcSign.contractId = contractId;
2646
+ dlcSign.cetSignatures = cetSignatures;
2647
+ dlcSign.refundSignature = refundSignature;
2648
+ dlcSign.fundingSignatures = fundingSignatures;
2649
+
2650
+ dlcSigns.push(dlcSign);
2651
+ }
2652
+
2653
+ return { dlcSigns, dlcTransactionsList };
2654
+ }
2655
+
2103
2656
  /**
2104
2657
  * Finalize Dlc Sign
2105
2658
  * @param _dlcOffer Dlc Offer Message
@@ -2153,6 +2706,79 @@ Payout Group found but incorrect group index',
2153
2706
  return fundTx;
2154
2707
  }
2155
2708
 
2709
+ async batchFinalizeDlcSign(
2710
+ _dlcOffers: DlcOffer[],
2711
+ _dlcAccepts: DlcAccept[],
2712
+ _dlcSigns: DlcSign[],
2713
+ _dlcTxsList: DlcTransactions[],
2714
+ ): Promise<Tx> {
2715
+ const dlcOffers = _dlcOffers.map((_dlcOffer) => {
2716
+ const { dlcOffer } = checkTypes({ _dlcOffer });
2717
+ dlcOffer.validate();
2718
+ return dlcOffer;
2719
+ });
2720
+
2721
+ const dlcAccepts = _dlcAccepts.map((_dlcAccept) => {
2722
+ const { dlcAccept } = checkTypes({ _dlcAccept });
2723
+ dlcAccept.validate();
2724
+ return dlcAccept;
2725
+ });
2726
+
2727
+ const dlcSigns = _dlcSigns.map((_dlcSign) => {
2728
+ const { dlcSign } = checkTypes({ _dlcSign });
2729
+ return dlcSign;
2730
+ });
2731
+
2732
+ const dlcTxsList = _dlcTxsList.map((_dlcTxs) => {
2733
+ const { dlcTxs } = checkTypes({ _dlcTxs });
2734
+ return dlcTxs;
2735
+ });
2736
+
2737
+ await this.VerifyFundingSigs(
2738
+ dlcOffers[0],
2739
+ dlcAccepts[0],
2740
+ dlcSigns[0],
2741
+ dlcTxsList[0],
2742
+ false,
2743
+ );
2744
+
2745
+ for (let i = 0; i < dlcOffers.length; i++) {
2746
+ const dlcOffer = dlcOffers[i];
2747
+ const dlcAccept = dlcAccepts[i];
2748
+ const dlcSign = dlcSigns[i];
2749
+ const dlcTxs = dlcTxsList[i];
2750
+
2751
+ const payoutResponses = this.GetPayouts(dlcOffer);
2752
+ const { messagesList } = this.FlattenPayouts(payoutResponses);
2753
+
2754
+ await this.VerifyCetAdaptorAndRefundSigs(
2755
+ dlcOffer,
2756
+ dlcAccept,
2757
+ dlcSign,
2758
+ dlcTxs,
2759
+ messagesList,
2760
+ false,
2761
+ );
2762
+ }
2763
+
2764
+ const fundingSignatures = await this.CreateFundingSigs(
2765
+ dlcOffers[0],
2766
+ dlcAccepts[0],
2767
+ dlcTxsList[0],
2768
+ false,
2769
+ );
2770
+
2771
+ const fundTx = await this.CreateFundingTx(
2772
+ dlcOffers[0],
2773
+ dlcAccepts[0],
2774
+ dlcSigns[0],
2775
+ dlcTxsList[0],
2776
+ fundingSignatures,
2777
+ );
2778
+
2779
+ return fundTx;
2780
+ }
2781
+
2156
2782
  /**
2157
2783
  * Execute DLC
2158
2784
  * @param _dlcOffer Dlc Offer Message
@@ -2298,7 +2924,7 @@ Payout Group found but incorrect group index',
2298
2924
  let inputs: Input[] = _inputs;
2299
2925
  if (!_inputs) {
2300
2926
  const tempInputs = await this.GetInputsForAmount(
2301
- BigInt(20000),
2927
+ [BigInt(20000)],
2302
2928
  dlcOffer.feeRatePerVb,
2303
2929
  _inputs,
2304
2930
  );
@@ -2890,6 +3516,14 @@ Payout Group found but incorrect group index',
2890
3516
  return this._cfdDlcJs.CreateDlcTransactions(jsonObject);
2891
3517
  }
2892
3518
 
3519
+ async CreateBatchDlcTransactions(
3520
+ jsonObject: CreateBatchDlcTransactionsRequest,
3521
+ ): Promise<CreateBatchDlcTransactionsResponse> {
3522
+ await this.CfdLoaded();
3523
+
3524
+ return this._cfdDlcJs.CreateBatchDlcTransactions(jsonObject);
3525
+ }
3526
+
2893
3527
  async CreateFundTransaction(
2894
3528
  jsonObject: CreateFundTransactionRequest,
2895
3529
  ): Promise<CreateFundTransactionResponse> {
@@ -2898,6 +3532,14 @@ Payout Group found but incorrect group index',
2898
3532
  return this._cfdDlcJs.CreateFundTransaction(jsonObject);
2899
3533
  }
2900
3534
 
3535
+ async CreateBatchFundTransaction(
3536
+ jsonObject: CreateBatchFundTransactionRequest,
3537
+ ): Promise<CreateBatchFundTransactionResponse> {
3538
+ await this.CfdLoaded();
3539
+
3540
+ return this._cfdDlcJs.CreateBatchFundTransaction(jsonObject);
3541
+ }
3542
+
2901
3543
  async CreateRefundTransaction(
2902
3544
  jsonObject: CreateRefundTransactionRequest,
2903
3545
  ): Promise<CreateRefundTransactionResponse> {
@@ -3050,10 +3692,26 @@ Payout Group found but incorrect group index',
3050
3692
  }
3051
3693
  }
3052
3694
 
3053
- export interface InitializeResponse {
3695
+ export interface BasicInitializeResponse {
3696
+ fundingPubKey: Buffer;
3697
+ payoutSPK: Buffer;
3698
+ payoutSerialId: bigint;
3699
+ changeSPK: Buffer;
3700
+ changeSerialId: bigint;
3701
+ }
3702
+
3703
+ export interface InitializeResponse extends BasicInitializeResponse {
3704
+ fundingInputs: FundingInput[];
3705
+ }
3706
+
3707
+ export interface BatchBaseInitializeResponse {
3054
3708
  fundingPubKey: Buffer;
3055
3709
  payoutSPK: Buffer;
3056
3710
  payoutSerialId: bigint;
3711
+ }
3712
+
3713
+ export interface BatchInitializeResponse {
3714
+ initializeResponses: BatchBaseInitializeResponse[];
3057
3715
  fundingInputs: FundingInput[];
3058
3716
  changeSPK: Buffer;
3059
3717
  changeSerialId: bigint;
@@ -3064,11 +3722,21 @@ export interface AcceptDlcOfferResponse {
3064
3722
  dlcTransactions: DlcTransactions;
3065
3723
  }
3066
3724
 
3725
+ export interface BatchAcceptDlcOfferResponse {
3726
+ dlcAccepts: DlcAccept[];
3727
+ dlcTransactionsList: DlcTransactions[];
3728
+ }
3729
+
3067
3730
  export interface SignDlcAcceptResponse {
3068
3731
  dlcSign: DlcSign;
3069
3732
  dlcTransactions: DlcTransactions;
3070
3733
  }
3071
3734
 
3735
+ export interface BatchSignDlcAcceptResponse {
3736
+ dlcSigns: DlcSign[];
3737
+ dlcTransactionsList: DlcTransactions[];
3738
+ }
3739
+
3072
3740
  export interface GetPayoutsResponse {
3073
3741
  payouts: PayoutRequest[];
3074
3742
  payoutGroups: PayoutGroup[];
@@ -3080,6 +3748,11 @@ export interface CreateDlcTxsResponse {
3080
3748
  messagesList: Messages[];
3081
3749
  }
3082
3750
 
3751
+ export interface CreateBatchDlcTxsResponse {
3752
+ dlcTransactionsList: DlcTransactions[];
3753
+ nestedMessagesList: Messages[][];
3754
+ }
3755
+
3083
3756
  interface ISig {
3084
3757
  encryptedSig: Buffer;
3085
3758
  dleqProof: Buffer;
@@ -3116,4 +3789,9 @@ export interface InputsForAmountResponse {
3116
3789
  fee: number;
3117
3790
  }
3118
3791
 
3792
+ export interface InputsForDualAmountResponse {
3793
+ inputs: Input[];
3794
+ fee: number;
3795
+ }
3796
+
3119
3797
  const BurnAddress = 'bcrt1qxcjufgh2jarkp2qkx68azh08w9v5gah8u6es8s';