@atomiqlabs/sdk 8.8.3 → 8.9.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.
Files changed (130) hide show
  1. package/api/index.d.ts +1 -0
  2. package/api/index.js +3 -0
  3. package/dist/ApiList.d.ts +37 -0
  4. package/dist/ApiList.js +30 -0
  5. package/dist/api/ApiEndpoints.d.ts +393 -0
  6. package/dist/api/ApiEndpoints.js +2 -0
  7. package/dist/api/ApiParser.d.ts +10 -0
  8. package/dist/api/ApiParser.js +134 -0
  9. package/dist/api/ApiTypes.d.ts +157 -0
  10. package/dist/api/ApiTypes.js +75 -0
  11. package/dist/api/SerializedAction.d.ts +40 -0
  12. package/dist/api/SerializedAction.js +59 -0
  13. package/dist/api/SwapperApi.d.ts +50 -0
  14. package/dist/api/SwapperApi.js +431 -0
  15. package/dist/api/index.d.ts +5 -0
  16. package/dist/api/index.js +24 -0
  17. package/dist/events/UnifiedSwapEventListener.d.ts +4 -3
  18. package/dist/events/UnifiedSwapEventListener.js +8 -2
  19. package/dist/http/HttpUtils.d.ts +4 -2
  20. package/dist/http/HttpUtils.js +10 -4
  21. package/dist/http/paramcoders/client/StreamingFetchPromise.d.ts +2 -1
  22. package/dist/http/paramcoders/client/StreamingFetchPromise.js +3 -2
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.js +1 -0
  25. package/dist/intermediaries/IntermediaryDiscovery.d.ts +7 -2
  26. package/dist/intermediaries/IntermediaryDiscovery.js +4 -4
  27. package/dist/intermediaries/apis/IntermediaryAPI.d.ts +171 -14
  28. package/dist/intermediaries/apis/IntermediaryAPI.js +174 -28
  29. package/dist/intermediaries/auth/SignedKeyBasedAuth.d.ts +14 -0
  30. package/dist/intermediaries/auth/SignedKeyBasedAuth.js +68 -0
  31. package/dist/storage/IUnifiedStorage.d.ts +45 -3
  32. package/dist/storage/UnifiedSwapStorage.d.ts +8 -2
  33. package/dist/storage/UnifiedSwapStorage.js +46 -8
  34. package/dist/swapper/Swapper.d.ts +36 -3
  35. package/dist/swapper/Swapper.js +54 -18
  36. package/dist/swapper/SwapperUtils.d.ts +18 -2
  37. package/dist/swapper/SwapperUtils.js +39 -1
  38. package/dist/swaps/ISwap.d.ts +70 -9
  39. package/dist/swaps/ISwap.js +28 -6
  40. package/dist/swaps/ISwapWrapper.d.ts +11 -1
  41. package/dist/swaps/ISwapWrapper.js +23 -3
  42. package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +1 -1
  43. package/dist/swaps/escrow_swaps/IEscrowSwap.js +4 -2
  44. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +2 -1
  45. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +2 -2
  46. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +3 -1
  47. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +3 -2
  48. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +47 -31
  49. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +201 -67
  50. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +3 -1
  51. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +6 -6
  52. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +82 -15
  53. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +304 -98
  54. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +3 -1
  55. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +6 -6
  56. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +75 -42
  57. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +424 -87
  58. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +3 -1
  59. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +7 -7
  60. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +54 -11
  61. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +214 -41
  62. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +2 -1
  63. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +7 -8
  64. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +3 -1
  65. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +5 -5
  66. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +76 -19
  67. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +290 -51
  68. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +3 -1
  69. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +5 -5
  70. package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +53 -12
  71. package/dist/swaps/trusted/ln/LnForGasSwap.js +163 -49
  72. package/dist/swaps/trusted/ln/LnForGasWrapper.js +1 -2
  73. package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +14 -13
  74. package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +30 -47
  75. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +3 -1
  76. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +4 -4
  77. package/dist/types/SwapExecutionAction.d.ts +141 -34
  78. package/dist/types/SwapExecutionAction.js +104 -0
  79. package/dist/types/SwapExecutionStep.d.ts +144 -0
  80. package/dist/types/SwapExecutionStep.js +87 -0
  81. package/dist/types/TokenAmount.d.ts +6 -0
  82. package/dist/types/TokenAmount.js +26 -1
  83. package/dist/utils/BitcoinUtils.d.ts +2 -0
  84. package/dist/utils/BitcoinUtils.js +34 -1
  85. package/dist/utils/Utils.d.ts +3 -1
  86. package/dist/utils/Utils.js +7 -1
  87. package/package.json +7 -4
  88. package/src/api/ApiEndpoints.ts +427 -0
  89. package/src/api/ApiParser.ts +138 -0
  90. package/src/api/ApiTypes.ts +229 -0
  91. package/src/api/SerializedAction.ts +97 -0
  92. package/src/api/SwapperApi.ts +545 -0
  93. package/src/api/index.ts +5 -0
  94. package/src/events/UnifiedSwapEventListener.ts +11 -3
  95. package/src/http/HttpUtils.ts +10 -4
  96. package/src/http/paramcoders/client/StreamingFetchPromise.ts +4 -2
  97. package/src/index.ts +1 -0
  98. package/src/intermediaries/IntermediaryDiscovery.ts +9 -2
  99. package/src/intermediaries/apis/IntermediaryAPI.ts +314 -30
  100. package/src/intermediaries/auth/SignedKeyBasedAuth.ts +69 -0
  101. package/src/storage/IUnifiedStorage.ts +45 -4
  102. package/src/storage/UnifiedSwapStorage.ts +42 -8
  103. package/src/swapper/Swapper.ts +87 -18
  104. package/src/swapper/SwapperUtils.ts +42 -2
  105. package/src/swaps/ISwap.ts +88 -16
  106. package/src/swaps/ISwapWrapper.ts +28 -3
  107. package/src/swaps/escrow_swaps/IEscrowSwap.ts +5 -3
  108. package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +3 -1
  109. package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +4 -1
  110. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +264 -67
  111. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +6 -4
  112. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +390 -89
  113. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +6 -4
  114. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +548 -94
  115. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +7 -5
  116. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +276 -45
  117. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +7 -6
  118. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +5 -3
  119. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +393 -57
  120. package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +5 -3
  121. package/src/swaps/trusted/ln/LnForGasSwap.ts +211 -47
  122. package/src/swaps/trusted/ln/LnForGasWrapper.ts +1 -2
  123. package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +32 -51
  124. package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +5 -3
  125. package/src/types/SwapExecutionAction.ts +266 -43
  126. package/src/types/SwapExecutionStep.ts +224 -0
  127. package/src/types/TokenAmount.ts +36 -2
  128. package/src/utils/BitcoinUtils.ts +32 -0
  129. package/src/utils/Utils.ts +10 -1
  130. package/src/intermediaries/apis/TrustedIntermediaryAPI.ts +0 -258
@@ -200,6 +200,29 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
200
200
  getInputTxId() {
201
201
  return this.txId ?? null;
202
202
  }
203
+ async _setSubmittedBitcoinTx(txId, psbt) {
204
+ let changed = false;
205
+ if (this.txId !== txId) {
206
+ this.txId = txId;
207
+ changed = true;
208
+ }
209
+ const submittedVout = this.address == null || this.amount == null || psbt == null
210
+ ? undefined
211
+ : (0, BitcoinUtils_1.getVoutIndex)(psbt, this.wrapper._options.bitcoinNetwork, this.address, this.amount);
212
+ if (submittedVout != null && this.vout !== submittedVout) {
213
+ this.vout = submittedVout;
214
+ changed = true;
215
+ }
216
+ const submittedSenderAddress = psbt == null
217
+ ? undefined
218
+ : (0, BitcoinUtils_1.getSenderAddress)(psbt, this.wrapper._options.bitcoinNetwork);
219
+ if (submittedSenderAddress != null && this.senderAddress !== submittedSenderAddress) {
220
+ this.senderAddress = submittedSenderAddress;
221
+ changed = true;
222
+ }
223
+ if (changed)
224
+ await this._saveAndEmit();
225
+ }
203
226
  /**
204
227
  * Returns timeout time (in UNIX milliseconds) when the on-chain address will expire and no funds should be sent
205
228
  * to that address anymore
@@ -404,7 +427,10 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
404
427
  if (btcTx != null && vout != null && requiredConfirmations == null) {
405
428
  requiredConfirmations = this.inferRequiredConfirmationsCount(btcTx, vout);
406
429
  }
407
- if (btcTx != null && (btcTx.txid !== this.txId || (this.requiredConfirmations == null && requiredConfirmations != null))) {
430
+ if (btcTx != null && (btcTx.txid !== this.txId ||
431
+ this.vout == null ||
432
+ this.senderAddress == null ||
433
+ (this.requiredConfirmations == null && requiredConfirmations != null))) {
408
434
  this.txId = btcTx.txid;
409
435
  this.vout = vout;
410
436
  this.requiredConfirmations = requiredConfirmations;
@@ -496,7 +522,8 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
496
522
  psbt,
497
523
  psbtHex: serializedPsbt.toString("hex"),
498
524
  psbtBase64: serializedPsbt.toString("base64"),
499
- signInputs
525
+ signInputs,
526
+ feeRate
500
527
  };
501
528
  }
502
529
  /**
@@ -505,6 +532,8 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
505
532
  getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs) {
506
533
  if (this._state !== FromBTCSwapState.CLAIM_COMMITED)
507
534
  throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
535
+ if (this.txId != null)
536
+ throw new Error("Bitcoin transaction already submitted for this swap!");
508
537
  return this._getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs);
509
538
  }
510
539
  /**
@@ -517,6 +546,8 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
517
546
  const psbt = (0, BitcoinUtils_1.parsePsbtTransaction)(_psbt);
518
547
  if (this._state !== FromBTCSwapState.CLAIM_COMMITED)
519
548
  throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
549
+ if (this.txId != null)
550
+ throw new Error("Bitcoin transaction already submitted for this swap!");
520
551
  //Ensure not expired
521
552
  if (this.getTimeoutTime() < Date.now()) {
522
553
  throw new Error("Swap address expired!");
@@ -531,7 +562,9 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
531
562
  }
532
563
  if (!psbt.isFinal)
533
564
  psbt.finalize();
534
- return await this.wrapper._btcRpc.sendRawTransaction(buffer_1.Buffer.from(psbt.toBytes(true, true)).toString("hex"));
565
+ const txId = await this.wrapper._btcRpc.sendRawTransaction(buffer_1.Buffer.from(psbt.toBytes(true, true)).toString("hex"));
566
+ await this._setSubmittedBitcoinTx(txId, psbt);
567
+ return txId;
535
568
  }
536
569
  /**
537
570
  * @inheritDoc
@@ -553,12 +586,16 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
553
586
  throw new Error("Cannot send bitcoin transaction, because the address is not known! This can happen after a swap is recovered.");
554
587
  if (this._state !== FromBTCSwapState.CLAIM_COMMITED)
555
588
  throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
589
+ if (this.txId != null)
590
+ throw new Error("Bitcoin transaction already submitted for this swap!");
556
591
  //Ensure not expired
557
592
  if (this.getTimeoutTime() < Date.now()) {
558
593
  throw new Error("Swap address expired!");
559
594
  }
560
595
  if ((0, IBitcoinWallet_1.isIBitcoinWallet)(wallet)) {
561
- return await wallet.sendTransaction(this.address, this.amount, feeRate);
596
+ const txId = await wallet.sendTransaction(this.address, this.amount, feeRate);
597
+ await this._setSubmittedBitcoinTx(txId);
598
+ return txId;
562
599
  }
563
600
  else {
564
601
  const { psbt, psbtHex, psbtBase64, signInputs } = await this.getFundedPsbt(wallet, feeRate);
@@ -600,7 +637,7 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
600
637
  if (this._state === FromBTCSwapState.CLAIM_COMMITED) {
601
638
  if (wallet != null) {
602
639
  const bitcoinPaymentSent = await this.getBitcoinPayment();
603
- if (bitcoinPaymentSent == null) {
640
+ if (bitcoinPaymentSent == null && this.txId == null) {
604
641
  //Send btc tx
605
642
  const txId = await this.sendBitcoinTransaction(wallet, options?.feeRate);
606
643
  if (callbacks?.onSourceTransactionSent != null)
@@ -623,71 +660,352 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
623
660
  throw new Error("Invalid state reached!");
624
661
  }
625
662
  /**
626
- * @inheritDoc
627
- *
628
- * @param options.bitcoinFeeRate Optional fee rate to use for the created Bitcoin transaction
629
- * @param options.bitcoinWallet Bitcoin wallet to use, when provided the function returns a funded
630
- * psbt (`"FUNDED_PSBT"`), if not passed just a bitcoin receive address is returned (`"ADDRESS"`)
631
- * @param options.skipChecks Skip checks like making sure init signature is still valid and swap
632
- * wasn't commited yet (this is handled on swap creation, if you commit right after quoting, you
633
- * can use `skipChecks=true`)
634
- *
635
- * @throws {Error} if the swap or quote is expired, or if triggered in invalid state
663
+ * @internal
636
664
  */
637
- async txsExecute(options) {
638
- if (this._state === FromBTCSwapState.PR_CREATED) {
639
- if (!await this._verifyQuoteValid())
640
- throw new Error("Quote already expired or close to expiry!");
641
- if (this.getTimeoutTime() < Date.now())
642
- throw new Error("Swap address already expired or close to expiry!");
643
- return [
665
+ async _getExecutionStatus(options) {
666
+ const state = this._state;
667
+ const now = Date.now();
668
+ const timeoutTime = this.getTimeoutTime();
669
+ let confirmations;
670
+ let bitcoinTxId;
671
+ let destinationSetupStatus = "awaiting";
672
+ let bitcoinPaymentStatus = "inactive";
673
+ let destinationSettlementStatus = "inactive";
674
+ let buildCurrentAction = async () => undefined;
675
+ switch (state) {
676
+ case FromBTCSwapState.PR_CREATED: {
677
+ const quoteValid = await this._verifyQuoteValid();
678
+ destinationSetupStatus = quoteValid && timeoutTime >= now ? "awaiting" : "soft_expired";
679
+ if (quoteValid && timeoutTime >= now) {
680
+ buildCurrentAction = this._buildInitSmartChainTxAction.bind(this);
681
+ }
682
+ break;
683
+ }
684
+ case FromBTCSwapState.QUOTE_SOFT_EXPIRED:
685
+ destinationSetupStatus = "soft_expired";
686
+ break;
687
+ case FromBTCSwapState.QUOTE_EXPIRED:
688
+ destinationSetupStatus = "expired";
689
+ break;
690
+ case FromBTCSwapState.CLAIM_COMMITED:
691
+ case FromBTCSwapState.EXPIRED:
692
+ case FromBTCSwapState.FAILED:
693
+ const bitcoinPayment = this.address == null ? null : await this.getBitcoinPayment();
694
+ bitcoinTxId = bitcoinPayment?.txId;
695
+ let bitcoinConfirmationDelay;
696
+ if (bitcoinPayment != null && bitcoinPayment.confirmations < bitcoinPayment.targetConfirmations) {
697
+ const tx = await this.wrapper._btcRpc.getTransaction(bitcoinPayment.txId);
698
+ const result = tx == null
699
+ ? null
700
+ : await this.wrapper._btcRpc.getConfirmationDelay(tx, bitcoinPayment.targetConfirmations);
701
+ bitcoinConfirmationDelay = result ?? -1;
702
+ }
703
+ destinationSetupStatus = "completed";
704
+ if (bitcoinPayment == null) {
705
+ if (this.txId != null) {
706
+ bitcoinPaymentStatus = state === FromBTCSwapState.FAILED ? "expired" : "received";
707
+ if (state !== FromBTCSwapState.FAILED) {
708
+ buildCurrentAction = this._buildWaitBitcoinConfirmationsAction.bind(this, -1, "Wait for bitcoin transaction to be picked up by the RPC and confirmed.");
709
+ }
710
+ }
711
+ else {
712
+ bitcoinPaymentStatus = "awaiting";
713
+ if (state === FromBTCSwapState.EXPIRED)
714
+ bitcoinPaymentStatus = "soft_expired";
715
+ if (state === FromBTCSwapState.FAILED)
716
+ bitcoinPaymentStatus = "expired";
717
+ if (state === FromBTCSwapState.CLAIM_COMMITED && timeoutTime >= now &&
718
+ this.address != null && this.amount != null) {
719
+ buildCurrentAction = this._buildSendToAddressOrSignPsbtAction.bind(this);
720
+ }
721
+ }
722
+ }
723
+ else if (bitcoinPayment.confirmations >= bitcoinPayment.targetConfirmations) {
724
+ bitcoinPaymentStatus = "confirmed";
725
+ if (state !== FromBTCSwapState.FAILED) {
726
+ buildCurrentAction = this._buildWaitBitcoinConfirmationsAction.bind(this, bitcoinConfirmationDelay ?? -1, undefined);
727
+ }
728
+ }
729
+ else {
730
+ bitcoinPaymentStatus = "received";
731
+ confirmations = {
732
+ current: bitcoinPayment.confirmations,
733
+ target: bitcoinPayment.targetConfirmations,
734
+ etaSeconds: bitcoinConfirmationDelay ?? -1
735
+ };
736
+ if (state !== FromBTCSwapState.FAILED) {
737
+ buildCurrentAction = this._buildWaitBitcoinConfirmationsAction.bind(this, bitcoinConfirmationDelay ?? -1, undefined);
738
+ }
739
+ }
740
+ destinationSettlementStatus = state === FromBTCSwapState.FAILED ? "expired" : "inactive";
741
+ break;
742
+ case FromBTCSwapState.BTC_TX_CONFIRMED:
743
+ destinationSetupStatus = "completed";
744
+ bitcoinPaymentStatus = "confirmed";
745
+ if (this.btcTxConfirmedAt == null ||
746
+ options?.maxWaitTillAutomaticSettlementSeconds === 0 ||
747
+ (now - this.btcTxConfirmedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60) * 1000) {
748
+ destinationSettlementStatus = "awaiting_manual";
749
+ buildCurrentAction = this._buildClaimSmartChainTxAction.bind(this);
750
+ }
751
+ else {
752
+ destinationSettlementStatus = "awaiting_automatic";
753
+ buildCurrentAction = this._buildWaitSettlementAction.bind(this, options?.maxWaitTillAutomaticSettlementSeconds);
754
+ }
755
+ break;
756
+ case FromBTCSwapState.CLAIM_CLAIMED:
757
+ destinationSetupStatus = "completed";
758
+ bitcoinPaymentStatus = "confirmed";
759
+ destinationSettlementStatus = "settled";
760
+ break;
761
+ }
762
+ if (bitcoinPaymentStatus === "confirmed") {
763
+ const requiredConfirmations = this.getRequiredConfirmationsCount();
764
+ if (!Number.isNaN(requiredConfirmations)) {
765
+ confirmations = {
766
+ current: requiredConfirmations,
767
+ target: requiredConfirmations,
768
+ etaSeconds: 0
769
+ };
770
+ }
771
+ }
772
+ return {
773
+ steps: [
644
774
  {
645
- name: "Commit",
646
- description: `Opens up the bitcoin swap address on the ${this.chainIdentifier} side`,
775
+ type: "Setup",
776
+ side: "destination",
647
777
  chain: this.chainIdentifier,
648
- txs: await this.txsCommit(options?.skipChecks)
778
+ title: "Open Bitcoin swap address",
779
+ description: `Create the escrow on the ${this.chainIdentifier} side to open the Bitcoin swap address`,
780
+ status: destinationSetupStatus,
781
+ setupTxId: this._commitTxId
649
782
  },
650
783
  {
651
- name: "Payment",
652
- description: "Send funds to the bitcoin swap address",
784
+ type: "Payment",
785
+ side: "source",
653
786
  chain: "BITCOIN",
654
- txs: [
655
- options?.bitcoinWallet == null ? {
656
- address: this.address ?? "",
657
- amount: Number(this.amount),
658
- hyperlink: this._getHyperlink(),
659
- type: "ADDRESS"
660
- } : {
661
- ...await this.getFundedPsbt(options.bitcoinWallet),
662
- type: "FUNDED_PSBT"
663
- }
664
- ]
787
+ title: "Bitcoin payment",
788
+ description: "Send Bitcoin to the swap address and wait for the transaction to confirm",
789
+ status: bitcoinPaymentStatus,
790
+ confirmations,
791
+ initTxId: this.txId ?? bitcoinTxId,
792
+ settleTxId: this.txId
793
+ },
794
+ {
795
+ type: "Settlement",
796
+ side: "destination",
797
+ chain: this.chainIdentifier,
798
+ title: "Destination settlement",
799
+ description: `Wait for automatic settlement on the ${this.chainIdentifier} side, or settle manually if it takes too long`,
800
+ status: destinationSettlementStatus,
801
+ initTxId: this._commitTxId,
802
+ settleTxId: this._claimTxId
803
+ }
804
+ ],
805
+ buildCurrentAction,
806
+ state
807
+ };
808
+ }
809
+ /**
810
+ * @inheritDoc
811
+ * @internal
812
+ */
813
+ async _submitExecutionTransactions(txs, abortSignal, requiredStates, idempotent) {
814
+ if (txs.length === 0)
815
+ throw new Error("Need to submit at least 1 transaction in the array, submitted empty array of transactions!");
816
+ if (idempotent) {
817
+ // Handle idempotent calls
818
+ let idempotencyTriggered = false;
819
+ const txIds = [];
820
+ for (let tx of txs) {
821
+ let parsedTx;
822
+ if (typeof (tx) === "string") {
823
+ try {
824
+ parsedTx = await this.wrapper._chain.deserializeSignedTx(tx);
825
+ }
826
+ catch (e) { }
827
+ try {
828
+ parsedTx = (0, BitcoinUtils_1.parsePsbtTransaction)(tx);
829
+ }
830
+ catch (e) { }
831
+ }
832
+ else {
833
+ parsedTx = tx;
665
834
  }
666
- ];
835
+ if (parsedTx == null) {
836
+ this.logger.debug("_submitExecutionTransactions(): Failed to parse provided execution transaction: ", tx);
837
+ continue;
838
+ }
839
+ if (parsedTx instanceof btc_signer_1.Transaction) {
840
+ // Bitcoin tx
841
+ const btcTx = await this.wrapper._btcRpc.parseTransaction(buffer_1.Buffer.from(parsedTx.toBytes(true)).toString("hex"));
842
+ if (btcTx.txid === this.txId)
843
+ idempotencyTriggered = true;
844
+ txIds.push(btcTx.txid);
845
+ }
846
+ else {
847
+ // SC tx
848
+ if (this.wrapper._chain.getTxId != null) {
849
+ const txId = await this.wrapper._chain.getTxId(parsedTx);
850
+ if (this._commitTxId === txId || this._claimTxId === txId)
851
+ idempotencyTriggered = true;
852
+ txIds.push(txId);
853
+ }
854
+ }
855
+ }
856
+ if (idempotencyTriggered)
857
+ return txIds;
667
858
  }
859
+ if (requiredStates != null && !requiredStates.includes(this._state))
860
+ throw new Error("Swap state has changed before transactions were submitted!");
668
861
  if (this._state === FromBTCSwapState.CLAIM_COMMITED) {
862
+ let psbt;
863
+ if (txs.length !== 1)
864
+ throw new Error("Need to submit exactly 1 signed PSBT!");
865
+ if (typeof (txs[0]) !== "string" && !(txs[0] instanceof btc_signer_1.Transaction))
866
+ throw new Error("Must submit a valid PSBT as hex/base64 string or `@scure/btc-signer` Transaction object!");
867
+ psbt = txs[0];
868
+ return [await this.submitPsbt(psbt)];
869
+ }
870
+ if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
871
+ if (!await this._verifyQuoteValid())
872
+ throw new Error("Quote is already expired!");
669
873
  if (this.getTimeoutTime() < Date.now())
670
874
  throw new Error("Swap address already expired or close to expiry!");
671
- return [
672
- {
673
- name: "Payment",
674
- description: "Send funds to the bitcoin swap address",
675
- chain: "BITCOIN",
676
- txs: [
677
- options?.bitcoinWallet == null ? {
678
- address: this.getAddress(),
679
- amount: Number(this.amount),
680
- hyperlink: this._getHyperlink(),
681
- type: "ADDRESS"
682
- } : {
683
- ...await this.getFundedPsbt(options.bitcoinWallet),
684
- type: "FUNDED_PSBT"
685
- }
686
- ]
875
+ const parsedTxs = [];
876
+ for (let tx of txs) {
877
+ parsedTxs.push(typeof (tx) === "string" ? await this.wrapper._chain.deserializeSignedTx(tx) : tx);
878
+ }
879
+ const txIds = await this.wrapper._chain.sendSignedAndConfirm(parsedTxs, true, abortSignal, false);
880
+ await this.waitTillCommited(abortSignal);
881
+ return txIds;
882
+ }
883
+ if (this._state === FromBTCSwapState.BTC_TX_CONFIRMED) {
884
+ const parsedTxs = [];
885
+ for (let tx of txs) {
886
+ parsedTxs.push(typeof (tx) === "string" ? await this.wrapper._chain.deserializeSignedTx(tx) : tx);
887
+ }
888
+ const txIds = await this.wrapper._chain.sendSignedAndConfirm(parsedTxs, true, abortSignal, false);
889
+ await this.waitTillClaimed(undefined, abortSignal);
890
+ return txIds;
891
+ }
892
+ throw new Error("Invalid swap state for transaction submission!");
893
+ }
894
+ /**
895
+ * @internal
896
+ */
897
+ async _buildSendToAddressOrSignPsbtAction(actionOptions) {
898
+ if (this.address == null)
899
+ throw new Error("Bitcoin swap address not known!");
900
+ if (this.amount == null)
901
+ throw new Error("Bitcoin swap amount not known!");
902
+ if (actionOptions?.bitcoinWallet == null) {
903
+ return {
904
+ type: "SendToAddress",
905
+ name: "Deposit on Bitcoin",
906
+ description: "Send funds to the bitcoin swap address",
907
+ chain: "BITCOIN",
908
+ txs: [{
909
+ type: "BITCOIN_ADDRESS",
910
+ address: this.address,
911
+ hyperlink: this._getHyperlink(),
912
+ amount: (0, TokenAmount_1.toTokenAmount)(this.amount, Token_1.BitcoinTokens.BTC, this.wrapper._prices)
913
+ }],
914
+ waitForTransactions: async (maxWaitTimeSeconds, pollIntervalSeconds, abortSignal) => {
915
+ let btcTxId;
916
+ const abortController = (0, Utils_1.extendAbortController)(abortSignal, maxWaitTimeSeconds, "Timed out waiting for bitcoin transaction");
917
+ try {
918
+ return await this.waitForBitcoinTransaction((txId) => {
919
+ btcTxId = txId;
920
+ abortController.abort();
921
+ }, pollIntervalSeconds, abortController.signal);
922
+ }
923
+ catch (e) {
924
+ if (btcTxId != null)
925
+ return btcTxId;
926
+ throw e;
927
+ }
687
928
  }
688
- ];
929
+ };
689
930
  }
690
- throw new Error("Invalid swap state to obtain execution txns, required PR_CREATED or CLAIM_COMMITED");
931
+ return {
932
+ type: "SignPSBT",
933
+ name: "Deposit on Bitcoin",
934
+ description: "Send funds to the bitcoin swap address",
935
+ chain: "BITCOIN",
936
+ txs: [{
937
+ ...await this.getFundedPsbt(actionOptions.bitcoinWallet, actionOptions?.bitcoinFeeRate),
938
+ type: "FUNDED_PSBT"
939
+ }],
940
+ submitPsbt: async (signedPsbt, idempotent) => {
941
+ return this._submitExecutionTransactions(Array.isArray(signedPsbt) ? signedPsbt : [signedPsbt], undefined, [FromBTCSwapState.CLAIM_COMMITED], idempotent);
942
+ }
943
+ };
944
+ }
945
+ /**
946
+ * @internal
947
+ */
948
+ async _buildWaitBitcoinConfirmationsAction(confirmationDelay, description) {
949
+ return {
950
+ type: "Wait",
951
+ name: "Bitcoin confirmations",
952
+ description: description ?? "Wait for bitcoin transaction to confirm",
953
+ pollTimeSeconds: 10,
954
+ expectedTimeSeconds: confirmationDelay === -1 ? -1 : Math.floor(confirmationDelay / 1000),
955
+ wait: async (maxWaitTimeSeconds, pollIntervalSeconds, abortSignal, btcConfirmationsCallback) => {
956
+ const abortController = (0, Utils_1.extendAbortController)(abortSignal, maxWaitTimeSeconds, "Timed out waiting for bitcoin transaction to confirm");
957
+ await this.waitForBitcoinTransaction(btcConfirmationsCallback, pollIntervalSeconds, abortController.signal);
958
+ }
959
+ };
960
+ }
961
+ /**
962
+ * @internal
963
+ */
964
+ async _buildWaitSettlementAction(maxWaitTillAutomaticSettlementSeconds) {
965
+ return {
966
+ type: "Wait",
967
+ name: "Automatic settlement",
968
+ description: "Wait for automatic settlement by the watchtower",
969
+ pollTimeSeconds: 5,
970
+ expectedTimeSeconds: 10,
971
+ wait: async (maxWaitTimeSeconds, pollIntervalSeconds, abortSignal) => {
972
+ await this.waitTillClaimed(maxWaitTimeSeconds ?? maxWaitTillAutomaticSettlementSeconds ?? 60, abortSignal, pollIntervalSeconds);
973
+ }
974
+ };
975
+ }
976
+ /**
977
+ * @internal
978
+ */
979
+ async _buildInitSmartChainTxAction(actionOptions) {
980
+ return {
981
+ type: "SignSmartChainTransaction",
982
+ name: "Initiate swap",
983
+ description: `Opens up the bitcoin swap address on the ${this.chainIdentifier} side`,
984
+ chain: this.chainIdentifier,
985
+ txs: await this.prepareTransactions(this.txsCommit(actionOptions?.skipChecks)),
986
+ submitTransactions: async (txs, abortSignal, idempotent) => {
987
+ return this._submitExecutionTransactions(txs, abortSignal, [FromBTCSwapState.PR_CREATED, FromBTCSwapState.QUOTE_SOFT_EXPIRED], idempotent);
988
+ },
989
+ requiredSigner: this._getInitiator()
990
+ };
991
+ }
992
+ /**
993
+ * @inheritDoc
994
+ * @internal
995
+ */
996
+ async _buildClaimSmartChainTxAction(actionOptions) {
997
+ const signerAddress = await this.wrapper._getSignerAddress(actionOptions?.manualSettlementSmartChainSigner);
998
+ return {
999
+ type: "SignSmartChainTransaction",
1000
+ name: "Settle manually",
1001
+ description: "Manually settle (claim) the swap on the destination smart chain",
1002
+ chain: this.chainIdentifier,
1003
+ txs: await this.prepareTransactions(this.txsClaim(actionOptions?.manualSettlementSmartChainSigner)),
1004
+ submitTransactions: async (txs, abortSignal, idempotent) => {
1005
+ return this._submitExecutionTransactions(txs, abortSignal, [FromBTCSwapState.BTC_TX_CONFIRMED], idempotent);
1006
+ },
1007
+ requiredSigner: signerAddress ?? this._getInitiator()
1008
+ };
691
1009
  }
692
1010
  /**
693
1011
  * @inheritDoc
@@ -702,26 +1020,26 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
702
1020
  * @param options.maxWaitTillAutomaticSettlementSeconds Maximum time to wait for an automatic settlement after
703
1021
  * the bitcoin transaction is confirmed (defaults to 60 seconds)
704
1022
  */
705
- async getCurrentActions(options) {
706
- if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.CLAIM_COMMITED) {
707
- try {
708
- return await this.txsExecute(options);
709
- }
710
- catch (e) { }
711
- }
712
- if (this.isClaimable()) {
713
- if (this.btcTxConfirmedAt == null ||
714
- options?.maxWaitTillAutomaticSettlementSeconds === 0 ||
715
- (Date.now() - this.btcTxConfirmedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60) * 1000) {
716
- return [{
717
- name: "Claim",
718
- description: "Manually settle (claim) the swap on the destination smart chain",
719
- chain: this.chainIdentifier,
720
- txs: await this.txsClaim(options?.manualSettlementSmartChainSigner)
721
- }];
722
- }
723
- }
724
- return [];
1023
+ async getExecutionAction(options) {
1024
+ const executionStatus = await this._getExecutionStatus(options);
1025
+ return executionStatus.buildCurrentAction(options);
1026
+ }
1027
+ /**
1028
+ * @inheritDoc
1029
+ */
1030
+ async getExecutionStatus(options) {
1031
+ const executionStatus = await this._getExecutionStatus(options);
1032
+ return {
1033
+ steps: executionStatus.steps,
1034
+ currentAction: options?.skipBuildingAction ? undefined : await executionStatus.buildCurrentAction(options),
1035
+ stateInfo: this._getStateInfo(executionStatus.state)
1036
+ };
1037
+ }
1038
+ /**
1039
+ * @inheritDoc
1040
+ */
1041
+ async getExecutionSteps(options) {
1042
+ return (await this._getExecutionStatus(options)).steps;
725
1043
  }
726
1044
  //////////////////////////////
727
1045
  //// Commit
@@ -761,11 +1079,13 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
761
1079
  this.waitTillState(FromBTCSwapState.CLAIM_COMMITED, "gte", abortController.signal).then(() => 0)
762
1080
  ]);
763
1081
  abortController.abort();
764
- if (result === 0)
1082
+ if (result === 0) {
765
1083
  this.logger.debug("waitTillCommited(): Resolved from state changed");
766
- if (result === true)
1084
+ }
1085
+ else if (result != null) {
767
1086
  this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
768
- if (result === false) {
1087
+ }
1088
+ if (result === null) {
769
1089
  this.logger.debug("waitTillCommited(): Resolved from watchdog - signature expired");
770
1090
  if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
771
1091
  await this._saveAndEmit(FromBTCSwapState.QUOTE_EXPIRED);
@@ -773,6 +1093,8 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
773
1093
  return;
774
1094
  }
775
1095
  if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
1096
+ if (typeof (result) === "object" && result.getInitTxId != null && this._commitTxId == null)
1097
+ this._commitTxId = await result.getInitTxId();
776
1098
  await this._saveAndEmit(FromBTCSwapState.CLAIM_COMMITED);
777
1099
  }
778
1100
  }
@@ -880,7 +1202,7 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
880
1202
  * @throws {Error} If swap is in invalid state (must be {@link FromBTCSwapState.BTC_TX_CONFIRMED})
881
1203
  * @throws {Error} If the LP refunded sooner than we were able to claim
882
1204
  */
883
- async waitTillClaimed(maxWaitTimeSeconds, abortSignal) {
1205
+ async waitTillClaimed(maxWaitTimeSeconds, abortSignal, pollIntervalSeconds) {
884
1206
  if (this._state === FromBTCSwapState.CLAIM_CLAIMED)
885
1207
  return Promise.resolve(true);
886
1208
  if (this._state !== FromBTCSwapState.BTC_TX_CONFIRMED)
@@ -897,7 +1219,7 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
897
1219
  let res;
898
1220
  try {
899
1221
  res = await Promise.race([
900
- this.watchdogWaitTillResult(undefined, abortController.signal),
1222
+ this.watchdogWaitTillResult(pollIntervalSeconds, abortController.signal),
901
1223
  this.waitTillState(FromBTCSwapState.CLAIM_CLAIMED, "eq", abortController.signal).then(() => 0),
902
1224
  this.waitTillState(FromBTCSwapState.FAILED, "eq", abortController.signal).then(() => 1),
903
1225
  ]);
@@ -1018,6 +1340,8 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
1018
1340
  async _forciblySetOnchainState(status) {
1019
1341
  switch (status.type) {
1020
1342
  case base_1.SwapCommitStateType.PAID:
1343
+ if (this._commitTxId == null && status.getInitTxId != null)
1344
+ this._commitTxId = await status.getInitTxId();
1021
1345
  if (this._claimTxId == null)
1022
1346
  this._claimTxId = await status.getClaimTxId();
1023
1347
  const txId = buffer_1.Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
@@ -1025,20 +1349,33 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
1025
1349
  this._state = FromBTCSwapState.CLAIM_CLAIMED;
1026
1350
  return true;
1027
1351
  case base_1.SwapCommitStateType.NOT_COMMITED:
1028
- if (this._refundTxId == null && status.getRefundTxId)
1352
+ let changed = false;
1353
+ if (this._commitTxId == null && status.getInitTxId != null) {
1354
+ this._commitTxId = await status.getInitTxId();
1355
+ changed = true;
1356
+ }
1357
+ if (this._refundTxId == null && status.getRefundTxId) {
1029
1358
  this._refundTxId = await status.getRefundTxId();
1359
+ changed = true;
1360
+ }
1030
1361
  if (this._refundTxId != null) {
1031
1362
  this._state = FromBTCSwapState.FAILED;
1032
- return true;
1363
+ changed = true;
1033
1364
  }
1034
- break;
1365
+ return changed;
1035
1366
  case base_1.SwapCommitStateType.EXPIRED:
1367
+ if (this._commitTxId == null && status.getInitTxId != null)
1368
+ this._commitTxId = await status.getInitTxId();
1036
1369
  if (this._refundTxId == null && status.getRefundTxId)
1037
1370
  this._refundTxId = await status.getRefundTxId();
1038
1371
  this._state = this._refundTxId == null ? FromBTCSwapState.QUOTE_EXPIRED : FromBTCSwapState.FAILED;
1039
1372
  return true;
1040
1373
  case base_1.SwapCommitStateType.COMMITED:
1041
1374
  let save = false;
1375
+ if (this._commitTxId == null && status.getInitTxId != null) {
1376
+ this._commitTxId = await status.getInitTxId();
1377
+ save = true;
1378
+ }
1042
1379
  if (this._state !== FromBTCSwapState.CLAIM_COMMITED && this._state !== FromBTCSwapState.BTC_TX_CONFIRMED && this._state !== FromBTCSwapState.EXPIRED) {
1043
1380
  this._state = FromBTCSwapState.CLAIM_COMMITED;
1044
1381
  save = true;
@@ -1048,7 +1385,7 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
1048
1385
  this.btcTxLastChecked = Date.now();
1049
1386
  const res = await this.getBitcoinPayment();
1050
1387
  if (res != null) {
1051
- if (this.txId !== res.txId) {
1388
+ if (this.txId !== res.txId || this.vout !== res.vout || (res.inputAddresses != null && this.senderAddress == null)) {
1052
1389
  if (res.inputAddresses != null)
1053
1390
  this.senderAddress = res.inputAddresses[0];
1054
1391
  this.txId = res.txId;
@@ -1095,7 +1432,7 @@ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
1095
1432
  const res = await this.getBitcoinPayment();
1096
1433
  if (res != null) {
1097
1434
  let shouldSave = false;
1098
- if (this.txId !== res.txId) {
1435
+ if (this.txId !== res.txId || this.vout !== res.vout || (res.inputAddresses != null && this.senderAddress == null)) {
1099
1436
  this.txId = res.txId;
1100
1437
  this.vout = res.vout;
1101
1438
  if (res.inputAddresses != null)