@atomiqlabs/sdk 8.7.7 → 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 (152) 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/bitcoin/coinselect2/accumulative.d.ts +1 -0
  18. package/dist/bitcoin/coinselect2/accumulative.js +1 -1
  19. package/dist/bitcoin/coinselect2/blackjack.d.ts +1 -0
  20. package/dist/bitcoin/coinselect2/blackjack.js +1 -1
  21. package/dist/bitcoin/coinselect2/index.d.ts +3 -2
  22. package/dist/bitcoin/coinselect2/index.js +2 -2
  23. package/dist/bitcoin/coinselect2/utils.d.ts +7 -2
  24. package/dist/bitcoin/coinselect2/utils.js +45 -10
  25. package/dist/bitcoin/wallet/BitcoinWallet.d.ts +8 -25
  26. package/dist/bitcoin/wallet/BitcoinWallet.js +31 -18
  27. package/dist/bitcoin/wallet/IBitcoinWallet.d.ts +40 -2
  28. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +7 -2
  29. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +10 -4
  30. package/dist/events/UnifiedSwapEventListener.d.ts +4 -3
  31. package/dist/events/UnifiedSwapEventListener.js +8 -2
  32. package/dist/http/HttpUtils.d.ts +4 -2
  33. package/dist/http/HttpUtils.js +10 -4
  34. package/dist/http/paramcoders/client/StreamingFetchPromise.d.ts +2 -1
  35. package/dist/http/paramcoders/client/StreamingFetchPromise.js +3 -2
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.js +1 -0
  38. package/dist/intermediaries/IntermediaryDiscovery.d.ts +7 -2
  39. package/dist/intermediaries/IntermediaryDiscovery.js +4 -4
  40. package/dist/intermediaries/apis/IntermediaryAPI.d.ts +182 -15
  41. package/dist/intermediaries/apis/IntermediaryAPI.js +192 -31
  42. package/dist/intermediaries/auth/SignedKeyBasedAuth.d.ts +14 -0
  43. package/dist/intermediaries/auth/SignedKeyBasedAuth.js +68 -0
  44. package/dist/storage/IUnifiedStorage.d.ts +45 -3
  45. package/dist/storage/UnifiedSwapStorage.d.ts +8 -2
  46. package/dist/storage/UnifiedSwapStorage.js +46 -8
  47. package/dist/swapper/Swapper.d.ts +77 -4
  48. package/dist/swapper/Swapper.js +117 -25
  49. package/dist/swapper/SwapperUtils.d.ts +18 -2
  50. package/dist/swapper/SwapperUtils.js +39 -1
  51. package/dist/swaps/ISwap.d.ts +70 -9
  52. package/dist/swaps/ISwap.js +28 -6
  53. package/dist/swaps/ISwapWrapper.d.ts +11 -1
  54. package/dist/swaps/ISwapWrapper.js +23 -3
  55. package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +1 -1
  56. package/dist/swaps/escrow_swaps/IEscrowSwap.js +4 -2
  57. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +2 -1
  58. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +2 -2
  59. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +3 -1
  60. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +3 -2
  61. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +47 -31
  62. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +201 -67
  63. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +3 -1
  64. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +6 -6
  65. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +82 -15
  66. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +304 -98
  67. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +3 -1
  68. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +6 -6
  69. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +75 -42
  70. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +424 -87
  71. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +3 -1
  72. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +7 -7
  73. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +54 -11
  74. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +214 -41
  75. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +2 -1
  76. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +7 -8
  77. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +3 -1
  78. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +5 -5
  79. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +85 -22
  80. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +299 -56
  81. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +41 -7
  82. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +183 -58
  83. package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +53 -12
  84. package/dist/swaps/trusted/ln/LnForGasSwap.js +163 -49
  85. package/dist/swaps/trusted/ln/LnForGasWrapper.js +1 -2
  86. package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +14 -13
  87. package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +30 -47
  88. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +3 -1
  89. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +4 -4
  90. package/dist/types/SwapExecutionAction.d.ts +141 -34
  91. package/dist/types/SwapExecutionAction.js +104 -0
  92. package/dist/types/SwapExecutionStep.d.ts +144 -0
  93. package/dist/types/SwapExecutionStep.js +87 -0
  94. package/dist/types/TokenAmount.d.ts +6 -0
  95. package/dist/types/TokenAmount.js +26 -1
  96. package/dist/utils/BitcoinUtils.d.ts +4 -0
  97. package/dist/utils/BitcoinUtils.js +73 -1
  98. package/dist/utils/BitcoinWalletUtils.d.ts +2 -2
  99. package/dist/utils/Utils.d.ts +3 -1
  100. package/dist/utils/Utils.js +7 -1
  101. package/package.json +7 -4
  102. package/src/api/ApiEndpoints.ts +427 -0
  103. package/src/api/ApiParser.ts +138 -0
  104. package/src/api/ApiTypes.ts +229 -0
  105. package/src/api/SerializedAction.ts +97 -0
  106. package/src/api/SwapperApi.ts +545 -0
  107. package/src/api/index.ts +5 -0
  108. package/src/bitcoin/coinselect2/accumulative.ts +2 -1
  109. package/src/bitcoin/coinselect2/blackjack.ts +2 -1
  110. package/src/bitcoin/coinselect2/index.ts +5 -4
  111. package/src/bitcoin/coinselect2/utils.ts +55 -14
  112. package/src/bitcoin/wallet/BitcoinWallet.ts +69 -57
  113. package/src/bitcoin/wallet/IBitcoinWallet.ts +44 -3
  114. package/src/bitcoin/wallet/SingleAddressBitcoinWallet.ts +12 -4
  115. package/src/events/UnifiedSwapEventListener.ts +11 -3
  116. package/src/http/HttpUtils.ts +10 -4
  117. package/src/http/paramcoders/client/StreamingFetchPromise.ts +4 -2
  118. package/src/index.ts +1 -0
  119. package/src/intermediaries/IntermediaryDiscovery.ts +9 -2
  120. package/src/intermediaries/apis/IntermediaryAPI.ts +335 -35
  121. package/src/intermediaries/auth/SignedKeyBasedAuth.ts +69 -0
  122. package/src/storage/IUnifiedStorage.ts +45 -4
  123. package/src/storage/UnifiedSwapStorage.ts +42 -8
  124. package/src/swapper/Swapper.ts +165 -24
  125. package/src/swapper/SwapperUtils.ts +42 -2
  126. package/src/swaps/ISwap.ts +88 -16
  127. package/src/swaps/ISwapWrapper.ts +28 -3
  128. package/src/swaps/escrow_swaps/IEscrowSwap.ts +5 -3
  129. package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +3 -1
  130. package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +4 -1
  131. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +264 -67
  132. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +6 -4
  133. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +390 -89
  134. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +6 -4
  135. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +548 -94
  136. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +7 -5
  137. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +276 -45
  138. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +7 -6
  139. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +5 -3
  140. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +413 -64
  141. package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +239 -61
  142. package/src/swaps/trusted/ln/LnForGasSwap.ts +211 -47
  143. package/src/swaps/trusted/ln/LnForGasWrapper.ts +1 -2
  144. package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +32 -51
  145. package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +5 -3
  146. package/src/types/SwapExecutionAction.ts +266 -43
  147. package/src/types/SwapExecutionStep.ts +224 -0
  148. package/src/types/TokenAmount.ts +36 -2
  149. package/src/utils/BitcoinUtils.ts +73 -0
  150. package/src/utils/BitcoinWalletUtils.ts +2 -2
  151. package/src/utils/Utils.ts +10 -1
  152. package/src/intermediaries/apis/TrustedIntermediaryAPI.ts +0 -258
@@ -15,7 +15,7 @@ import {
15
15
  getTxoHash, toBigInt
16
16
  } from "../../../../utils/Utils";
17
17
  import {
18
- fromOutputScript,
18
+ fromOutputScript, getSenderAddress, getVoutIndex,
19
19
  parsePsbtTransaction,
20
20
  toOutputScript,
21
21
  } from "../../../../utils/BitcoinUtils";
@@ -33,8 +33,20 @@ import {IAddressSwap} from "../../../IAddressSwap";
33
33
  import {TokenAmount, toTokenAmount} from "../../../../types/TokenAmount";
34
34
  import {BitcoinTokens, BtcToken, SCToken} from "../../../../types/Token";
35
35
  import {getLogger, LoggerType} from "../../../../utils/Logger";
36
+ import {timeoutPromise} from "../../../../utils/TimeoutUtils";
36
37
  import {toBitcoinWallet} from "../../../../utils/BitcoinWalletUtils";
37
- import {SwapExecutionAction} from "../../../../types/SwapExecutionAction";
38
+ import {
39
+ SwapExecutionActionSendToAddress,
40
+ SwapExecutionActionSignPSBT,
41
+ SwapExecutionActionSignSmartChainTx,
42
+ SwapExecutionActionWait
43
+ } from "../../../../types/SwapExecutionAction";
44
+ import {
45
+ SwapExecutionStepPayment,
46
+ SwapExecutionStepSettlement,
47
+ SwapExecutionStepSetup
48
+ } from "../../../../types/SwapExecutionStep";
49
+ import {SwapStateInfo} from "../../../../types/SwapStateInfo";
38
50
 
39
51
  /**
40
52
  * State enum for legacy escrow based Bitcoin -> Smart chain swaps.
@@ -264,6 +276,32 @@ export class FromBTCSwap<T extends ChainType = ChainType>
264
276
  return this.txId ?? null;
265
277
  }
266
278
 
279
+ private async _setSubmittedBitcoinTx(txId: string, psbt?: Transaction): Promise<void> {
280
+ let changed = false;
281
+ if(this.txId!==txId) {
282
+ this.txId = txId;
283
+ changed = true;
284
+ }
285
+
286
+ const submittedVout = this.address==null || this.amount==null || psbt==null
287
+ ? undefined
288
+ : getVoutIndex(psbt, this.wrapper._options.bitcoinNetwork, this.address, this.amount);
289
+ if(submittedVout!=null && this.vout!==submittedVout) {
290
+ this.vout = submittedVout;
291
+ changed = true;
292
+ }
293
+
294
+ const submittedSenderAddress = psbt==null
295
+ ? undefined
296
+ : getSenderAddress(psbt, this.wrapper._options.bitcoinNetwork);
297
+ if(submittedSenderAddress!=null && this.senderAddress!==submittedSenderAddress) {
298
+ this.senderAddress = submittedSenderAddress;
299
+ changed = true;
300
+ }
301
+
302
+ if(changed) await this._saveAndEmit();
303
+ }
304
+
267
305
  /**
268
306
  * Returns timeout time (in UNIX milliseconds) when the on-chain address will expire and no funds should be sent
269
307
  * to that address anymore
@@ -507,7 +545,12 @@ export class FromBTCSwap<T extends ChainType = ChainType>
507
545
  requiredConfirmations = this.inferRequiredConfirmationsCount(btcTx, vout);
508
546
  }
509
547
 
510
- if(btcTx!=null && (btcTx.txid!==this.txId || (this.requiredConfirmations==null && requiredConfirmations!=null))) {
548
+ if(btcTx!=null && (
549
+ btcTx.txid!==this.txId ||
550
+ this.vout==null ||
551
+ this.senderAddress==null ||
552
+ (this.requiredConfirmations==null && requiredConfirmations!=null)
553
+ )) {
511
554
  this.txId = btcTx.txid;
512
555
  this.vout = vout;
513
556
  this.requiredConfirmations = requiredConfirmations;
@@ -571,7 +614,7 @@ export class FromBTCSwap<T extends ChainType = ChainType>
571
614
  _bitcoinWallet: IBitcoinWallet | MinimalBitcoinWalletInterface,
572
615
  feeRate?: number,
573
616
  additionalOutputs?: ({amount: bigint, outputScript: Uint8Array} | {amount: bigint, address: string})[]
574
- ): Promise<{psbt: Transaction, psbtHex: string, psbtBase64: string, signInputs: number[]}> {
617
+ ): Promise<{psbt: Transaction, psbtHex: string, psbtBase64: string, signInputs: number[], feeRate: number}> {
575
618
  if(this.address==null) throw new Error("Cannot create funded PSBT, because the address is not known! This can happen after a swap is recovered.");
576
619
 
577
620
  let bitcoinWallet: IBitcoinWallet;
@@ -611,7 +654,8 @@ export class FromBTCSwap<T extends ChainType = ChainType>
611
654
  psbt,
612
655
  psbtHex: serializedPsbt.toString("hex"),
613
656
  psbtBase64: serializedPsbt.toString("base64"),
614
- signInputs
657
+ signInputs,
658
+ feeRate
615
659
  };
616
660
  }
617
661
 
@@ -625,6 +669,8 @@ export class FromBTCSwap<T extends ChainType = ChainType>
625
669
  ) {
626
670
  if(this._state!==FromBTCSwapState.CLAIM_COMMITED)
627
671
  throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
672
+ if(this.txId!=null)
673
+ throw new Error("Bitcoin transaction already submitted for this swap!");
628
674
  return this._getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs);
629
675
  }
630
676
 
@@ -638,6 +684,8 @@ export class FromBTCSwap<T extends ChainType = ChainType>
638
684
  const psbt = parsePsbtTransaction(_psbt);
639
685
  if(this._state!==FromBTCSwapState.CLAIM_COMMITED)
640
686
  throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
687
+ if(this.txId!=null)
688
+ throw new Error("Bitcoin transaction already submitted for this swap!");
641
689
 
642
690
  //Ensure not expired
643
691
  if(this.getTimeoutTime()<Date.now()) {
@@ -655,7 +703,9 @@ export class FromBTCSwap<T extends ChainType = ChainType>
655
703
 
656
704
  if(!psbt.isFinal) psbt.finalize();
657
705
 
658
- return await this.wrapper._btcRpc.sendRawTransaction(Buffer.from(psbt.toBytes(true, true)).toString("hex"));
706
+ const txId = await this.wrapper._btcRpc.sendRawTransaction(Buffer.from(psbt.toBytes(true, true)).toString("hex"));
707
+ await this._setSubmittedBitcoinTx(txId, psbt);
708
+ return txId;
659
709
  }
660
710
 
661
711
  /**
@@ -677,6 +727,8 @@ export class FromBTCSwap<T extends ChainType = ChainType>
677
727
 
678
728
  if(this._state!==FromBTCSwapState.CLAIM_COMMITED)
679
729
  throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
730
+ if(this.txId!=null)
731
+ throw new Error("Bitcoin transaction already submitted for this swap!");
680
732
 
681
733
  //Ensure not expired
682
734
  if(this.getTimeoutTime()<Date.now()) {
@@ -684,7 +736,9 @@ export class FromBTCSwap<T extends ChainType = ChainType>
684
736
  }
685
737
 
686
738
  if(isIBitcoinWallet(wallet)) {
687
- return await wallet.sendTransaction(this.address, this.amount, feeRate);
739
+ const txId = await wallet.sendTransaction(this.address, this.amount, feeRate);
740
+ await this._setSubmittedBitcoinTx(txId);
741
+ return txId;
688
742
  } else {
689
743
  const {psbt, psbtHex, psbtBase64, signInputs} = await this.getFundedPsbt(wallet, feeRate);
690
744
  const signedPsbt = await wallet.signPsbt({
@@ -742,7 +796,7 @@ export class FromBTCSwap<T extends ChainType = ChainType>
742
796
  if(wallet!=null) {
743
797
  const bitcoinPaymentSent = await this.getBitcoinPayment();
744
798
 
745
- if(bitcoinPaymentSent==null) {
799
+ if(bitcoinPaymentSent==null && this.txId==null) {
746
800
  //Send btc tx
747
801
  const txId = await this.sendBitcoinTransaction(wallet, options?.feeRate);
748
802
  if(callbacks?.onSourceTransactionSent!=null) callbacks.onSourceTransactionSent(txId);
@@ -766,74 +820,421 @@ export class FromBTCSwap<T extends ChainType = ChainType>
766
820
  }
767
821
 
768
822
  /**
769
- * @inheritDoc
770
- *
771
- * @param options.bitcoinFeeRate Optional fee rate to use for the created Bitcoin transaction
772
- * @param options.bitcoinWallet Bitcoin wallet to use, when provided the function returns a funded
773
- * psbt (`"FUNDED_PSBT"`), if not passed just a bitcoin receive address is returned (`"ADDRESS"`)
774
- * @param options.skipChecks Skip checks like making sure init signature is still valid and swap
775
- * wasn't commited yet (this is handled on swap creation, if you commit right after quoting, you
776
- * can use `skipChecks=true`)
777
- *
778
- * @throws {Error} if the swap or quote is expired, or if triggered in invalid state
823
+ * @internal
779
824
  */
780
- async txsExecute(options?: {
781
- bitcoinFeeRate?: number,
782
- bitcoinWallet?: MinimalBitcoinWalletInterface,
783
- skipChecks?: boolean
825
+ protected async _getExecutionStatus(options?: {
826
+ maxWaitTillAutomaticSettlementSeconds?: number
784
827
  }) {
785
- if(this._state===FromBTCSwapState.PR_CREATED) {
786
- if(!await this._verifyQuoteValid()) throw new Error("Quote already expired or close to expiry!");
787
- if(this.getTimeoutTime()<Date.now()) throw new Error("Swap address already expired or close to expiry!");
788
- return [
828
+ const state = this._state;
829
+ const now = Date.now();
830
+ const timeoutTime = this.getTimeoutTime();
831
+
832
+ let confirmations: {
833
+ current: number,
834
+ target: number,
835
+ etaSeconds: number
836
+ } | undefined;
837
+ let bitcoinTxId: string | undefined;
838
+
839
+ let destinationSetupStatus: SwapExecutionStepSetup<T["ChainId"]>["status"] = "awaiting";
840
+ let bitcoinPaymentStatus: SwapExecutionStepPayment<"BITCOIN">["status"] = "inactive";
841
+ let destinationSettlementStatus: SwapExecutionStepSettlement<T["ChainId"], "awaiting_automatic" | "awaiting_manual">["status"] = "inactive";
842
+ let buildCurrentAction: (actionOptions?: {
843
+ bitcoinFeeRate?: number,
844
+ bitcoinWallet?: MinimalBitcoinWalletInterface,
845
+ skipChecks?: boolean,
846
+ manualSettlementSmartChainSigner?: string | T["Signer"] | T["NativeSigner"],
847
+ }) => Promise<
848
+ SwapExecutionActionSendToAddress<false> |
849
+ SwapExecutionActionSignPSBT<"FUNDED_PSBT"> |
850
+ SwapExecutionActionWait<"BITCOIN_CONFS" | "SETTLEMENT"> |
851
+ SwapExecutionActionSignSmartChainTx<T> |
852
+ undefined
853
+ > = async () => undefined;
854
+
855
+ switch(state) {
856
+ case FromBTCSwapState.PR_CREATED: {
857
+ const quoteValid = await this._verifyQuoteValid();
858
+ destinationSetupStatus = quoteValid && timeoutTime>=now ? "awaiting" : "soft_expired";
859
+ if(quoteValid && timeoutTime>=now) {
860
+ buildCurrentAction = this._buildInitSmartChainTxAction.bind(this);
861
+ }
862
+ break;
863
+ }
864
+ case FromBTCSwapState.QUOTE_SOFT_EXPIRED:
865
+ destinationSetupStatus = "soft_expired";
866
+ break;
867
+ case FromBTCSwapState.QUOTE_EXPIRED:
868
+ destinationSetupStatus = "expired";
869
+ break;
870
+ case FromBTCSwapState.CLAIM_COMMITED:
871
+ case FromBTCSwapState.EXPIRED:
872
+ case FromBTCSwapState.FAILED:
873
+ const bitcoinPayment = this.address==null ? null : await this.getBitcoinPayment();
874
+ bitcoinTxId = bitcoinPayment?.txId;
875
+ let bitcoinConfirmationDelay: number | undefined;
876
+ if(bitcoinPayment!=null && bitcoinPayment.confirmations < bitcoinPayment.targetConfirmations) {
877
+ const tx = await this.wrapper._btcRpc.getTransaction(bitcoinPayment.txId);
878
+ const result = tx==null
879
+ ? null
880
+ : await this.wrapper._btcRpc.getConfirmationDelay(tx, bitcoinPayment.targetConfirmations);
881
+ bitcoinConfirmationDelay = result ?? -1;
882
+ }
883
+
884
+ destinationSetupStatus = "completed";
885
+ if(bitcoinPayment==null) {
886
+ if(this.txId!=null) {
887
+ bitcoinPaymentStatus = state===FromBTCSwapState.FAILED ? "expired" : "received";
888
+ if(state!==FromBTCSwapState.FAILED) {
889
+ buildCurrentAction = this._buildWaitBitcoinConfirmationsAction.bind(this, -1, "Wait for bitcoin transaction to be picked up by the RPC and confirmed.");
890
+ }
891
+ } else {
892
+ bitcoinPaymentStatus = "awaiting";
893
+ if(state===FromBTCSwapState.EXPIRED) bitcoinPaymentStatus = "soft_expired";
894
+ if(state===FromBTCSwapState.FAILED) bitcoinPaymentStatus = "expired";
895
+ if(
896
+ state===FromBTCSwapState.CLAIM_COMMITED && timeoutTime>=now &&
897
+ this.address!=null && this.amount!=null
898
+ ) {
899
+ buildCurrentAction = this._buildSendToAddressOrSignPsbtAction.bind(this);
900
+ }
901
+ }
902
+ } else if(bitcoinPayment.confirmations >= bitcoinPayment.targetConfirmations) {
903
+ bitcoinPaymentStatus = "confirmed";
904
+ if(state!==FromBTCSwapState.FAILED) {
905
+ buildCurrentAction = this._buildWaitBitcoinConfirmationsAction.bind(this, bitcoinConfirmationDelay ?? -1, undefined);
906
+ }
907
+ } else {
908
+ bitcoinPaymentStatus = "received";
909
+ confirmations = {
910
+ current: bitcoinPayment.confirmations,
911
+ target: bitcoinPayment.targetConfirmations,
912
+ etaSeconds: bitcoinConfirmationDelay ?? -1
913
+ };
914
+ if(state!==FromBTCSwapState.FAILED) {
915
+ buildCurrentAction = this._buildWaitBitcoinConfirmationsAction.bind(this, bitcoinConfirmationDelay ?? -1, undefined);
916
+ }
917
+ }
918
+ destinationSettlementStatus = state===FromBTCSwapState.FAILED ? "expired" : "inactive";
919
+ break;
920
+ case FromBTCSwapState.BTC_TX_CONFIRMED:
921
+ destinationSetupStatus = "completed";
922
+ bitcoinPaymentStatus = "confirmed";
923
+ if(
924
+ this.btcTxConfirmedAt==null ||
925
+ options?.maxWaitTillAutomaticSettlementSeconds===0 ||
926
+ (now - this.btcTxConfirmedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60)*1000
927
+ ) {
928
+ destinationSettlementStatus = "awaiting_manual";
929
+ buildCurrentAction = this._buildClaimSmartChainTxAction.bind(this);
930
+ } else {
931
+ destinationSettlementStatus = "awaiting_automatic";
932
+ buildCurrentAction = this._buildWaitSettlementAction.bind(this, options?.maxWaitTillAutomaticSettlementSeconds);
933
+ }
934
+ break;
935
+ case FromBTCSwapState.CLAIM_CLAIMED:
936
+ destinationSetupStatus = "completed";
937
+ bitcoinPaymentStatus = "confirmed";
938
+ destinationSettlementStatus = "settled";
939
+ break;
940
+ }
941
+
942
+ if(bitcoinPaymentStatus==="confirmed") {
943
+ const requiredConfirmations = this.getRequiredConfirmationsCount();
944
+ if(!Number.isNaN(requiredConfirmations)) {
945
+ confirmations = {
946
+ current: requiredConfirmations,
947
+ target: requiredConfirmations,
948
+ etaSeconds: 0
949
+ };
950
+ }
951
+ }
952
+
953
+ return {
954
+ steps: [
789
955
  {
790
- name: "Commit" as const,
791
- description: `Opens up the bitcoin swap address on the ${this.chainIdentifier} side`,
956
+ type: "Setup",
957
+ side: "destination",
792
958
  chain: this.chainIdentifier,
793
- txs: await this.txsCommit(options?.skipChecks)
959
+ title: "Open Bitcoin swap address",
960
+ description: `Create the escrow on the ${this.chainIdentifier} side to open the Bitcoin swap address`,
961
+ status: destinationSetupStatus,
962
+ setupTxId: this._commitTxId
794
963
  },
795
964
  {
796
- name: "Payment" as const,
797
- description: "Send funds to the bitcoin swap address",
798
- chain: "BITCOIN" as const,
799
- txs: [
800
- options?.bitcoinWallet==null ? {
801
- address: this.address ?? "",
802
- amount: Number(this.amount),
803
- hyperlink: this._getHyperlink(),
804
- type: "ADDRESS" as const
805
- } : {
806
- ...await this.getFundedPsbt(options.bitcoinWallet),
807
- type: "FUNDED_PSBT" as const
808
- }
809
- ]
965
+ type: "Payment",
966
+ side: "source",
967
+ chain: "BITCOIN",
968
+ title: "Bitcoin payment",
969
+ description: "Send Bitcoin to the swap address and wait for the transaction to confirm",
970
+ status: bitcoinPaymentStatus,
971
+ confirmations,
972
+ initTxId: this.txId ?? bitcoinTxId,
973
+ settleTxId: this.txId
974
+ },
975
+ {
976
+ type: "Settlement",
977
+ side: "destination",
978
+ chain: this.chainIdentifier,
979
+ title: "Destination settlement",
980
+ description: `Wait for automatic settlement on the ${this.chainIdentifier} side, or settle manually if it takes too long`,
981
+ status: destinationSettlementStatus,
982
+ initTxId: this._commitTxId,
983
+ settleTxId: this._claimTxId
984
+ }
985
+ ] as [
986
+ SwapExecutionStepSetup<T["ChainId"]>,
987
+ SwapExecutionStepPayment<"BITCOIN">,
988
+ SwapExecutionStepSettlement<T["ChainId"], "awaiting_automatic" | "awaiting_manual">
989
+ ],
990
+ buildCurrentAction,
991
+ state
992
+ };
993
+ }
994
+
995
+ /**
996
+ * @inheritDoc
997
+ * @internal
998
+ */
999
+ async _submitExecutionTransactions(txs: (T["SignedTXType"] | Transaction | string)[], abortSignal?: AbortSignal, requiredStates?: FromBTCSwapState[], idempotent?: boolean): Promise<string[]> {
1000
+ if(txs.length===0) throw new Error("Need to submit at least 1 transaction in the array, submitted empty array of transactions!");
1001
+
1002
+ if(idempotent) {
1003
+ // Handle idempotent calls
1004
+ let idempotencyTriggered = false;
1005
+ const txIds: string[] = [];
1006
+ for(let tx of txs) {
1007
+ let parsedTx: T["SignedTXType"] | Transaction | undefined;
1008
+ if(typeof(tx)==="string") {
1009
+ try {
1010
+ parsedTx = await this.wrapper._chain.deserializeSignedTx(tx);
1011
+ } catch (e) {}
1012
+ try {
1013
+ parsedTx = parsePsbtTransaction(tx);
1014
+ } catch (e) {}
1015
+ } else {
1016
+ parsedTx = tx;
1017
+ }
1018
+
1019
+ if(parsedTx==null) {
1020
+ this.logger.debug("_submitExecutionTransactions(): Failed to parse provided execution transaction: ", tx);
1021
+ continue;
810
1022
  }
811
- ];
1023
+
1024
+ if(parsedTx instanceof Transaction) {
1025
+ // Bitcoin tx
1026
+ const btcTx = await this.wrapper._btcRpc.parseTransaction(Buffer.from(parsedTx.toBytes(true)).toString("hex"));
1027
+ if(btcTx.txid===this.txId) idempotencyTriggered = true;
1028
+ txIds.push(btcTx.txid);
1029
+ } else {
1030
+ // SC tx
1031
+ if(this.wrapper._chain.getTxId!=null) {
1032
+ const txId = await this.wrapper._chain.getTxId(parsedTx);
1033
+ if(this._commitTxId===txId || this._claimTxId===txId) idempotencyTriggered = true;
1034
+ txIds.push(txId);
1035
+ }
1036
+ }
1037
+ }
1038
+ if(idempotencyTriggered) return txIds;
812
1039
  }
813
1040
 
1041
+ if(requiredStates!=null && !requiredStates.includes(this._state)) throw new Error("Swap state has changed before transactions were submitted!");
1042
+
814
1043
  if(this._state===FromBTCSwapState.CLAIM_COMMITED) {
1044
+ let psbt: string | Transaction;
1045
+ if(txs.length!==1) throw new Error("Need to submit exactly 1 signed PSBT!");
1046
+ if(typeof(txs[0])!=="string" && !(txs[0] instanceof Transaction))
1047
+ throw new Error("Must submit a valid PSBT as hex/base64 string or `@scure/btc-signer` Transaction object!");
1048
+ psbt = txs[0];
1049
+ return [await this.submitPsbt(psbt)];
1050
+ }
1051
+
1052
+ if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
1053
+ if(!await this._verifyQuoteValid()) throw new Error("Quote is already expired!");
815
1054
  if(this.getTimeoutTime()<Date.now()) throw new Error("Swap address already expired or close to expiry!");
816
- return [
817
- {
818
- name: "Payment" as const,
819
- description: "Send funds to the bitcoin swap address",
820
- chain: "BITCOIN" as const,
821
- txs: [
822
- options?.bitcoinWallet==null ? {
823
- address: this.getAddress(),
824
- amount: Number(this.amount),
825
- hyperlink: this._getHyperlink(),
826
- type: "ADDRESS" as const
827
- } : {
828
- ...await this.getFundedPsbt(options.bitcoinWallet),
829
- type: "FUNDED_PSBT" as const
830
- }
831
- ]
1055
+
1056
+ const parsedTxs: T["SignedTXType"][] = [];
1057
+ for(let tx of txs) {
1058
+ parsedTxs.push(typeof(tx)==="string" ? await this.wrapper._chain.deserializeSignedTx(tx) : tx);
1059
+ }
1060
+ const txIds = await this.wrapper._chain.sendSignedAndConfirm(parsedTxs, true, abortSignal, false);
1061
+ await this.waitTillCommited(abortSignal);
1062
+ return txIds;
1063
+ }
1064
+
1065
+ if(this._state===FromBTCSwapState.BTC_TX_CONFIRMED) {
1066
+ const parsedTxs: T["SignedTXType"][] = [];
1067
+ for(let tx of txs) {
1068
+ parsedTxs.push(typeof(tx)==="string" ? await this.wrapper._chain.deserializeSignedTx(tx) : tx);
1069
+ }
1070
+ const txIds = await this.wrapper._chain.sendSignedAndConfirm(parsedTxs, true, abortSignal, false);
1071
+ await this.waitTillClaimed(undefined, abortSignal);
1072
+ return txIds;
1073
+ }
1074
+
1075
+ throw new Error("Invalid swap state for transaction submission!");
1076
+ }
1077
+
1078
+ /**
1079
+ * @internal
1080
+ */
1081
+ private async _buildSendToAddressOrSignPsbtAction(actionOptions?: {
1082
+ bitcoinFeeRate?: number,
1083
+ bitcoinWallet?: MinimalBitcoinWalletInterface,
1084
+ }): Promise<
1085
+ SwapExecutionActionSendToAddress<false> |
1086
+ SwapExecutionActionSignPSBT<"FUNDED_PSBT">
1087
+ > {
1088
+ if(this.address==null) throw new Error("Bitcoin swap address not known!");
1089
+ if(this.amount==null) throw new Error("Bitcoin swap amount not known!");
1090
+
1091
+ if(actionOptions?.bitcoinWallet==null) {
1092
+ return {
1093
+ type: "SendToAddress",
1094
+ name: "Deposit on Bitcoin",
1095
+ description: "Send funds to the bitcoin swap address",
1096
+ chain: "BITCOIN",
1097
+ txs: [{
1098
+ type: "BITCOIN_ADDRESS",
1099
+ address: this.address,
1100
+ hyperlink: this._getHyperlink(),
1101
+ amount: toTokenAmount(this.amount, BitcoinTokens.BTC, this.wrapper._prices)
1102
+ }],
1103
+ waitForTransactions: async (
1104
+ maxWaitTimeSeconds?: number, pollIntervalSeconds?: number, abortSignal?: AbortSignal
1105
+ ) => {
1106
+ let btcTxId: string | undefined;
1107
+ const abortController = extendAbortController(
1108
+ abortSignal, maxWaitTimeSeconds, "Timed out waiting for bitcoin transaction"
1109
+ );
1110
+
1111
+ try {
1112
+ return await this.waitForBitcoinTransaction(
1113
+ (txId) => {
1114
+ btcTxId = txId;
1115
+ abortController.abort();
1116
+ },
1117
+ pollIntervalSeconds,
1118
+ abortController.signal
1119
+ );
1120
+ } catch (e) {
1121
+ if(btcTxId!=null) return btcTxId;
1122
+ throw e;
1123
+ }
832
1124
  }
833
- ];
1125
+ } as SwapExecutionActionSendToAddress<false>;
834
1126
  }
835
1127
 
836
- throw new Error("Invalid swap state to obtain execution txns, required PR_CREATED or CLAIM_COMMITED");
1128
+ return {
1129
+ type: "SignPSBT",
1130
+ name: "Deposit on Bitcoin",
1131
+ description: "Send funds to the bitcoin swap address",
1132
+ chain: "BITCOIN",
1133
+ txs: [{
1134
+ ...await this.getFundedPsbt(actionOptions.bitcoinWallet, actionOptions?.bitcoinFeeRate),
1135
+ type: "FUNDED_PSBT"
1136
+ }],
1137
+ submitPsbt: async (signedPsbt: string | Transaction | (string | Transaction)[], idempotent?: boolean) => {
1138
+ return this._submitExecutionTransactions(
1139
+ Array.isArray(signedPsbt) ? signedPsbt : [signedPsbt],
1140
+ undefined,
1141
+ [FromBTCSwapState.CLAIM_COMMITED],
1142
+ idempotent
1143
+ );
1144
+ }
1145
+ } as SwapExecutionActionSignPSBT<"FUNDED_PSBT">;
1146
+ }
1147
+
1148
+ /**
1149
+ * @internal
1150
+ */
1151
+ private async _buildWaitBitcoinConfirmationsAction(confirmationDelay: number, description?: string): Promise<SwapExecutionActionWait<"BITCOIN_CONFS">> {
1152
+ return {
1153
+ type: "Wait",
1154
+ name: "Bitcoin confirmations",
1155
+ description: description ?? "Wait for bitcoin transaction to confirm",
1156
+ pollTimeSeconds: 10,
1157
+ expectedTimeSeconds: confirmationDelay===-1 ? -1 : Math.floor(confirmationDelay/1000),
1158
+ wait: async (
1159
+ maxWaitTimeSeconds?: number, pollIntervalSeconds?: number, abortSignal?: AbortSignal,
1160
+ btcConfirmationsCallback?: (txId?: string, confirmations?: number, targetConfirmations?: number, txEtaMs?: number) => void
1161
+ ) => {
1162
+ const abortController = extendAbortController(
1163
+ abortSignal, maxWaitTimeSeconds, "Timed out waiting for bitcoin transaction to confirm"
1164
+ );
1165
+ await this.waitForBitcoinTransaction(btcConfirmationsCallback, pollIntervalSeconds, abortController.signal);
1166
+ }
1167
+ } as SwapExecutionActionWait<"BITCOIN_CONFS">;
1168
+ }
1169
+
1170
+ /**
1171
+ * @internal
1172
+ */
1173
+ private async _buildWaitSettlementAction(maxWaitTillAutomaticSettlementSeconds?: number): Promise<SwapExecutionActionWait<"SETTLEMENT">> {
1174
+ return {
1175
+ type: "Wait",
1176
+ name: "Automatic settlement",
1177
+ description: "Wait for automatic settlement by the watchtower",
1178
+ pollTimeSeconds: 5,
1179
+ expectedTimeSeconds: 10,
1180
+ wait: async (
1181
+ maxWaitTimeSeconds?: number, pollIntervalSeconds?: number, abortSignal?: AbortSignal
1182
+ ) => {
1183
+ await this.waitTillClaimed(maxWaitTimeSeconds ?? maxWaitTillAutomaticSettlementSeconds ?? 60, abortSignal, pollIntervalSeconds);
1184
+ }
1185
+ } as SwapExecutionActionWait<"SETTLEMENT">;
1186
+ }
1187
+
1188
+ /**
1189
+ * @internal
1190
+ */
1191
+ private async _buildInitSmartChainTxAction(actionOptions?: {
1192
+ skipChecks?: boolean,
1193
+ }): Promise<SwapExecutionActionSignSmartChainTx<T>> {
1194
+ return {
1195
+ type: "SignSmartChainTransaction",
1196
+ name: "Initiate swap",
1197
+ description: `Opens up the bitcoin swap address on the ${this.chainIdentifier} side`,
1198
+ chain: this.chainIdentifier,
1199
+ txs: await this.prepareTransactions(this.txsCommit(actionOptions?.skipChecks)),
1200
+ submitTransactions: async (txs: (T["SignedTXType"] | string)[], abortSignal?: AbortSignal, idempotent?: boolean) => {
1201
+ return this._submitExecutionTransactions(
1202
+ txs,
1203
+ abortSignal,
1204
+ [FromBTCSwapState.PR_CREATED, FromBTCSwapState.QUOTE_SOFT_EXPIRED],
1205
+ idempotent
1206
+ );
1207
+ },
1208
+ requiredSigner: this._getInitiator()
1209
+ } as SwapExecutionActionSignSmartChainTx<T>;
1210
+ }
1211
+
1212
+ /**
1213
+ * @inheritDoc
1214
+ * @internal
1215
+ */
1216
+ private async _buildClaimSmartChainTxAction(actionOptions?: {
1217
+ manualSettlementSmartChainSigner?: string | T["Signer"] | T["NativeSigner"],
1218
+ }): Promise<SwapExecutionActionSignSmartChainTx<T>> {
1219
+ const signerAddress =
1220
+ await this.wrapper._getSignerAddress(actionOptions?.manualSettlementSmartChainSigner);
1221
+
1222
+ return {
1223
+ type: "SignSmartChainTransaction",
1224
+ name: "Settle manually",
1225
+ description: "Manually settle (claim) the swap on the destination smart chain",
1226
+ chain: this.chainIdentifier,
1227
+ txs: await this.prepareTransactions(this.txsClaim(actionOptions?.manualSettlementSmartChainSigner)),
1228
+ submitTransactions: async (txs: (T["SignedTXType"] | string)[], abortSignal?: AbortSignal, idempotent?: boolean) => {
1229
+ return this._submitExecutionTransactions(
1230
+ txs,
1231
+ abortSignal,
1232
+ [FromBTCSwapState.BTC_TX_CONFIRMED],
1233
+ idempotent
1234
+ );
1235
+ },
1236
+ requiredSigner: signerAddress ?? this._getInitiator()
1237
+ } as SwapExecutionActionSignSmartChainTx<T>;
837
1238
  }
838
1239
 
839
1240
  /**
@@ -849,33 +1250,66 @@ export class FromBTCSwap<T extends ChainType = ChainType>
849
1250
  * @param options.maxWaitTillAutomaticSettlementSeconds Maximum time to wait for an automatic settlement after
850
1251
  * the bitcoin transaction is confirmed (defaults to 60 seconds)
851
1252
  */
852
- async getCurrentActions(options?: {
1253
+ async getExecutionAction(options?: {
853
1254
  bitcoinFeeRate?: number,
854
1255
  bitcoinWallet?: MinimalBitcoinWalletInterface,
855
1256
  skipChecks?: boolean,
856
1257
  manualSettlementSmartChainSigner?: string | T["Signer"] | T["NativeSigner"],
857
1258
  maxWaitTillAutomaticSettlementSeconds?: number
858
- }): Promise<SwapExecutionAction<T>[]> {
859
- if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.CLAIM_COMMITED) {
860
- try {
861
- return await this.txsExecute(options);
862
- } catch (e) {}
863
- }
864
- if(this.isClaimable()) {
865
- if(
866
- this.btcTxConfirmedAt==null ||
867
- options?.maxWaitTillAutomaticSettlementSeconds===0 ||
868
- (Date.now() - this.btcTxConfirmedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60)*1000
869
- ) {
870
- return [{
871
- name: "Claim" as const,
872
- description: "Manually settle (claim) the swap on the destination smart chain",
873
- chain: this.chainIdentifier,
874
- txs: await this.txsClaim(options?.manualSettlementSmartChainSigner)
875
- }];
876
- }
877
- }
878
- return[];
1259
+ }): Promise<
1260
+ SwapExecutionActionSendToAddress<false> |
1261
+ SwapExecutionActionSignPSBT<"FUNDED_PSBT"> |
1262
+ SwapExecutionActionWait<"BITCOIN_CONFS" | "SETTLEMENT"> |
1263
+ SwapExecutionActionSignSmartChainTx<T> |
1264
+ undefined
1265
+ > {
1266
+ const executionStatus = await this._getExecutionStatus(options);
1267
+ return executionStatus.buildCurrentAction(options);
1268
+ }
1269
+
1270
+ /**
1271
+ * @inheritDoc
1272
+ */
1273
+ async getExecutionStatus(options?: {
1274
+ skipBuildingAction?: boolean,
1275
+ bitcoinFeeRate?: number,
1276
+ bitcoinWallet?: MinimalBitcoinWalletInterface,
1277
+ skipChecks?: boolean,
1278
+ manualSettlementSmartChainSigner?: string | T["Signer"] | T["NativeSigner"],
1279
+ maxWaitTillAutomaticSettlementSeconds?: number
1280
+ }): Promise<{
1281
+ steps: [
1282
+ SwapExecutionStepSetup<T["ChainId"]>,
1283
+ SwapExecutionStepPayment<"BITCOIN">,
1284
+ SwapExecutionStepSettlement<T["ChainId"], "awaiting_automatic" | "awaiting_manual">
1285
+ ],
1286
+ currentAction:
1287
+ SwapExecutionActionSendToAddress<false> |
1288
+ SwapExecutionActionSignPSBT<"FUNDED_PSBT"> |
1289
+ SwapExecutionActionWait<"BITCOIN_CONFS" | "SETTLEMENT"> |
1290
+ SwapExecutionActionSignSmartChainTx<T> |
1291
+ undefined,
1292
+ stateInfo: SwapStateInfo<FromBTCSwapState>
1293
+ }> {
1294
+ const executionStatus = await this._getExecutionStatus(options);
1295
+ return {
1296
+ steps: executionStatus.steps,
1297
+ currentAction: options?.skipBuildingAction ? undefined : await executionStatus.buildCurrentAction(options),
1298
+ stateInfo: this._getStateInfo(executionStatus.state)
1299
+ };
1300
+ }
1301
+
1302
+ /**
1303
+ * @inheritDoc
1304
+ */
1305
+ async getExecutionSteps(options?: {
1306
+ maxWaitTillAutomaticSettlementSeconds?: number
1307
+ }): Promise<[
1308
+ SwapExecutionStepSetup<T["ChainId"]>,
1309
+ SwapExecutionStepPayment<"BITCOIN">,
1310
+ SwapExecutionStepSettlement<T["ChainId"], "awaiting_automatic" | "awaiting_manual">
1311
+ ]> {
1312
+ return (await this._getExecutionStatus(options)).steps;
879
1313
  }
880
1314
 
881
1315
  //////////////////////////////
@@ -920,9 +1354,13 @@ export class FromBTCSwap<T extends ChainType = ChainType>
920
1354
  ]);
921
1355
  abortController.abort();
922
1356
 
923
- if(result===0) this.logger.debug("waitTillCommited(): Resolved from state changed");
924
- if(result===true) this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
925
- if(result===false) {
1357
+ if(result===0) {
1358
+ this.logger.debug("waitTillCommited(): Resolved from state changed");
1359
+ } else if(result!=null) {
1360
+ this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
1361
+ }
1362
+
1363
+ if(result===null) {
926
1364
  this.logger.debug("waitTillCommited(): Resolved from watchdog - signature expired");
927
1365
  if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
928
1366
  await this._saveAndEmit(FromBTCSwapState.QUOTE_EXPIRED);
@@ -931,6 +1369,8 @@ export class FromBTCSwap<T extends ChainType = ChainType>
931
1369
  }
932
1370
 
933
1371
  if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
1372
+ if(typeof(result)==="object" && (result as any).getInitTxId!=null && this._commitTxId==null)
1373
+ this._commitTxId = await (result as any).getInitTxId();
934
1374
  await this._saveAndEmit(FromBTCSwapState.CLAIM_COMMITED);
935
1375
  }
936
1376
  }
@@ -1045,7 +1485,7 @@ export class FromBTCSwap<T extends ChainType = ChainType>
1045
1485
  * @throws {Error} If swap is in invalid state (must be {@link FromBTCSwapState.BTC_TX_CONFIRMED})
1046
1486
  * @throws {Error} If the LP refunded sooner than we were able to claim
1047
1487
  */
1048
- async waitTillClaimed(maxWaitTimeSeconds?: number, abortSignal?: AbortSignal): Promise<boolean> {
1488
+ async waitTillClaimed(maxWaitTimeSeconds?: number, abortSignal?: AbortSignal, pollIntervalSeconds?: number): Promise<boolean> {
1049
1489
  if(this._state===FromBTCSwapState.CLAIM_CLAIMED) return Promise.resolve(true);
1050
1490
  if(this._state!==FromBTCSwapState.BTC_TX_CONFIRMED) throw new Error("Invalid state (not BTC_TX_CONFIRMED)");
1051
1491
 
@@ -1063,7 +1503,7 @@ export class FromBTCSwap<T extends ChainType = ChainType>
1063
1503
  let res: 0 | 1 | SwapCommitState;
1064
1504
  try {
1065
1505
  res = await Promise.race([
1066
- this.watchdogWaitTillResult(undefined, abortController.signal),
1506
+ this.watchdogWaitTillResult(pollIntervalSeconds, abortController.signal),
1067
1507
  this.waitTillState(FromBTCSwapState.CLAIM_CLAIMED, "eq", abortController.signal).then(() => 0 as const),
1068
1508
  this.waitTillState(FromBTCSwapState.FAILED, "eq", abortController.signal).then(() => 1 as const),
1069
1509
  ]);
@@ -1200,24 +1640,38 @@ export class FromBTCSwap<T extends ChainType = ChainType>
1200
1640
  async _forciblySetOnchainState(status: SwapCommitState): Promise<boolean> {
1201
1641
  switch(status.type) {
1202
1642
  case SwapCommitStateType.PAID:
1643
+ if(this._commitTxId==null && (status as any).getInitTxId!=null) this._commitTxId = await (status as any).getInitTxId();
1203
1644
  if(this._claimTxId==null) this._claimTxId = await status.getClaimTxId();
1204
1645
  const txId = Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
1205
1646
  await this._setBitcoinTxId(txId);
1206
1647
  this._state = FromBTCSwapState.CLAIM_CLAIMED;
1207
1648
  return true;
1208
1649
  case SwapCommitStateType.NOT_COMMITED:
1209
- if(this._refundTxId==null && status.getRefundTxId) this._refundTxId = await status.getRefundTxId();
1650
+ let changed: boolean = false;
1651
+ if(this._commitTxId==null && (status as any).getInitTxId!=null) {
1652
+ this._commitTxId = await (status as any).getInitTxId();
1653
+ changed = true;
1654
+ }
1655
+ if(this._refundTxId==null && status.getRefundTxId) {
1656
+ this._refundTxId = await status.getRefundTxId();
1657
+ changed = true;
1658
+ }
1210
1659
  if(this._refundTxId!=null) {
1211
1660
  this._state = FromBTCSwapState.FAILED;
1212
- return true;
1661
+ changed = true;
1213
1662
  }
1214
- break;
1663
+ return changed;
1215
1664
  case SwapCommitStateType.EXPIRED:
1665
+ if(this._commitTxId==null && (status as any).getInitTxId!=null) this._commitTxId = await (status as any).getInitTxId();
1216
1666
  if(this._refundTxId==null && status.getRefundTxId) this._refundTxId = await status.getRefundTxId();
1217
1667
  this._state = this._refundTxId==null ? FromBTCSwapState.QUOTE_EXPIRED : FromBTCSwapState.FAILED;
1218
1668
  return true;
1219
1669
  case SwapCommitStateType.COMMITED:
1220
1670
  let save: boolean = false;
1671
+ if(this._commitTxId==null && (status as any).getInitTxId!=null) {
1672
+ this._commitTxId = await (status as any).getInitTxId();
1673
+ save = true;
1674
+ }
1221
1675
  if(this._state!==FromBTCSwapState.CLAIM_COMMITED && this._state!==FromBTCSwapState.BTC_TX_CONFIRMED && this._state!==FromBTCSwapState.EXPIRED) {
1222
1676
  this._state = FromBTCSwapState.CLAIM_COMMITED;
1223
1677
  save = true;
@@ -1227,7 +1681,7 @@ export class FromBTCSwap<T extends ChainType = ChainType>
1227
1681
  this.btcTxLastChecked = Date.now();
1228
1682
  const res = await this.getBitcoinPayment();
1229
1683
  if(res!=null) {
1230
- if(this.txId!==res.txId) {
1684
+ if(this.txId!==res.txId || this.vout!==res.vout || (res.inputAddresses!=null && this.senderAddress==null)) {
1231
1685
  if(res.inputAddresses!=null) this.senderAddress = res.inputAddresses[0];
1232
1686
  this.txId = res.txId;
1233
1687
  this.vout = res.vout;
@@ -1271,7 +1725,7 @@ export class FromBTCSwap<T extends ChainType = ChainType>
1271
1725
  const res = await this.getBitcoinPayment();
1272
1726
  if(res!=null) {
1273
1727
  let shouldSave: boolean = false;
1274
- if(this.txId!==res.txId) {
1728
+ if(this.txId!==res.txId || this.vout!==res.vout || (res.inputAddresses!=null && this.senderAddress==null)) {
1275
1729
  this.txId = res.txId;
1276
1730
  this.vout = res.vout;
1277
1731
  if(res.inputAddresses!=null) this.senderAddress = res.inputAddresses[0];