@atomiqlabs/sdk 8.6.6 → 8.6.7

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 (225) hide show
  1. package/dist/SmartChainAssets.d.ts +181 -181
  2. package/dist/SmartChainAssets.js +181 -181
  3. package/dist/bitcoin/coinselect2/accumulative.d.ts +6 -6
  4. package/dist/bitcoin/coinselect2/accumulative.js +52 -51
  5. package/dist/bitcoin/coinselect2/blackjack.d.ts +6 -6
  6. package/dist/bitcoin/coinselect2/blackjack.js +38 -37
  7. package/dist/bitcoin/coinselect2/index.d.ts +19 -17
  8. package/dist/bitcoin/coinselect2/index.js +69 -69
  9. package/dist/bitcoin/coinselect2/utils.d.ts +77 -75
  10. package/dist/bitcoin/coinselect2/utils.js +123 -123
  11. package/dist/bitcoin/wallet/BitcoinWallet.d.ts +130 -128
  12. package/dist/bitcoin/wallet/BitcoinWallet.js +322 -322
  13. package/dist/bitcoin/wallet/IBitcoinWallet.d.ts +78 -78
  14. package/dist/bitcoin/wallet/IBitcoinWallet.js +21 -20
  15. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +101 -99
  16. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +176 -176
  17. package/dist/enums/FeeType.d.ts +15 -15
  18. package/dist/enums/FeeType.js +19 -19
  19. package/dist/enums/SwapAmountType.d.ts +15 -15
  20. package/dist/enums/SwapAmountType.js +19 -19
  21. package/dist/enums/SwapDirection.d.ts +15 -15
  22. package/dist/enums/SwapDirection.js +19 -19
  23. package/dist/enums/SwapSide.d.ts +15 -15
  24. package/dist/enums/SwapSide.js +19 -19
  25. package/dist/enums/SwapType.d.ts +75 -75
  26. package/dist/enums/SwapType.js +79 -79
  27. package/dist/errors/IntermediaryError.d.ts +13 -13
  28. package/dist/errors/IntermediaryError.js +27 -27
  29. package/dist/errors/RequestError.d.ts +32 -32
  30. package/dist/errors/RequestError.js +54 -54
  31. package/dist/errors/UserError.d.ts +8 -8
  32. package/dist/errors/UserError.js +16 -16
  33. package/dist/events/UnifiedSwapEventListener.d.ts +23 -23
  34. package/dist/events/UnifiedSwapEventListener.js +130 -130
  35. package/dist/http/HttpUtils.d.ts +27 -27
  36. package/dist/http/HttpUtils.js +91 -90
  37. package/dist/http/paramcoders/IParamReader.d.ts +8 -8
  38. package/dist/http/paramcoders/IParamReader.js +2 -2
  39. package/dist/http/paramcoders/ParamDecoder.d.ts +44 -42
  40. package/dist/http/paramcoders/ParamDecoder.js +137 -137
  41. package/dist/http/paramcoders/ParamEncoder.d.ts +20 -18
  42. package/dist/http/paramcoders/ParamEncoder.js +36 -36
  43. package/dist/http/paramcoders/SchemaVerifier.d.ts +26 -26
  44. package/dist/http/paramcoders/SchemaVerifier.js +145 -145
  45. package/dist/http/paramcoders/client/ResponseParamDecoder.d.ts +11 -11
  46. package/dist/http/paramcoders/client/ResponseParamDecoder.js +57 -57
  47. package/dist/http/paramcoders/client/StreamParamEncoder.d.ts +13 -11
  48. package/dist/http/paramcoders/client/StreamParamEncoder.js +26 -26
  49. package/dist/http/paramcoders/client/StreamingFetchPromise.d.ts +16 -16
  50. package/dist/http/paramcoders/client/StreamingFetchPromise.js +174 -173
  51. package/dist/index.d.ts +85 -85
  52. package/dist/index.js +158 -158
  53. package/dist/intermediaries/Intermediary.d.ts +157 -157
  54. package/dist/intermediaries/Intermediary.js +142 -142
  55. package/dist/intermediaries/IntermediaryDiscovery.d.ts +199 -198
  56. package/dist/intermediaries/IntermediaryDiscovery.js +406 -406
  57. package/dist/intermediaries/apis/IntermediaryAPI.d.ts +439 -437
  58. package/dist/intermediaries/apis/IntermediaryAPI.js +603 -603
  59. package/dist/intermediaries/apis/TrustedIntermediaryAPI.d.ts +155 -155
  60. package/dist/intermediaries/apis/TrustedIntermediaryAPI.js +137 -137
  61. package/dist/lnurl/LNURL.d.ts +102 -102
  62. package/dist/lnurl/LNURL.js +321 -321
  63. package/dist/prices/RedundantSwapPrice.d.ts +110 -110
  64. package/dist/prices/RedundantSwapPrice.js +222 -222
  65. package/dist/prices/SingleSwapPrice.d.ts +34 -34
  66. package/dist/prices/SingleSwapPrice.js +44 -44
  67. package/dist/prices/SwapPriceWithChain.d.ts +107 -107
  68. package/dist/prices/SwapPriceWithChain.js +128 -128
  69. package/dist/prices/abstract/ICachedSwapPrice.d.ts +28 -28
  70. package/dist/prices/abstract/ICachedSwapPrice.js +62 -62
  71. package/dist/prices/abstract/IPriceProvider.d.ts +81 -81
  72. package/dist/prices/abstract/IPriceProvider.js +74 -74
  73. package/dist/prices/abstract/ISwapPrice.d.ts +168 -168
  74. package/dist/prices/abstract/ISwapPrice.js +279 -279
  75. package/dist/prices/providers/BinancePriceProvider.d.ts +23 -23
  76. package/dist/prices/providers/BinancePriceProvider.js +30 -30
  77. package/dist/prices/providers/CoinGeckoPriceProvider.d.ts +23 -23
  78. package/dist/prices/providers/CoinGeckoPriceProvider.js +29 -29
  79. package/dist/prices/providers/CoinPaprikaPriceProvider.d.ts +25 -25
  80. package/dist/prices/providers/CoinPaprikaPriceProvider.js +29 -29
  81. package/dist/prices/providers/CustomPriceProvider.d.ts +24 -24
  82. package/dist/prices/providers/CustomPriceProvider.js +35 -35
  83. package/dist/prices/providers/KrakenPriceProvider.d.ts +38 -38
  84. package/dist/prices/providers/KrakenPriceProvider.js +45 -45
  85. package/dist/prices/providers/OKXPriceProvider.d.ts +34 -34
  86. package/dist/prices/providers/OKXPriceProvider.js +29 -29
  87. package/dist/prices/providers/abstract/ExchangePriceProvider.d.ts +17 -17
  88. package/dist/prices/providers/abstract/ExchangePriceProvider.js +21 -21
  89. package/dist/prices/providers/abstract/HttpPriceProvider.d.ts +7 -7
  90. package/dist/prices/providers/abstract/HttpPriceProvider.js +12 -12
  91. package/dist/storage/IUnifiedStorage.d.ts +85 -85
  92. package/dist/storage/IUnifiedStorage.js +2 -2
  93. package/dist/storage/UnifiedSwapStorage.d.ts +114 -114
  94. package/dist/storage/UnifiedSwapStorage.js +116 -116
  95. package/dist/storage-browser/IndexedDBUnifiedStorage.d.ts +63 -63
  96. package/dist/storage-browser/IndexedDBUnifiedStorage.js +298 -298
  97. package/dist/storage-browser/LocalStorageManager.d.ts +49 -49
  98. package/dist/storage-browser/LocalStorageManager.js +93 -93
  99. package/dist/swapper/Swapper.d.ts +687 -686
  100. package/dist/swapper/Swapper.js +1603 -1603
  101. package/dist/swapper/SwapperFactory.d.ts +135 -135
  102. package/dist/swapper/SwapperFactory.js +162 -162
  103. package/dist/swapper/SwapperUtils.d.ts +200 -200
  104. package/dist/swapper/SwapperUtils.js +467 -467
  105. package/dist/swapper/SwapperWithChain.d.ts +404 -404
  106. package/dist/swapper/SwapperWithChain.js +469 -469
  107. package/dist/swapper/SwapperWithSigner.d.ts +322 -322
  108. package/dist/swapper/SwapperWithSigner.js +318 -318
  109. package/dist/swaps/IAddressSwap.d.ts +22 -22
  110. package/dist/swaps/IAddressSwap.js +14 -13
  111. package/dist/swaps/IBTCWalletSwap.d.ts +73 -73
  112. package/dist/swaps/IBTCWalletSwap.js +18 -17
  113. package/dist/swaps/IClaimableSwap.d.ts +49 -49
  114. package/dist/swaps/IClaimableSwap.js +15 -14
  115. package/dist/swaps/IClaimableSwapWrapper.d.ts +15 -15
  116. package/dist/swaps/IClaimableSwapWrapper.js +2 -2
  117. package/dist/swaps/IRefundableSwap.d.ts +43 -43
  118. package/dist/swaps/IRefundableSwap.js +14 -13
  119. package/dist/swaps/ISwap.d.ts +387 -386
  120. package/dist/swaps/ISwap.js +346 -346
  121. package/dist/swaps/ISwapWithGasDrop.d.ts +21 -21
  122. package/dist/swaps/ISwapWithGasDrop.js +12 -11
  123. package/dist/swaps/ISwapWrapper.d.ts +285 -283
  124. package/dist/swaps/ISwapWrapper.js +353 -353
  125. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.d.ts +98 -98
  126. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.js +126 -126
  127. package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +135 -133
  128. package/dist/swaps/escrow_swaps/IEscrowSwap.js +169 -169
  129. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +115 -114
  130. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +134 -134
  131. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +101 -98
  132. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +130 -130
  133. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.d.ts +162 -162
  134. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +190 -190
  135. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.d.ts +58 -58
  136. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.js +78 -78
  137. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +531 -529
  138. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +1285 -1285
  139. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +184 -181
  140. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +418 -418
  141. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +583 -581
  142. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +1371 -1371
  143. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +228 -225
  144. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +506 -506
  145. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +458 -458
  146. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +1126 -1126
  147. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +191 -190
  148. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +378 -378
  149. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +403 -403
  150. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +924 -924
  151. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.d.ts +62 -62
  152. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.js +112 -112
  153. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.d.ts +127 -125
  154. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.js +256 -256
  155. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +242 -241
  156. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +520 -520
  157. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.d.ts +73 -73
  158. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.js +155 -155
  159. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +128 -127
  160. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +278 -278
  161. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +630 -630
  162. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +1443 -1443
  163. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +214 -213
  164. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +756 -756
  165. package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +261 -261
  166. package/dist/swaps/trusted/ln/LnForGasSwap.js +511 -511
  167. package/dist/swaps/trusted/ln/LnForGasWrapper.d.ts +40 -40
  168. package/dist/swaps/trusted/ln/LnForGasWrapper.js +82 -82
  169. package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +342 -342
  170. package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +715 -715
  171. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +69 -68
  172. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +92 -92
  173. package/dist/types/AmountData.d.ts +10 -10
  174. package/dist/types/AmountData.js +2 -2
  175. package/dist/types/CustomPriceFunction.d.ts +11 -11
  176. package/dist/types/CustomPriceFunction.js +2 -2
  177. package/dist/types/PriceInfoType.d.ts +28 -28
  178. package/dist/types/PriceInfoType.js +57 -56
  179. package/dist/types/SwapExecutionAction.d.ts +88 -88
  180. package/dist/types/SwapExecutionAction.js +2 -2
  181. package/dist/types/SwapStateInfo.d.ts +5 -5
  182. package/dist/types/SwapStateInfo.js +2 -2
  183. package/dist/types/SwapWithSigner.d.ts +17 -17
  184. package/dist/types/SwapWithSigner.js +43 -42
  185. package/dist/types/Token.d.ts +99 -99
  186. package/dist/types/Token.js +76 -76
  187. package/dist/types/TokenAmount.d.ts +69 -69
  188. package/dist/types/TokenAmount.js +60 -59
  189. package/dist/types/fees/Fee.d.ts +50 -50
  190. package/dist/types/fees/Fee.js +2 -2
  191. package/dist/types/fees/FeeBreakdown.d.ts +11 -11
  192. package/dist/types/fees/FeeBreakdown.js +2 -2
  193. package/dist/types/fees/PercentagePPM.d.ts +17 -17
  194. package/dist/types/fees/PercentagePPM.js +18 -17
  195. package/dist/types/lnurl/LNURLPay.d.ts +61 -61
  196. package/dist/types/lnurl/LNURLPay.js +31 -30
  197. package/dist/types/lnurl/LNURLWithdraw.d.ts +48 -48
  198. package/dist/types/lnurl/LNURLWithdraw.js +27 -26
  199. package/dist/types/wallets/LightningInvoiceCreateService.d.ts +24 -24
  200. package/dist/types/wallets/LightningInvoiceCreateService.js +15 -14
  201. package/dist/types/wallets/MinimalBitcoinWalletInterface.d.ts +23 -23
  202. package/dist/types/wallets/MinimalBitcoinWalletInterface.js +2 -2
  203. package/dist/types/wallets/MinimalLightningNetworkWalletInterface.d.ts +9 -9
  204. package/dist/types/wallets/MinimalLightningNetworkWalletInterface.js +2 -2
  205. package/dist/utils/AutomaticClockDriftCorrection.d.ts +1 -1
  206. package/dist/utils/AutomaticClockDriftCorrection.js +70 -69
  207. package/dist/utils/BitcoinUtils.d.ts +14 -12
  208. package/dist/utils/BitcoinUtils.js +102 -101
  209. package/dist/utils/BitcoinWalletUtils.d.ts +7 -7
  210. package/dist/utils/BitcoinWalletUtils.js +14 -13
  211. package/dist/utils/Logger.d.ts +7 -7
  212. package/dist/utils/Logger.js +12 -11
  213. package/dist/utils/RetryUtils.d.ts +22 -22
  214. package/dist/utils/RetryUtils.js +67 -66
  215. package/dist/utils/SwapUtils.d.ts +88 -88
  216. package/dist/utils/SwapUtils.js +72 -72
  217. package/dist/utils/TimeoutUtils.d.ts +17 -17
  218. package/dist/utils/TimeoutUtils.js +55 -54
  219. package/dist/utils/TokenUtils.d.ts +19 -19
  220. package/dist/utils/TokenUtils.js +37 -36
  221. package/dist/utils/TypeUtils.d.ts +7 -7
  222. package/dist/utils/TypeUtils.js +2 -2
  223. package/dist/utils/Utils.d.ts +58 -56
  224. package/dist/utils/Utils.js +194 -193
  225. package/package.json +1 -1
@@ -1,1126 +1,1126 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FromBTCSwap = exports.FromBTCSwapState = void 0;
4
- exports.isFromBTCSwapInit = isFromBTCSwapInit;
5
- const IFromBTCSelfInitSwap_1 = require("../IFromBTCSelfInitSwap");
6
- const SwapType_1 = require("../../../../enums/SwapType");
7
- const base_1 = require("@atomiqlabs/base");
8
- const buffer_1 = require("buffer");
9
- const Utils_1 = require("../../../../utils/Utils");
10
- const BitcoinUtils_1 = require("../../../../utils/BitcoinUtils");
11
- const IBitcoinWallet_1 = require("../../../../bitcoin/wallet/IBitcoinWallet");
12
- const btc_signer_1 = require("@scure/btc-signer");
13
- const SingleAddressBitcoinWallet_1 = require("../../../../bitcoin/wallet/SingleAddressBitcoinWallet");
14
- const IEscrowSelfInitSwap_1 = require("../../IEscrowSelfInitSwap");
15
- const TokenAmount_1 = require("../../../../types/TokenAmount");
16
- const Token_1 = require("../../../../types/Token");
17
- const Logger_1 = require("../../../../utils/Logger");
18
- const BitcoinWalletUtils_1 = require("../../../../utils/BitcoinWalletUtils");
19
- /**
20
- * State enum for legacy escrow based Bitcoin -> Smart chain swaps.
21
- *
22
- * @category Swaps/Legacy/Bitcoin → Smart chain
23
- */
24
- var FromBTCSwapState;
25
- (function (FromBTCSwapState) {
26
- /**
27
- * Bitcoin swap address has expired and the intermediary (LP) has already refunded
28
- * its funds. No BTC should be sent anymore!
29
- */
30
- FromBTCSwapState[FromBTCSwapState["FAILED"] = -4] = "FAILED";
31
- /**
32
- * Bitcoin swap address has expired, user should not send any BTC anymore! Though
33
- * the intermediary (LP) hasn't refunded yet. So if there is a transaction already
34
- * in-flight the swap might still succeed.
35
- */
36
- FromBTCSwapState[FromBTCSwapState["EXPIRED"] = -3] = "EXPIRED";
37
- /**
38
- * Swap has expired for good and there is no way how it can be executed anymore
39
- */
40
- FromBTCSwapState[FromBTCSwapState["QUOTE_EXPIRED"] = -2] = "QUOTE_EXPIRED";
41
- /**
42
- * A swap is almost expired, and it should be presented to the user as expired, though
43
- * there is still a chance that it will be processed
44
- */
45
- FromBTCSwapState[FromBTCSwapState["QUOTE_SOFT_EXPIRED"] = -1] = "QUOTE_SOFT_EXPIRED";
46
- /**
47
- * Swap quote was created, use the {@link FromBTCSwap.commit} or {@link FromBTCSwap.txsCommit} functions
48
- * to initiate it by creating the swap escrow on the destination smart chain
49
- */
50
- FromBTCSwapState[FromBTCSwapState["PR_CREATED"] = 0] = "PR_CREATED";
51
- /**
52
- * Swap escrow was initiated (committed) on the destination chain, user can send the BTC to the
53
- * swap address with the {@link FromBTCSwap.getFundedPsbt}, {@link FromBTCSwap.getAddress} or
54
- * {@link FromBTCSwap.getHyperlink} functions.
55
- */
56
- FromBTCSwapState[FromBTCSwapState["CLAIM_COMMITED"] = 1] = "CLAIM_COMMITED";
57
- /**
58
- * Input bitcoin transaction was confirmed, wait for automatic settlement by the watchtowers
59
- * using the {@link FromBTCSwap.waitTillClaimed} function or settle manually using the {@link FromBTCSwap.claim}
60
- * or {@link FromBTCSwap.txsClaim} function.
61
- */
62
- FromBTCSwapState[FromBTCSwapState["BTC_TX_CONFIRMED"] = 2] = "BTC_TX_CONFIRMED";
63
- /**
64
- * Swap successfully settled and funds received on the destination chain
65
- */
66
- FromBTCSwapState[FromBTCSwapState["CLAIM_CLAIMED"] = 3] = "CLAIM_CLAIMED";
67
- })(FromBTCSwapState || (exports.FromBTCSwapState = FromBTCSwapState = {}));
68
- const FromBTCSwapStateDescription = {
69
- [FromBTCSwapState.FAILED]: "Bitcoin swap address has expired and the intermediary (LP) has already refunded its funds. No BTC should be sent anymore!",
70
- [FromBTCSwapState.EXPIRED]: "Bitcoin swap address has expired, user should not send any BTC anymore! Though the intermediary (LP) hasn't refunded yet. So if there is a transaction already in-flight the swap might still succeed.",
71
- [FromBTCSwapState.QUOTE_EXPIRED]: "Swap has expired for good and there is no way how it can be executed anymore",
72
- [FromBTCSwapState.QUOTE_SOFT_EXPIRED]: "The swap is expired, though there is still a chance that it will be processed",
73
- [FromBTCSwapState.PR_CREATED]: "Swap quote was created, initiate it by creating the swap escrow on the destination smart chain",
74
- [FromBTCSwapState.CLAIM_COMMITED]: "Swap escrow was initiated (committed) on the destination chain, user can send the BTC to the Bitcoin swap address.",
75
- [FromBTCSwapState.BTC_TX_CONFIRMED]: "Input bitcoin transaction was confirmed, wait for automatic settlement by the watchtower or settle manually.",
76
- [FromBTCSwapState.CLAIM_CLAIMED]: "Swap successfully settled and funds received on the destination chain"
77
- };
78
- function isFromBTCSwapInit(obj) {
79
- return typeof (obj.data) === "object" &&
80
- (obj.address == null || typeof (obj.address) === "string") &&
81
- (obj.amount == null || typeof (obj.amount) === "bigint") &&
82
- (obj.requiredConfirmations == null || typeof (obj.requiredConfirmations) === "number") &&
83
- (0, IEscrowSelfInitSwap_1.isIEscrowSelfInitSwapInit)(obj);
84
- }
85
- /**
86
- * Legacy escrow (PrTLC) based swap for Bitcoin -> Smart chains, requires manual initiation
87
- * of the swap escrow on the destination chain.
88
- *
89
- * @category Swaps/Legacy/Bitcoin → Smart chain
90
- */
91
- class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
92
- constructor(wrapper, initOrObject) {
93
- if (isFromBTCSwapInit(initOrObject) && initOrObject.url != null)
94
- initOrObject.url += "/frombtc";
95
- super(wrapper, initOrObject);
96
- this.TYPE = SwapType_1.SwapType.FROM_BTC;
97
- /**
98
- * @internal
99
- */
100
- this.swapStateName = (state) => FromBTCSwapState[state];
101
- /**
102
- * @internal
103
- */
104
- this.swapStateDescription = FromBTCSwapStateDescription;
105
- /**
106
- * @internal
107
- */
108
- this.inputToken = Token_1.BitcoinTokens.BTC;
109
- if (isFromBTCSwapInit(initOrObject)) {
110
- this._state = FromBTCSwapState.PR_CREATED;
111
- this._data = initOrObject.data;
112
- this.feeRate = initOrObject.feeRate;
113
- this.address = initOrObject.address;
114
- this.amount = initOrObject.amount;
115
- this.requiredConfirmations = initOrObject.requiredConfirmations;
116
- }
117
- else {
118
- this.address = initOrObject.address;
119
- this.amount = (0, Utils_1.toBigInt)(initOrObject.amount);
120
- this.senderAddress = initOrObject.senderAddress;
121
- this.txId = initOrObject.txId;
122
- this.vout = initOrObject.vout;
123
- this.requiredConfirmations = initOrObject.requiredConfirmations ?? this._data.getConfirmationsHint();
124
- this.btcTxConfirmedAt = initOrObject.btcTxConfirmedAt;
125
- }
126
- this.tryRecomputeSwapPrice();
127
- this.logger = (0, Logger_1.getLogger)("FromBTC(" + this.getIdentifierHashString() + "): ");
128
- }
129
- /**
130
- * @inheritDoc
131
- * @internal
132
- */
133
- getSwapData() {
134
- return this._data;
135
- }
136
- /**
137
- * @inheritDoc
138
- * @internal
139
- */
140
- upgradeVersion() {
141
- if (this.version == null) {
142
- switch (this._state) {
143
- case -2:
144
- this._state = FromBTCSwapState.FAILED;
145
- break;
146
- case -1:
147
- this._state = FromBTCSwapState.QUOTE_EXPIRED;
148
- break;
149
- case 0:
150
- this._state = FromBTCSwapState.PR_CREATED;
151
- break;
152
- case 1:
153
- this._state = FromBTCSwapState.CLAIM_COMMITED;
154
- break;
155
- case 2:
156
- this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
157
- break;
158
- case 3:
159
- this._state = FromBTCSwapState.CLAIM_CLAIMED;
160
- break;
161
- }
162
- this.version = 1;
163
- }
164
- }
165
- //////////////////////////////
166
- //// Getters & utils
167
- /**
168
- * Returns bitcoin address where the on-chain BTC should be sent to
169
- */
170
- getAddress() {
171
- if (this._state === FromBTCSwapState.PR_CREATED)
172
- throw new Error("Cannot get bitcoin address of non-initiated swaps! Initiate swap first with commit() or txsCommit().");
173
- return this.address ?? "";
174
- }
175
- /**
176
- * Unsafe bitcoin hyperlink getter, returns the address even before the swap is committed!
177
- *
178
- * @private
179
- */
180
- _getHyperlink() {
181
- return this.address == null || this.amount == null ? "" : "bitcoin:" + this.address + "?amount=" + encodeURIComponent((Number(this.amount) / 100000000).toString(10));
182
- }
183
- /**
184
- * @inheritDoc
185
- */
186
- getHyperlink() {
187
- if (this._state === FromBTCSwapState.PR_CREATED)
188
- throw new Error("Cannot get bitcoin address of non-initiated swaps! Initiate swap first with commit() or txsCommit().");
189
- return this._getHyperlink();
190
- }
191
- /**
192
- * @inheritDoc
193
- */
194
- getInputAddress() {
195
- return this.senderAddress ?? null;
196
- }
197
- /**
198
- * @inheritDoc
199
- */
200
- getInputTxId() {
201
- return this.txId ?? null;
202
- }
203
- /**
204
- * Returns timeout time (in UNIX milliseconds) when the on-chain address will expire and no funds should be sent
205
- * to that address anymore
206
- */
207
- getTimeoutTime() {
208
- return Number(this.wrapper._getOnchainSendTimeout(this._data, this.requiredConfirmations ?? 6)) * 1000;
209
- }
210
- /**
211
- * @inheritDoc
212
- */
213
- requiresAction() {
214
- return this.isClaimable() || (this._state === FromBTCSwapState.CLAIM_COMMITED && this.getTimeoutTime() > Date.now() && this.txId == null);
215
- }
216
- /**
217
- * @inheritDoc
218
- */
219
- isFinished() {
220
- return this._state === FromBTCSwapState.CLAIM_CLAIMED || this._state === FromBTCSwapState.QUOTE_EXPIRED || this._state === FromBTCSwapState.FAILED;
221
- }
222
- /**
223
- * @inheritDoc
224
- */
225
- isClaimable() {
226
- return this._state === FromBTCSwapState.BTC_TX_CONFIRMED;
227
- }
228
- /**
229
- * @inheritDoc
230
- */
231
- isSuccessful() {
232
- return this._state === FromBTCSwapState.CLAIM_CLAIMED;
233
- }
234
- /**
235
- * @inheritDoc
236
- */
237
- isFailed() {
238
- return this._state === FromBTCSwapState.FAILED || this._state === FromBTCSwapState.EXPIRED;
239
- }
240
- /**
241
- * @inheritDoc
242
- */
243
- isInProgress() {
244
- return this._state === FromBTCSwapState.CLAIM_COMMITED ||
245
- this._state === FromBTCSwapState.BTC_TX_CONFIRMED;
246
- }
247
- /**
248
- * @inheritDoc
249
- */
250
- isQuoteExpired() {
251
- return this._state === FromBTCSwapState.QUOTE_EXPIRED;
252
- }
253
- /**
254
- * @inheritDoc
255
- */
256
- isQuoteSoftExpired() {
257
- return this._state === FromBTCSwapState.QUOTE_EXPIRED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED;
258
- }
259
- /**
260
- * @inheritDoc
261
- * @internal
262
- */
263
- canCommit(skipQuoteExpiryChecks) {
264
- if (this._state !== FromBTCSwapState.PR_CREATED && (!skipQuoteExpiryChecks || this._state !== FromBTCSwapState.QUOTE_SOFT_EXPIRED))
265
- return false;
266
- if (this.requiredConfirmations == null)
267
- return false;
268
- const expiry = this.wrapper._getOnchainSendTimeout(this._data, this.requiredConfirmations);
269
- const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
270
- return (expiry - currentTimestamp) >= this.wrapper._options.minSendWindow;
271
- }
272
- //////////////////////////////
273
- //// Amounts & fees
274
- /**
275
- * @inheritDoc
276
- */
277
- getInputToken() {
278
- return Token_1.BitcoinTokens.BTC;
279
- }
280
- /**
281
- * @inheritDoc
282
- */
283
- getInput() {
284
- return (0, TokenAmount_1.toTokenAmount)(this.amount ?? null, this.inputToken, this.wrapper._prices);
285
- }
286
- /**
287
- * Returns claimer bounty, acting as a reward for watchtowers to claim the swap automatically,
288
- * this amount is pre-funded by the user on the destination chain when the swap escrow
289
- * is initiated. For total pre-funded deposit amount see {@link getTotalDeposit}.
290
- */
291
- getClaimerBounty() {
292
- return (0, TokenAmount_1.toTokenAmount)(this._data.getClaimerBounty(), this.wrapper._tokens[this._data.getDepositToken()], this.wrapper._prices);
293
- }
294
- //////////////////////////////
295
- //// Bitcoin tx
296
- /**
297
- * If the required number of confirmations is not known, this function tries to infer it by looping through
298
- * possible confirmation targets and comparing the claim hashes
299
- *
300
- * @param btcTx Bitcoin transaction
301
- * @param vout Output index of the desired output in the bitcoin transaction
302
- *
303
- * @private
304
- */
305
- inferRequiredConfirmationsCount(btcTx, vout) {
306
- const txOut = btcTx.outs[vout];
307
- for (let i = 1; i <= 20; i++) {
308
- const computedClaimHash = this.wrapper._contract.getHashForOnchain(buffer_1.Buffer.from(txOut.scriptPubKey.hex, "hex"), BigInt(txOut.value), i);
309
- if (computedClaimHash.toString("hex") === this._data.getClaimHash()) {
310
- return i;
311
- }
312
- }
313
- }
314
- /**
315
- * @inheritDoc
316
- */
317
- getRequiredConfirmationsCount() {
318
- return this.requiredConfirmations ?? NaN;
319
- }
320
- /**
321
- * Checks whether a bitcoin payment was already made, returns the payment or `null` when no payment has been made.
322
- *
323
- * @internal
324
- */
325
- async getBitcoinPayment() {
326
- const txoHashHint = this._data.getTxoHashHint();
327
- if (txoHashHint == null)
328
- throw new Error("Swap data doesn't include the txo hash hint! Cannot check bitcoin transaction!");
329
- if (this.address == null)
330
- throw new Error("Cannot check bitcoin payment, because the address is not known! This can happen after a swap is recovered.");
331
- const result = await this.wrapper._btcRpc.checkAddressTxos(this.address, buffer_1.Buffer.from(txoHashHint, "hex"));
332
- if (result == null)
333
- return null;
334
- if (this.requiredConfirmations == null) {
335
- this.requiredConfirmations = this.inferRequiredConfirmationsCount(result.tx, result.vout);
336
- }
337
- return {
338
- inputAddresses: result.tx.inputAddresses,
339
- txId: result.tx.txid,
340
- vout: result.vout,
341
- confirmations: result.tx.confirmations ?? 0,
342
- targetConfirmations: this.getRequiredConfirmationsCount()
343
- };
344
- }
345
- /**
346
- * Used to set the txId of the bitcoin payment from the on-chain events listener
347
- *
348
- * @param txId Transaction ID that settled the swap on the smart chain
349
- *
350
- * @internal
351
- */
352
- async _setBitcoinTxId(txId) {
353
- if (this.txId !== txId || this.address == null || this.vout == null || this.senderAddress == null || this.amount == null) {
354
- const btcTx = await this.wrapper._btcRpc.getTransaction(txId);
355
- if (btcTx == null)
356
- return;
357
- const txoHashHint = this._data.getTxoHashHint();
358
- if (txoHashHint != null) {
359
- const expectedTxoHash = buffer_1.Buffer.from(txoHashHint, "hex");
360
- const vout = btcTx.outs.findIndex(out => (0, Utils_1.getTxoHash)(out.scriptPubKey.hex, out.value).equals(expectedTxoHash));
361
- if (vout !== -1) {
362
- this.vout = vout;
363
- //If amount or address are not known, parse them from the bitcoin tx
364
- // this can happen if the swap is recovered from on-chain data and
365
- // hence doesn't contain the address and amount data
366
- if (this.amount == null)
367
- this.amount = BigInt(btcTx.outs[vout].value);
368
- if (this.address == null)
369
- try {
370
- this.address = (0, BitcoinUtils_1.fromOutputScript)(this.wrapper._options.bitcoinNetwork, btcTx.outs[vout].scriptPubKey.hex);
371
- }
372
- catch (e) {
373
- this.logger.warn("_setBitcoinTxId(): Failed to parse address from output script: ", e);
374
- }
375
- if (this.requiredConfirmations == null) {
376
- this.requiredConfirmations = this.inferRequiredConfirmationsCount(btcTx, vout);
377
- }
378
- }
379
- }
380
- if (btcTx.inputAddresses != null) {
381
- this.senderAddress = btcTx.inputAddresses[0];
382
- }
383
- }
384
- this.txId = txId;
385
- }
386
- /**
387
- * @inheritDoc
388
- *
389
- * @throws {Error} if in invalid state (must be {@link FromBTCSwapState.CLAIM_COMMITED})
390
- */
391
- async waitForBitcoinTransaction(updateCallback, checkIntervalSeconds, abortSignal) {
392
- if (this._state !== FromBTCSwapState.CLAIM_COMMITED && this._state !== FromBTCSwapState.EXPIRED)
393
- throw new Error("Must be in COMMITED state!");
394
- const txoHashHint = this._data.getTxoHashHint();
395
- if (txoHashHint == null)
396
- throw new Error("Swap data doesn't include the txo hash hint! Cannot check bitcoin transaction!");
397
- if (this.address == null)
398
- throw new Error("Cannot check bitcoin payment, because the address is not known! This can happen after a swap is recovered.");
399
- let abortedDueToEnoughConfirmationsResult;
400
- const abortController = (0, Utils_1.extendAbortController)(abortSignal);
401
- const result = await this.wrapper._btcRpc.waitForAddressTxo(this.address, buffer_1.Buffer.from(txoHashHint, "hex"), this.requiredConfirmations ?? 6, //In case confirmation count is not known, we use a conservative estimate
402
- (btcTx, vout, txEtaMs) => {
403
- let requiredConfirmations = this.requiredConfirmations;
404
- if (btcTx != null && vout != null && requiredConfirmations == null) {
405
- requiredConfirmations = this.inferRequiredConfirmationsCount(btcTx, vout);
406
- }
407
- if (btcTx != null && (btcTx.txid !== this.txId || (this.requiredConfirmations == null && requiredConfirmations != null))) {
408
- this.txId = btcTx.txid;
409
- this.vout = vout;
410
- this.requiredConfirmations = requiredConfirmations;
411
- if (btcTx.inputAddresses != null)
412
- this.senderAddress = btcTx.inputAddresses[0];
413
- this._saveAndEmit().catch(e => {
414
- this.logger.error("waitForBitcoinTransaction(): Failed to save swap from within waitForAddressTxo callback:", e);
415
- });
416
- }
417
- //Abort the loop as soon as the transaction gets enough confirmations, this is required in case
418
- // we pass a default 6 confirmations to the fn, but then are able to infer the actual confirmation
419
- // target from the prior block
420
- if (btcTx?.confirmations != null && requiredConfirmations != null && requiredConfirmations <= btcTx.confirmations && vout != null) {
421
- abortedDueToEnoughConfirmationsResult = {
422
- tx: btcTx,
423
- vout
424
- };
425
- abortController.abort();
426
- return;
427
- }
428
- if (updateCallback != null)
429
- updateCallback(btcTx?.txid, btcTx == null ? undefined : (btcTx?.confirmations ?? 0), requiredConfirmations ?? NaN, txEtaMs);
430
- }, abortController.signal, checkIntervalSeconds).catch(e => {
431
- //We catch the case when the loop was aborted due to the transaction getting enough confirmations
432
- if (abortedDueToEnoughConfirmationsResult != null)
433
- return abortedDueToEnoughConfirmationsResult;
434
- throw e;
435
- });
436
- if (abortSignal != null)
437
- abortSignal.throwIfAborted();
438
- this.txId = result.tx.txid;
439
- this.vout = result.vout;
440
- if (result.tx.inputAddresses != null)
441
- this.senderAddress = result.tx.inputAddresses[0];
442
- if (this._state !== FromBTCSwapState.CLAIM_CLAIMED &&
443
- this._state !== FromBTCSwapState.FAILED) {
444
- this.btcTxConfirmedAt ??= Date.now();
445
- this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
446
- }
447
- await this._saveAndEmit();
448
- return result.tx.txid;
449
- }
450
- /**
451
- * Private getter of the funded PSBT that doesn't check current state
452
- *
453
- * @param _bitcoinWallet Bitcoin wallet to fund the PSBT with
454
- * @param feeRate Optional bitcoin fee rate in sats/vB
455
- * @param additionalOutputs Optional additional outputs that should also be included in the generated PSBT
456
- *
457
- * @private
458
- */
459
- async _getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs) {
460
- if (this.address == null)
461
- throw new Error("Cannot create funded PSBT, because the address is not known! This can happen after a swap is recovered.");
462
- let bitcoinWallet;
463
- if ((0, IBitcoinWallet_1.isIBitcoinWallet)(_bitcoinWallet)) {
464
- bitcoinWallet = _bitcoinWallet;
465
- }
466
- else {
467
- bitcoinWallet = new SingleAddressBitcoinWallet_1.SingleAddressBitcoinWallet(this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork, _bitcoinWallet);
468
- }
469
- //TODO: Maybe re-introduce fee rate check here if passed from the user
470
- if (feeRate == null) {
471
- feeRate = await bitcoinWallet.getFeeRate();
472
- }
473
- const basePsbt = new btc_signer_1.Transaction({
474
- allowUnknownOutputs: true,
475
- allowLegacyWitnessUtxo: true
476
- });
477
- basePsbt.addOutput({
478
- amount: this.amount,
479
- script: (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.address)
480
- });
481
- if (additionalOutputs != null)
482
- additionalOutputs.forEach(output => {
483
- basePsbt.addOutput({
484
- amount: output.amount,
485
- script: output.outputScript ?? (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, output.address)
486
- });
487
- });
488
- const psbt = await bitcoinWallet.fundPsbt(basePsbt, feeRate);
489
- //Sign every input
490
- const signInputs = [];
491
- for (let i = 0; i < psbt.inputsLength; i++) {
492
- signInputs.push(i);
493
- }
494
- const serializedPsbt = buffer_1.Buffer.from(psbt.toPSBT());
495
- return {
496
- psbt,
497
- psbtHex: serializedPsbt.toString("hex"),
498
- psbtBase64: serializedPsbt.toString("base64"),
499
- signInputs
500
- };
501
- }
502
- /**
503
- * @inheritDoc
504
- */
505
- getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs) {
506
- if (this._state !== FromBTCSwapState.CLAIM_COMMITED)
507
- throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
508
- return this._getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs);
509
- }
510
- /**
511
- * @inheritDoc
512
- *
513
- * @throws {Error} if the swap is in invalid state (not in {@link FromBTCSwapState.CLAIM_COMMITED}), or if
514
- * the swap bitcoin address already expired.
515
- */
516
- async submitPsbt(_psbt) {
517
- const psbt = (0, BitcoinUtils_1.parsePsbtTransaction)(_psbt);
518
- if (this._state !== FromBTCSwapState.CLAIM_COMMITED)
519
- throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
520
- //Ensure not expired
521
- if (this.getTimeoutTime() < Date.now()) {
522
- throw new Error("Swap address expired!");
523
- }
524
- const output0 = psbt.getOutput(0);
525
- if (this.amount != null && output0.amount !== this.amount)
526
- throw new Error("PSBT output amount invalid, expected: " + this.amount + " got: " + output0.amount);
527
- if (this.address != null) {
528
- const expectedOutputScript = (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.address);
529
- if (output0.script == null || !expectedOutputScript.equals(output0.script))
530
- throw new Error("PSBT output script invalid!");
531
- }
532
- if (!psbt.isFinal)
533
- psbt.finalize();
534
- return await this.wrapper._btcRpc.sendRawTransaction(buffer_1.Buffer.from(psbt.toBytes(true, true)).toString("hex"));
535
- }
536
- /**
537
- * @inheritDoc
538
- */
539
- async estimateBitcoinFee(_bitcoinWallet, feeRate) {
540
- if (this.address == null || this.amount == null)
541
- return null;
542
- const bitcoinWallet = (0, BitcoinWalletUtils_1.toBitcoinWallet)(_bitcoinWallet, this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork);
543
- const txFee = await bitcoinWallet.getTransactionFee(this.address, this.amount, feeRate);
544
- if (txFee == null)
545
- return null;
546
- return (0, TokenAmount_1.toTokenAmount)(BigInt(txFee), Token_1.BitcoinTokens.BTC, this.wrapper._prices);
547
- }
548
- /**
549
- * @inheritDoc
550
- */
551
- async sendBitcoinTransaction(wallet, feeRate) {
552
- if (this.address == null || this.amount == null)
553
- throw new Error("Cannot send bitcoin transaction, because the address is not known! This can happen after a swap is recovered.");
554
- if (this._state !== FromBTCSwapState.CLAIM_COMMITED)
555
- throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
556
- //Ensure not expired
557
- if (this.getTimeoutTime() < Date.now()) {
558
- throw new Error("Swap address expired!");
559
- }
560
- if ((0, IBitcoinWallet_1.isIBitcoinWallet)(wallet)) {
561
- return await wallet.sendTransaction(this.address, this.amount, feeRate);
562
- }
563
- else {
564
- const { psbt, psbtHex, psbtBase64, signInputs } = await this.getFundedPsbt(wallet, feeRate);
565
- const signedPsbt = await wallet.signPsbt({
566
- psbt, psbtHex, psbtBase64
567
- }, signInputs);
568
- return await this.submitPsbt(signedPsbt);
569
- }
570
- }
571
- //////////////////////////////
572
- //// Execution
573
- /**
574
- * Executes the swap with the provided bitcoin wallet,
575
- *
576
- * @param dstSigner Signer on the destination network, needs to have the same address as the one specified when
577
- * quote was created, this is required for legacy swaps because the destination wallet needs to actively open
578
- * a bitcoin swap address to which the BTC is then sent, this means that the address also needs to have enough
579
- * native tokens to pay for gas on the destination network
580
- * @param wallet Bitcoin wallet to use to sign the bitcoin transaction, can also be null - then the execution waits
581
- * till a transaction is received from an external wallet
582
- * @param callbacks Callbacks to track the progress of the swap
583
- * @param options Optional options for the swap like feeRate, AbortSignal, and timeouts/intervals
584
- *
585
- * @returns {boolean} Whether a swap was settled automatically by swap watchtowers or requires manual claim by the
586
- * user, in case `false` is returned the user should call `swap.claim()` to settle the swap on the destination manually
587
- */
588
- async execute(dstSigner, wallet, callbacks, options) {
589
- if (this._state === FromBTCSwapState.FAILED)
590
- throw new Error("Swap failed!");
591
- if (this._state === FromBTCSwapState.EXPIRED)
592
- throw new Error("Swap address expired!");
593
- if (this._state === FromBTCSwapState.QUOTE_EXPIRED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED)
594
- throw new Error("Swap quote expired!");
595
- if (this._state === FromBTCSwapState.CLAIM_CLAIMED)
596
- throw new Error("Swap already settled!");
597
- if (this._state === FromBTCSwapState.PR_CREATED) {
598
- await this.commit(dstSigner, options?.abortSignal, undefined, callbacks?.onDestinationCommitSent);
599
- }
600
- if (this._state === FromBTCSwapState.CLAIM_COMMITED) {
601
- if (wallet != null) {
602
- const bitcoinPaymentSent = await this.getBitcoinPayment();
603
- if (bitcoinPaymentSent == null) {
604
- //Send btc tx
605
- const txId = await this.sendBitcoinTransaction(wallet, options?.feeRate);
606
- if (callbacks?.onSourceTransactionSent != null)
607
- callbacks.onSourceTransactionSent(txId);
608
- }
609
- }
610
- const txId = await this.waitForBitcoinTransaction(callbacks?.onSourceTransactionConfirmationStatus, options?.btcTxCheckIntervalSeconds, options?.abortSignal);
611
- if (callbacks?.onSourceTransactionConfirmed != null)
612
- callbacks.onSourceTransactionConfirmed(txId);
613
- }
614
- // @ts-ignore
615
- if (this._state === FromBTCSwapState.CLAIM_CLAIMED)
616
- return true;
617
- if (this._state === FromBTCSwapState.BTC_TX_CONFIRMED) {
618
- const success = await this.waitTillClaimed(options?.maxWaitTillAutomaticSettlementSeconds ?? 60, options?.abortSignal);
619
- if (success && callbacks?.onSwapSettled != null)
620
- callbacks.onSwapSettled(this.getOutputTxId());
621
- return success;
622
- }
623
- throw new Error("Invalid state reached!");
624
- }
625
- /**
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
636
- */
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 [
644
- {
645
- name: "Commit",
646
- description: `Opens up the bitcoin swap address on the ${this.chainIdentifier} side`,
647
- chain: this.chainIdentifier,
648
- txs: await this.txsCommit(options?.skipChecks)
649
- },
650
- {
651
- name: "Payment",
652
- description: "Send funds to the bitcoin swap address",
653
- 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
- ]
665
- }
666
- ];
667
- }
668
- if (this._state === FromBTCSwapState.CLAIM_COMMITED) {
669
- if (this.getTimeoutTime() < Date.now())
670
- 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
- ]
687
- }
688
- ];
689
- }
690
- throw new Error("Invalid swap state to obtain execution txns, required PR_CREATED or CLAIM_COMMITED");
691
- }
692
- /**
693
- * @inheritDoc
694
- *
695
- * @param options.bitcoinFeeRate Optional fee rate to use for the created Bitcoin transaction
696
- * @param options.bitcoinWallet Bitcoin wallet to use, when provided the function returns a funded
697
- * psbt (`"FUNDED_PSBT"`), if not passed just a bitcoin receive address is returned (`"ADDRESS"`)
698
- * @param options.skipChecks Skip checks like making sure init signature is still valid and swap
699
- * wasn't commited yet (this is handled on swap creation, if you commit right after quoting, you
700
- * can use `skipChecks=true`)
701
- * @param options.manualSettlementSmartChainSigner Optional smart chain signer to create a manual claim (settlement) transaction
702
- * @param options.maxWaitTillAutomaticSettlementSeconds Maximum time to wait for an automatic settlement after
703
- * the bitcoin transaction is confirmed (defaults to 60 seconds)
704
- */
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 [];
725
- }
726
- //////////////////////////////
727
- //// Commit
728
- /**
729
- * @inheritDoc
730
- *
731
- * @throws {Error} If invalid signer is provided that doesn't match the swap data
732
- */
733
- async commit(_signer, abortSignal, skipChecks, onBeforeTxSent) {
734
- const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
735
- this.checkSigner(signer);
736
- let txCount = 0;
737
- const txs = await this.txsCommit(skipChecks);
738
- const result = await this.wrapper._chain.sendAndConfirm(signer, txs, true, abortSignal, undefined, (txId) => {
739
- txCount++;
740
- if (onBeforeTxSent != null && txCount === txs.length)
741
- onBeforeTxSent(txId);
742
- return Promise.resolve();
743
- });
744
- this._commitTxId = result[result.length - 1];
745
- if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED || this._state === FromBTCSwapState.QUOTE_EXPIRED) {
746
- await this._saveAndEmit(FromBTCSwapState.CLAIM_COMMITED);
747
- }
748
- return this._commitTxId;
749
- }
750
- /**
751
- * @inheritDoc
752
- */
753
- async waitTillCommited(abortSignal) {
754
- if (this._state === FromBTCSwapState.CLAIM_COMMITED || this._state === FromBTCSwapState.CLAIM_CLAIMED)
755
- return Promise.resolve();
756
- if (this._state !== FromBTCSwapState.PR_CREATED && this._state !== FromBTCSwapState.QUOTE_SOFT_EXPIRED)
757
- throw new Error("Invalid state");
758
- const abortController = (0, Utils_1.extendAbortController)(abortSignal);
759
- const result = await Promise.race([
760
- this.watchdogWaitTillCommited(undefined, abortController.signal),
761
- this.waitTillState(FromBTCSwapState.CLAIM_COMMITED, "gte", abortController.signal).then(() => 0)
762
- ]);
763
- abortController.abort();
764
- if (result === 0)
765
- this.logger.debug("waitTillCommited(): Resolved from state changed");
766
- if (result === true)
767
- this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
768
- if (result === false) {
769
- this.logger.debug("waitTillCommited(): Resolved from watchdog - signature expired");
770
- if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
771
- await this._saveAndEmit(FromBTCSwapState.QUOTE_EXPIRED);
772
- }
773
- return;
774
- }
775
- if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
776
- await this._saveAndEmit(FromBTCSwapState.CLAIM_COMMITED);
777
- }
778
- }
779
- //////////////////////////////
780
- //// Claim
781
- /**
782
- * Returns transactions for settling (claiming) the swap if the swap requires manual settlement, you can check so
783
- * with isClaimable. After sending the transaction manually be sure to call the waitTillClaimed function to wait
784
- * till the claim transaction is observed, processed by the SDK and state of the swap properly updated.
785
- *
786
- * @remarks
787
- * Might also return transactions necessary to sync the bitcoin light client.
788
- *
789
- * @param _signer Address of the signer to create the claim transactions for
790
- *
791
- * @throws {Error} If the swap is in invalid state (must be {@link FromBTCSwapState.BTC_TX_CONFIRMED})
792
- */
793
- async txsClaim(_signer) {
794
- let signer = undefined;
795
- if (_signer != null) {
796
- if (typeof (_signer) === "string") {
797
- signer = _signer;
798
- }
799
- else if ((0, base_1.isAbstractSigner)(_signer)) {
800
- signer = _signer;
801
- }
802
- else {
803
- signer = await this.wrapper._chain.wrapSigner(_signer);
804
- }
805
- }
806
- if (this._state !== FromBTCSwapState.BTC_TX_CONFIRMED)
807
- throw new Error("Must be in BTC_TX_CONFIRMED state!");
808
- if (this.txId == null || this.vout == null)
809
- throw new Error("Bitcoin transaction ID not known!");
810
- const tx = await this.wrapper._btcRpc.getTransaction(this.txId);
811
- if (tx == null)
812
- throw new Error("Bitcoin transaction not found on the network!");
813
- this.requiredConfirmations ??= this.inferRequiredConfirmationsCount(tx, this.vout);
814
- if (this.requiredConfirmations == null)
815
- throw new Error("Cannot create claim transaction, because required confirmations are not known and cannot be infered! This can happen after a swap is recovered.");
816
- if (tx.blockhash == null || tx.confirmations == null || tx.blockheight == null || tx.confirmations < this.requiredConfirmations)
817
- throw new Error("Bitcoin transaction not confirmed yet!");
818
- return await this.wrapper._contract.txsClaimWithTxData(signer ?? this._getInitiator(), this._data, {
819
- blockhash: tx.blockhash,
820
- confirmations: tx.confirmations,
821
- txid: tx.txid,
822
- hex: tx.hex,
823
- height: tx.blockheight
824
- }, this.requiredConfirmations, this.vout, undefined, this.wrapper._synchronizer, true);
825
- }
826
- /**
827
- * Settles the swap by claiming the funds on the destination chain if the swap requires manual settlement, you can
828
- * check so with isClaimable.
829
- *
830
- * @remarks
831
- * Might also sync the bitcoin light client during the process.
832
- *
833
- * @param _signer Signer to use for signing the settlement transactions, can also be different to the recipient
834
- * @param abortSignal Abort signal
835
- * @param onBeforeTxSent Optional callback triggered before the claim transaction is broadcasted
836
- *
837
- * @returns Transaction ID of the settlement (claim) transaction on the destination smart chain
838
- */
839
- async claim(_signer, abortSignal, onBeforeTxSent) {
840
- const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
841
- let txIds;
842
- try {
843
- let txCount = 0;
844
- const txs = await this.txsClaim(signer);
845
- txIds = await this.wrapper._chain.sendAndConfirm(signer, txs, true, abortSignal, undefined, (txId) => {
846
- txCount++;
847
- if (onBeforeTxSent != null && txCount === txs.length)
848
- onBeforeTxSent(txId);
849
- return Promise.resolve();
850
- });
851
- }
852
- catch (e) {
853
- this.logger.info("claim(): Failed to claim ourselves, checking swap claim state...");
854
- if (this._state === FromBTCSwapState.CLAIM_CLAIMED) {
855
- this.logger.info("claim(): Transaction state is CLAIM_CLAIMED, swap was successfully claimed by the watchtower");
856
- return this._claimTxId;
857
- }
858
- const status = await this.wrapper._contract.getCommitStatus(this._getInitiator(), this._data);
859
- if (status?.type === base_1.SwapCommitStateType.PAID) {
860
- this.logger.info("claim(): Transaction commit status is PAID, swap was successfully claimed by the watchtower");
861
- if (this._claimTxId == null)
862
- this._claimTxId = await status.getClaimTxId();
863
- const txId = buffer_1.Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
864
- await this._setBitcoinTxId(txId);
865
- await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
866
- return this._claimTxId;
867
- }
868
- throw e;
869
- }
870
- this._claimTxId = txIds[txIds.length - 1];
871
- if (this._state === FromBTCSwapState.CLAIM_COMMITED || this._state === FromBTCSwapState.BTC_TX_CONFIRMED ||
872
- this._state === FromBTCSwapState.EXPIRED || this._state === FromBTCSwapState.FAILED) {
873
- await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
874
- }
875
- return txIds[txIds.length - 1];
876
- }
877
- /**
878
- * @inheritDoc
879
- *
880
- * @throws {Error} If swap is in invalid state (must be {@link FromBTCSwapState.BTC_TX_CONFIRMED})
881
- * @throws {Error} If the LP refunded sooner than we were able to claim
882
- */
883
- async waitTillClaimed(maxWaitTimeSeconds, abortSignal) {
884
- if (this._state === FromBTCSwapState.CLAIM_CLAIMED)
885
- return Promise.resolve(true);
886
- if (this._state !== FromBTCSwapState.BTC_TX_CONFIRMED)
887
- throw new Error("Invalid state (not BTC_TX_CONFIRMED)");
888
- const abortController = (0, Utils_1.extendAbortController)(abortSignal);
889
- let timedOut = false;
890
- if (maxWaitTimeSeconds != null) {
891
- const timeout = setTimeout(() => {
892
- timedOut = true;
893
- abortController.abort();
894
- }, maxWaitTimeSeconds * 1000);
895
- abortController.signal.addEventListener("abort", () => clearTimeout(timeout));
896
- }
897
- let res;
898
- try {
899
- res = await Promise.race([
900
- this.watchdogWaitTillResult(undefined, abortController.signal),
901
- this.waitTillState(FromBTCSwapState.CLAIM_CLAIMED, "eq", abortController.signal).then(() => 0),
902
- this.waitTillState(FromBTCSwapState.FAILED, "eq", abortController.signal).then(() => 1),
903
- ]);
904
- abortController.abort();
905
- }
906
- catch (e) {
907
- abortController.abort();
908
- if (timedOut)
909
- return false;
910
- throw e;
911
- }
912
- if (res === 0) {
913
- this.logger.debug("waitTillClaimed(): Resolved from state change (CLAIM_CLAIMED)");
914
- return true;
915
- }
916
- if (res === 1) {
917
- this.logger.debug("waitTillClaimed(): Resolved from state change (FAILED)");
918
- throw new Error("Offerer refunded during claiming");
919
- }
920
- this.logger.debug("waitTillClaimed(): Resolved from watchdog");
921
- if (res?.type === base_1.SwapCommitStateType.PAID) {
922
- if (this._state !== FromBTCSwapState.CLAIM_CLAIMED) {
923
- if (this._claimTxId == null)
924
- this._claimTxId = await res.getClaimTxId();
925
- const txId = buffer_1.Buffer.from(await res.getClaimResult(), "hex").reverse().toString("hex");
926
- await this._setBitcoinTxId(txId);
927
- await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
928
- }
929
- }
930
- if (res?.type === base_1.SwapCommitStateType.NOT_COMMITED || res?.type === base_1.SwapCommitStateType.EXPIRED) {
931
- if (this._state !== FromBTCSwapState.CLAIM_CLAIMED &&
932
- this._state !== FromBTCSwapState.FAILED) {
933
- if (res.getRefundTxId != null)
934
- this._refundTxId = await res.getRefundTxId();
935
- await this._saveAndEmit(FromBTCSwapState.FAILED);
936
- }
937
- throw new Error("Swap expired while waiting for claim!");
938
- }
939
- return true;
940
- }
941
- //////////////////////////////
942
- //// Storage
943
- /**
944
- * @inheritDoc
945
- */
946
- serialize() {
947
- return {
948
- ...super.serialize(),
949
- address: this.address,
950
- amount: this.amount == null ? null : this.amount.toString(10),
951
- requiredConfirmations: this.requiredConfirmations,
952
- senderAddress: this.senderAddress,
953
- txId: this.txId,
954
- vout: this.vout,
955
- btcTxConfirmedAt: this.btcTxConfirmedAt
956
- };
957
- }
958
- //////////////////////////////
959
- //// Swap ticks & sync
960
- /**
961
- * Checks the swap's state on-chain and compares it to its internal state, updates/changes it according to on-chain
962
- * data
963
- *
964
- * @private
965
- */
966
- async syncStateFromChain(quoteDefinitelyExpired, commitStatus) {
967
- if (this._state === FromBTCSwapState.PR_CREATED ||
968
- this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED ||
969
- this._state === FromBTCSwapState.CLAIM_COMMITED ||
970
- this._state === FromBTCSwapState.BTC_TX_CONFIRMED ||
971
- this._state === FromBTCSwapState.EXPIRED) {
972
- let quoteExpired = false;
973
- if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
974
- quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired(); //Make sure we check for expiry here, to prevent race conditions
975
- }
976
- const status = commitStatus ?? await this.wrapper._contract.getCommitStatus(this._getInitiator(), this._data);
977
- if (status != null && await this._forciblySetOnchainState(status))
978
- return true;
979
- if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
980
- if (quoteExpired) {
981
- this._state = FromBTCSwapState.QUOTE_EXPIRED;
982
- return true;
983
- }
984
- }
985
- }
986
- return false;
987
- }
988
- /**
989
- * @inheritDoc
990
- * @internal
991
- */
992
- _shouldFetchOnchainState() {
993
- return this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED ||
994
- this._state === FromBTCSwapState.CLAIM_COMMITED || this._state === FromBTCSwapState.BTC_TX_CONFIRMED ||
995
- this._state === FromBTCSwapState.EXPIRED;
996
- }
997
- /**
998
- * @inheritDoc
999
- * @internal
1000
- */
1001
- _shouldFetchExpiryStatus() {
1002
- return this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED;
1003
- }
1004
- /**
1005
- * @inheritDoc
1006
- * @internal
1007
- */
1008
- async _sync(save, quoteDefinitelyExpired, commitStatus) {
1009
- const changed = await this.syncStateFromChain(quoteDefinitelyExpired, commitStatus);
1010
- if (changed && save)
1011
- await this._saveAndEmit();
1012
- return changed;
1013
- }
1014
- /**
1015
- * @inheritDoc
1016
- * @internal
1017
- */
1018
- async _forciblySetOnchainState(status) {
1019
- switch (status.type) {
1020
- case base_1.SwapCommitStateType.PAID:
1021
- if (this._claimTxId == null)
1022
- this._claimTxId = await status.getClaimTxId();
1023
- const txId = buffer_1.Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
1024
- await this._setBitcoinTxId(txId);
1025
- this._state = FromBTCSwapState.CLAIM_CLAIMED;
1026
- return true;
1027
- case base_1.SwapCommitStateType.NOT_COMMITED:
1028
- if (this._refundTxId == null && status.getRefundTxId)
1029
- this._refundTxId = await status.getRefundTxId();
1030
- if (this._refundTxId != null) {
1031
- this._state = FromBTCSwapState.FAILED;
1032
- return true;
1033
- }
1034
- break;
1035
- case base_1.SwapCommitStateType.EXPIRED:
1036
- if (this._refundTxId == null && status.getRefundTxId)
1037
- this._refundTxId = await status.getRefundTxId();
1038
- this._state = this._refundTxId == null ? FromBTCSwapState.QUOTE_EXPIRED : FromBTCSwapState.FAILED;
1039
- return true;
1040
- case base_1.SwapCommitStateType.COMMITED:
1041
- let save = false;
1042
- if (this._state !== FromBTCSwapState.CLAIM_COMMITED && this._state !== FromBTCSwapState.BTC_TX_CONFIRMED && this._state !== FromBTCSwapState.EXPIRED) {
1043
- this._state = FromBTCSwapState.CLAIM_COMMITED;
1044
- save = true;
1045
- }
1046
- if (this.address == null)
1047
- return save;
1048
- this.btcTxLastChecked = Date.now();
1049
- const res = await this.getBitcoinPayment();
1050
- if (res != null) {
1051
- if (this.txId !== res.txId) {
1052
- if (res.inputAddresses != null)
1053
- this.senderAddress = res.inputAddresses[0];
1054
- this.txId = res.txId;
1055
- this.vout = res.vout;
1056
- save = true;
1057
- }
1058
- if (this.requiredConfirmations != null && res.confirmations >= this.requiredConfirmations) {
1059
- this.btcTxConfirmedAt ??= Date.now();
1060
- this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
1061
- save = true;
1062
- }
1063
- }
1064
- return save;
1065
- }
1066
- return false;
1067
- }
1068
- /**
1069
- * @inheritDoc
1070
- * @internal
1071
- */
1072
- async _tick(save) {
1073
- switch (this._state) {
1074
- case FromBTCSwapState.PR_CREATED:
1075
- if (this.expiry < Date.now()) {
1076
- this._state = FromBTCSwapState.QUOTE_SOFT_EXPIRED;
1077
- if (save)
1078
- await this._saveAndEmit();
1079
- return true;
1080
- }
1081
- break;
1082
- case FromBTCSwapState.CLAIM_COMMITED:
1083
- if (this.getTimeoutTime() < Date.now()) {
1084
- this._state = FromBTCSwapState.EXPIRED;
1085
- if (save)
1086
- await this._saveAndEmit();
1087
- return true;
1088
- }
1089
- case FromBTCSwapState.EXPIRED:
1090
- //Check if bitcoin payment was received at least every 2 minutes
1091
- if (this.btcTxLastChecked == null || Date.now() - this.btcTxLastChecked > 120_000) {
1092
- if (this.address != null)
1093
- try {
1094
- this.btcTxLastChecked = Date.now();
1095
- const res = await this.getBitcoinPayment();
1096
- if (res != null) {
1097
- let shouldSave = false;
1098
- if (this.txId !== res.txId) {
1099
- this.txId = res.txId;
1100
- this.vout = res.vout;
1101
- if (res.inputAddresses != null)
1102
- this.senderAddress = res.inputAddresses[0];
1103
- shouldSave = true;
1104
- }
1105
- if (this.requiredConfirmations != null && res.confirmations >= this.requiredConfirmations) {
1106
- this.btcTxConfirmedAt ??= Date.now();
1107
- this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
1108
- if (save)
1109
- await this._saveAndEmit();
1110
- shouldSave = true;
1111
- }
1112
- if (shouldSave && save)
1113
- await this._saveAndEmit();
1114
- return shouldSave;
1115
- }
1116
- }
1117
- catch (e) {
1118
- this.logger.warn("tickSwap(" + this.getIdentifierHashString() + "): ", e);
1119
- }
1120
- }
1121
- break;
1122
- }
1123
- return false;
1124
- }
1125
- }
1126
- exports.FromBTCSwap = FromBTCSwap;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FromBTCSwap = exports.isFromBTCSwapInit = exports.FromBTCSwapState = void 0;
4
+ const IFromBTCSelfInitSwap_1 = require("../IFromBTCSelfInitSwap");
5
+ const SwapType_1 = require("../../../../enums/SwapType");
6
+ const base_1 = require("@atomiqlabs/base");
7
+ const buffer_1 = require("buffer");
8
+ const Utils_1 = require("../../../../utils/Utils");
9
+ const BitcoinUtils_1 = require("../../../../utils/BitcoinUtils");
10
+ const IBitcoinWallet_1 = require("../../../../bitcoin/wallet/IBitcoinWallet");
11
+ const btc_signer_1 = require("@scure/btc-signer");
12
+ const SingleAddressBitcoinWallet_1 = require("../../../../bitcoin/wallet/SingleAddressBitcoinWallet");
13
+ const IEscrowSelfInitSwap_1 = require("../../IEscrowSelfInitSwap");
14
+ const TokenAmount_1 = require("../../../../types/TokenAmount");
15
+ const Token_1 = require("../../../../types/Token");
16
+ const Logger_1 = require("../../../../utils/Logger");
17
+ const BitcoinWalletUtils_1 = require("../../../../utils/BitcoinWalletUtils");
18
+ /**
19
+ * State enum for legacy escrow based Bitcoin -> Smart chain swaps.
20
+ *
21
+ * @category Swaps/Legacy/Bitcoin → Smart chain
22
+ */
23
+ var FromBTCSwapState;
24
+ (function (FromBTCSwapState) {
25
+ /**
26
+ * Bitcoin swap address has expired and the intermediary (LP) has already refunded
27
+ * its funds. No BTC should be sent anymore!
28
+ */
29
+ FromBTCSwapState[FromBTCSwapState["FAILED"] = -4] = "FAILED";
30
+ /**
31
+ * Bitcoin swap address has expired, user should not send any BTC anymore! Though
32
+ * the intermediary (LP) hasn't refunded yet. So if there is a transaction already
33
+ * in-flight the swap might still succeed.
34
+ */
35
+ FromBTCSwapState[FromBTCSwapState["EXPIRED"] = -3] = "EXPIRED";
36
+ /**
37
+ * Swap has expired for good and there is no way how it can be executed anymore
38
+ */
39
+ FromBTCSwapState[FromBTCSwapState["QUOTE_EXPIRED"] = -2] = "QUOTE_EXPIRED";
40
+ /**
41
+ * A swap is almost expired, and it should be presented to the user as expired, though
42
+ * there is still a chance that it will be processed
43
+ */
44
+ FromBTCSwapState[FromBTCSwapState["QUOTE_SOFT_EXPIRED"] = -1] = "QUOTE_SOFT_EXPIRED";
45
+ /**
46
+ * Swap quote was created, use the {@link FromBTCSwap.commit} or {@link FromBTCSwap.txsCommit} functions
47
+ * to initiate it by creating the swap escrow on the destination smart chain
48
+ */
49
+ FromBTCSwapState[FromBTCSwapState["PR_CREATED"] = 0] = "PR_CREATED";
50
+ /**
51
+ * Swap escrow was initiated (committed) on the destination chain, user can send the BTC to the
52
+ * swap address with the {@link FromBTCSwap.getFundedPsbt}, {@link FromBTCSwap.getAddress} or
53
+ * {@link FromBTCSwap.getHyperlink} functions.
54
+ */
55
+ FromBTCSwapState[FromBTCSwapState["CLAIM_COMMITED"] = 1] = "CLAIM_COMMITED";
56
+ /**
57
+ * Input bitcoin transaction was confirmed, wait for automatic settlement by the watchtowers
58
+ * using the {@link FromBTCSwap.waitTillClaimed} function or settle manually using the {@link FromBTCSwap.claim}
59
+ * or {@link FromBTCSwap.txsClaim} function.
60
+ */
61
+ FromBTCSwapState[FromBTCSwapState["BTC_TX_CONFIRMED"] = 2] = "BTC_TX_CONFIRMED";
62
+ /**
63
+ * Swap successfully settled and funds received on the destination chain
64
+ */
65
+ FromBTCSwapState[FromBTCSwapState["CLAIM_CLAIMED"] = 3] = "CLAIM_CLAIMED";
66
+ })(FromBTCSwapState = exports.FromBTCSwapState || (exports.FromBTCSwapState = {}));
67
+ const FromBTCSwapStateDescription = {
68
+ [FromBTCSwapState.FAILED]: "Bitcoin swap address has expired and the intermediary (LP) has already refunded its funds. No BTC should be sent anymore!",
69
+ [FromBTCSwapState.EXPIRED]: "Bitcoin swap address has expired, user should not send any BTC anymore! Though the intermediary (LP) hasn't refunded yet. So if there is a transaction already in-flight the swap might still succeed.",
70
+ [FromBTCSwapState.QUOTE_EXPIRED]: "Swap has expired for good and there is no way how it can be executed anymore",
71
+ [FromBTCSwapState.QUOTE_SOFT_EXPIRED]: "The swap is expired, though there is still a chance that it will be processed",
72
+ [FromBTCSwapState.PR_CREATED]: "Swap quote was created, initiate it by creating the swap escrow on the destination smart chain",
73
+ [FromBTCSwapState.CLAIM_COMMITED]: "Swap escrow was initiated (committed) on the destination chain, user can send the BTC to the Bitcoin swap address.",
74
+ [FromBTCSwapState.BTC_TX_CONFIRMED]: "Input bitcoin transaction was confirmed, wait for automatic settlement by the watchtower or settle manually.",
75
+ [FromBTCSwapState.CLAIM_CLAIMED]: "Swap successfully settled and funds received on the destination chain"
76
+ };
77
+ function isFromBTCSwapInit(obj) {
78
+ return typeof (obj.data) === "object" &&
79
+ (obj.address == null || typeof (obj.address) === "string") &&
80
+ (obj.amount == null || typeof (obj.amount) === "bigint") &&
81
+ (obj.requiredConfirmations == null || typeof (obj.requiredConfirmations) === "number") &&
82
+ (0, IEscrowSelfInitSwap_1.isIEscrowSelfInitSwapInit)(obj);
83
+ }
84
+ exports.isFromBTCSwapInit = isFromBTCSwapInit;
85
+ /**
86
+ * Legacy escrow (PrTLC) based swap for Bitcoin -> Smart chains, requires manual initiation
87
+ * of the swap escrow on the destination chain.
88
+ *
89
+ * @category Swaps/Legacy/Bitcoin → Smart chain
90
+ */
91
+ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
92
+ constructor(wrapper, initOrObject) {
93
+ if (isFromBTCSwapInit(initOrObject) && initOrObject.url != null)
94
+ initOrObject.url += "/frombtc";
95
+ super(wrapper, initOrObject);
96
+ this.TYPE = SwapType_1.SwapType.FROM_BTC;
97
+ /**
98
+ * @internal
99
+ */
100
+ this.swapStateName = (state) => FromBTCSwapState[state];
101
+ /**
102
+ * @internal
103
+ */
104
+ this.swapStateDescription = FromBTCSwapStateDescription;
105
+ /**
106
+ * @internal
107
+ */
108
+ this.inputToken = Token_1.BitcoinTokens.BTC;
109
+ if (isFromBTCSwapInit(initOrObject)) {
110
+ this._state = FromBTCSwapState.PR_CREATED;
111
+ this._data = initOrObject.data;
112
+ this.feeRate = initOrObject.feeRate;
113
+ this.address = initOrObject.address;
114
+ this.amount = initOrObject.amount;
115
+ this.requiredConfirmations = initOrObject.requiredConfirmations;
116
+ }
117
+ else {
118
+ this.address = initOrObject.address;
119
+ this.amount = (0, Utils_1.toBigInt)(initOrObject.amount);
120
+ this.senderAddress = initOrObject.senderAddress;
121
+ this.txId = initOrObject.txId;
122
+ this.vout = initOrObject.vout;
123
+ this.requiredConfirmations = initOrObject.requiredConfirmations ?? this._data.getConfirmationsHint();
124
+ this.btcTxConfirmedAt = initOrObject.btcTxConfirmedAt;
125
+ }
126
+ this.tryRecomputeSwapPrice();
127
+ this.logger = (0, Logger_1.getLogger)("FromBTC(" + this.getIdentifierHashString() + "): ");
128
+ }
129
+ /**
130
+ * @inheritDoc
131
+ * @internal
132
+ */
133
+ getSwapData() {
134
+ return this._data;
135
+ }
136
+ /**
137
+ * @inheritDoc
138
+ * @internal
139
+ */
140
+ upgradeVersion() {
141
+ if (this.version == null) {
142
+ switch (this._state) {
143
+ case -2:
144
+ this._state = FromBTCSwapState.FAILED;
145
+ break;
146
+ case -1:
147
+ this._state = FromBTCSwapState.QUOTE_EXPIRED;
148
+ break;
149
+ case 0:
150
+ this._state = FromBTCSwapState.PR_CREATED;
151
+ break;
152
+ case 1:
153
+ this._state = FromBTCSwapState.CLAIM_COMMITED;
154
+ break;
155
+ case 2:
156
+ this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
157
+ break;
158
+ case 3:
159
+ this._state = FromBTCSwapState.CLAIM_CLAIMED;
160
+ break;
161
+ }
162
+ this.version = 1;
163
+ }
164
+ }
165
+ //////////////////////////////
166
+ //// Getters & utils
167
+ /**
168
+ * Returns bitcoin address where the on-chain BTC should be sent to
169
+ */
170
+ getAddress() {
171
+ if (this._state === FromBTCSwapState.PR_CREATED)
172
+ throw new Error("Cannot get bitcoin address of non-initiated swaps! Initiate swap first with commit() or txsCommit().");
173
+ return this.address ?? "";
174
+ }
175
+ /**
176
+ * Unsafe bitcoin hyperlink getter, returns the address even before the swap is committed!
177
+ *
178
+ * @private
179
+ */
180
+ _getHyperlink() {
181
+ return this.address == null || this.amount == null ? "" : "bitcoin:" + this.address + "?amount=" + encodeURIComponent((Number(this.amount) / 100000000).toString(10));
182
+ }
183
+ /**
184
+ * @inheritDoc
185
+ */
186
+ getHyperlink() {
187
+ if (this._state === FromBTCSwapState.PR_CREATED)
188
+ throw new Error("Cannot get bitcoin address of non-initiated swaps! Initiate swap first with commit() or txsCommit().");
189
+ return this._getHyperlink();
190
+ }
191
+ /**
192
+ * @inheritDoc
193
+ */
194
+ getInputAddress() {
195
+ return this.senderAddress ?? null;
196
+ }
197
+ /**
198
+ * @inheritDoc
199
+ */
200
+ getInputTxId() {
201
+ return this.txId ?? null;
202
+ }
203
+ /**
204
+ * Returns timeout time (in UNIX milliseconds) when the on-chain address will expire and no funds should be sent
205
+ * to that address anymore
206
+ */
207
+ getTimeoutTime() {
208
+ return Number(this.wrapper._getOnchainSendTimeout(this._data, this.requiredConfirmations ?? 6)) * 1000;
209
+ }
210
+ /**
211
+ * @inheritDoc
212
+ */
213
+ requiresAction() {
214
+ return this.isClaimable() || (this._state === FromBTCSwapState.CLAIM_COMMITED && this.getTimeoutTime() > Date.now() && this.txId == null);
215
+ }
216
+ /**
217
+ * @inheritDoc
218
+ */
219
+ isFinished() {
220
+ return this._state === FromBTCSwapState.CLAIM_CLAIMED || this._state === FromBTCSwapState.QUOTE_EXPIRED || this._state === FromBTCSwapState.FAILED;
221
+ }
222
+ /**
223
+ * @inheritDoc
224
+ */
225
+ isClaimable() {
226
+ return this._state === FromBTCSwapState.BTC_TX_CONFIRMED;
227
+ }
228
+ /**
229
+ * @inheritDoc
230
+ */
231
+ isSuccessful() {
232
+ return this._state === FromBTCSwapState.CLAIM_CLAIMED;
233
+ }
234
+ /**
235
+ * @inheritDoc
236
+ */
237
+ isFailed() {
238
+ return this._state === FromBTCSwapState.FAILED || this._state === FromBTCSwapState.EXPIRED;
239
+ }
240
+ /**
241
+ * @inheritDoc
242
+ */
243
+ isInProgress() {
244
+ return this._state === FromBTCSwapState.CLAIM_COMMITED ||
245
+ this._state === FromBTCSwapState.BTC_TX_CONFIRMED;
246
+ }
247
+ /**
248
+ * @inheritDoc
249
+ */
250
+ isQuoteExpired() {
251
+ return this._state === FromBTCSwapState.QUOTE_EXPIRED;
252
+ }
253
+ /**
254
+ * @inheritDoc
255
+ */
256
+ isQuoteSoftExpired() {
257
+ return this._state === FromBTCSwapState.QUOTE_EXPIRED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED;
258
+ }
259
+ /**
260
+ * @inheritDoc
261
+ * @internal
262
+ */
263
+ canCommit(skipQuoteExpiryChecks) {
264
+ if (this._state !== FromBTCSwapState.PR_CREATED && (!skipQuoteExpiryChecks || this._state !== FromBTCSwapState.QUOTE_SOFT_EXPIRED))
265
+ return false;
266
+ if (this.requiredConfirmations == null)
267
+ return false;
268
+ const expiry = this.wrapper._getOnchainSendTimeout(this._data, this.requiredConfirmations);
269
+ const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
270
+ return (expiry - currentTimestamp) >= this.wrapper._options.minSendWindow;
271
+ }
272
+ //////////////////////////////
273
+ //// Amounts & fees
274
+ /**
275
+ * @inheritDoc
276
+ */
277
+ getInputToken() {
278
+ return Token_1.BitcoinTokens.BTC;
279
+ }
280
+ /**
281
+ * @inheritDoc
282
+ */
283
+ getInput() {
284
+ return (0, TokenAmount_1.toTokenAmount)(this.amount ?? null, this.inputToken, this.wrapper._prices);
285
+ }
286
+ /**
287
+ * Returns claimer bounty, acting as a reward for watchtowers to claim the swap automatically,
288
+ * this amount is pre-funded by the user on the destination chain when the swap escrow
289
+ * is initiated. For total pre-funded deposit amount see {@link getTotalDeposit}.
290
+ */
291
+ getClaimerBounty() {
292
+ return (0, TokenAmount_1.toTokenAmount)(this._data.getClaimerBounty(), this.wrapper._tokens[this._data.getDepositToken()], this.wrapper._prices);
293
+ }
294
+ //////////////////////////////
295
+ //// Bitcoin tx
296
+ /**
297
+ * If the required number of confirmations is not known, this function tries to infer it by looping through
298
+ * possible confirmation targets and comparing the claim hashes
299
+ *
300
+ * @param btcTx Bitcoin transaction
301
+ * @param vout Output index of the desired output in the bitcoin transaction
302
+ *
303
+ * @private
304
+ */
305
+ inferRequiredConfirmationsCount(btcTx, vout) {
306
+ const txOut = btcTx.outs[vout];
307
+ for (let i = 1; i <= 20; i++) {
308
+ const computedClaimHash = this.wrapper._contract.getHashForOnchain(buffer_1.Buffer.from(txOut.scriptPubKey.hex, "hex"), BigInt(txOut.value), i);
309
+ if (computedClaimHash.toString("hex") === this._data.getClaimHash()) {
310
+ return i;
311
+ }
312
+ }
313
+ }
314
+ /**
315
+ * @inheritDoc
316
+ */
317
+ getRequiredConfirmationsCount() {
318
+ return this.requiredConfirmations ?? NaN;
319
+ }
320
+ /**
321
+ * Checks whether a bitcoin payment was already made, returns the payment or `null` when no payment has been made.
322
+ *
323
+ * @internal
324
+ */
325
+ async getBitcoinPayment() {
326
+ const txoHashHint = this._data.getTxoHashHint();
327
+ if (txoHashHint == null)
328
+ throw new Error("Swap data doesn't include the txo hash hint! Cannot check bitcoin transaction!");
329
+ if (this.address == null)
330
+ throw new Error("Cannot check bitcoin payment, because the address is not known! This can happen after a swap is recovered.");
331
+ const result = await this.wrapper._btcRpc.checkAddressTxos(this.address, buffer_1.Buffer.from(txoHashHint, "hex"));
332
+ if (result == null)
333
+ return null;
334
+ if (this.requiredConfirmations == null) {
335
+ this.requiredConfirmations = this.inferRequiredConfirmationsCount(result.tx, result.vout);
336
+ }
337
+ return {
338
+ inputAddresses: result.tx.inputAddresses,
339
+ txId: result.tx.txid,
340
+ vout: result.vout,
341
+ confirmations: result.tx.confirmations ?? 0,
342
+ targetConfirmations: this.getRequiredConfirmationsCount()
343
+ };
344
+ }
345
+ /**
346
+ * Used to set the txId of the bitcoin payment from the on-chain events listener
347
+ *
348
+ * @param txId Transaction ID that settled the swap on the smart chain
349
+ *
350
+ * @internal
351
+ */
352
+ async _setBitcoinTxId(txId) {
353
+ if (this.txId !== txId || this.address == null || this.vout == null || this.senderAddress == null || this.amount == null) {
354
+ const btcTx = await this.wrapper._btcRpc.getTransaction(txId);
355
+ if (btcTx == null)
356
+ return;
357
+ const txoHashHint = this._data.getTxoHashHint();
358
+ if (txoHashHint != null) {
359
+ const expectedTxoHash = buffer_1.Buffer.from(txoHashHint, "hex");
360
+ const vout = btcTx.outs.findIndex(out => (0, Utils_1.getTxoHash)(out.scriptPubKey.hex, out.value).equals(expectedTxoHash));
361
+ if (vout !== -1) {
362
+ this.vout = vout;
363
+ //If amount or address are not known, parse them from the bitcoin tx
364
+ // this can happen if the swap is recovered from on-chain data and
365
+ // hence doesn't contain the address and amount data
366
+ if (this.amount == null)
367
+ this.amount = BigInt(btcTx.outs[vout].value);
368
+ if (this.address == null)
369
+ try {
370
+ this.address = (0, BitcoinUtils_1.fromOutputScript)(this.wrapper._options.bitcoinNetwork, btcTx.outs[vout].scriptPubKey.hex);
371
+ }
372
+ catch (e) {
373
+ this.logger.warn("_setBitcoinTxId(): Failed to parse address from output script: ", e);
374
+ }
375
+ if (this.requiredConfirmations == null) {
376
+ this.requiredConfirmations = this.inferRequiredConfirmationsCount(btcTx, vout);
377
+ }
378
+ }
379
+ }
380
+ if (btcTx.inputAddresses != null) {
381
+ this.senderAddress = btcTx.inputAddresses[0];
382
+ }
383
+ }
384
+ this.txId = txId;
385
+ }
386
+ /**
387
+ * @inheritDoc
388
+ *
389
+ * @throws {Error} if in invalid state (must be {@link FromBTCSwapState.CLAIM_COMMITED})
390
+ */
391
+ async waitForBitcoinTransaction(updateCallback, checkIntervalSeconds, abortSignal) {
392
+ if (this._state !== FromBTCSwapState.CLAIM_COMMITED && this._state !== FromBTCSwapState.EXPIRED)
393
+ throw new Error("Must be in COMMITED state!");
394
+ const txoHashHint = this._data.getTxoHashHint();
395
+ if (txoHashHint == null)
396
+ throw new Error("Swap data doesn't include the txo hash hint! Cannot check bitcoin transaction!");
397
+ if (this.address == null)
398
+ throw new Error("Cannot check bitcoin payment, because the address is not known! This can happen after a swap is recovered.");
399
+ let abortedDueToEnoughConfirmationsResult;
400
+ const abortController = (0, Utils_1.extendAbortController)(abortSignal);
401
+ const result = await this.wrapper._btcRpc.waitForAddressTxo(this.address, buffer_1.Buffer.from(txoHashHint, "hex"), this.requiredConfirmations ?? 6, //In case confirmation count is not known, we use a conservative estimate
402
+ (btcTx, vout, txEtaMs) => {
403
+ let requiredConfirmations = this.requiredConfirmations;
404
+ if (btcTx != null && vout != null && requiredConfirmations == null) {
405
+ requiredConfirmations = this.inferRequiredConfirmationsCount(btcTx, vout);
406
+ }
407
+ if (btcTx != null && (btcTx.txid !== this.txId || (this.requiredConfirmations == null && requiredConfirmations != null))) {
408
+ this.txId = btcTx.txid;
409
+ this.vout = vout;
410
+ this.requiredConfirmations = requiredConfirmations;
411
+ if (btcTx.inputAddresses != null)
412
+ this.senderAddress = btcTx.inputAddresses[0];
413
+ this._saveAndEmit().catch(e => {
414
+ this.logger.error("waitForBitcoinTransaction(): Failed to save swap from within waitForAddressTxo callback:", e);
415
+ });
416
+ }
417
+ //Abort the loop as soon as the transaction gets enough confirmations, this is required in case
418
+ // we pass a default 6 confirmations to the fn, but then are able to infer the actual confirmation
419
+ // target from the prior block
420
+ if (btcTx?.confirmations != null && requiredConfirmations != null && requiredConfirmations <= btcTx.confirmations && vout != null) {
421
+ abortedDueToEnoughConfirmationsResult = {
422
+ tx: btcTx,
423
+ vout
424
+ };
425
+ abortController.abort();
426
+ return;
427
+ }
428
+ if (updateCallback != null)
429
+ updateCallback(btcTx?.txid, btcTx == null ? undefined : (btcTx?.confirmations ?? 0), requiredConfirmations ?? NaN, txEtaMs);
430
+ }, abortController.signal, checkIntervalSeconds).catch(e => {
431
+ //We catch the case when the loop was aborted due to the transaction getting enough confirmations
432
+ if (abortedDueToEnoughConfirmationsResult != null)
433
+ return abortedDueToEnoughConfirmationsResult;
434
+ throw e;
435
+ });
436
+ if (abortSignal != null)
437
+ abortSignal.throwIfAborted();
438
+ this.txId = result.tx.txid;
439
+ this.vout = result.vout;
440
+ if (result.tx.inputAddresses != null)
441
+ this.senderAddress = result.tx.inputAddresses[0];
442
+ if (this._state !== FromBTCSwapState.CLAIM_CLAIMED &&
443
+ this._state !== FromBTCSwapState.FAILED) {
444
+ this.btcTxConfirmedAt ??= Date.now();
445
+ this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
446
+ }
447
+ await this._saveAndEmit();
448
+ return result.tx.txid;
449
+ }
450
+ /**
451
+ * Private getter of the funded PSBT that doesn't check current state
452
+ *
453
+ * @param _bitcoinWallet Bitcoin wallet to fund the PSBT with
454
+ * @param feeRate Optional bitcoin fee rate in sats/vB
455
+ * @param additionalOutputs Optional additional outputs that should also be included in the generated PSBT
456
+ *
457
+ * @private
458
+ */
459
+ async _getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs) {
460
+ if (this.address == null)
461
+ throw new Error("Cannot create funded PSBT, because the address is not known! This can happen after a swap is recovered.");
462
+ let bitcoinWallet;
463
+ if ((0, IBitcoinWallet_1.isIBitcoinWallet)(_bitcoinWallet)) {
464
+ bitcoinWallet = _bitcoinWallet;
465
+ }
466
+ else {
467
+ bitcoinWallet = new SingleAddressBitcoinWallet_1.SingleAddressBitcoinWallet(this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork, _bitcoinWallet);
468
+ }
469
+ //TODO: Maybe re-introduce fee rate check here if passed from the user
470
+ if (feeRate == null) {
471
+ feeRate = await bitcoinWallet.getFeeRate();
472
+ }
473
+ const basePsbt = new btc_signer_1.Transaction({
474
+ allowUnknownOutputs: true,
475
+ allowLegacyWitnessUtxo: true
476
+ });
477
+ basePsbt.addOutput({
478
+ amount: this.amount,
479
+ script: (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.address)
480
+ });
481
+ if (additionalOutputs != null)
482
+ additionalOutputs.forEach(output => {
483
+ basePsbt.addOutput({
484
+ amount: output.amount,
485
+ script: output.outputScript ?? (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, output.address)
486
+ });
487
+ });
488
+ const psbt = await bitcoinWallet.fundPsbt(basePsbt, feeRate);
489
+ //Sign every input
490
+ const signInputs = [];
491
+ for (let i = 0; i < psbt.inputsLength; i++) {
492
+ signInputs.push(i);
493
+ }
494
+ const serializedPsbt = buffer_1.Buffer.from(psbt.toPSBT());
495
+ return {
496
+ psbt,
497
+ psbtHex: serializedPsbt.toString("hex"),
498
+ psbtBase64: serializedPsbt.toString("base64"),
499
+ signInputs
500
+ };
501
+ }
502
+ /**
503
+ * @inheritDoc
504
+ */
505
+ getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs) {
506
+ if (this._state !== FromBTCSwapState.CLAIM_COMMITED)
507
+ throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
508
+ return this._getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs);
509
+ }
510
+ /**
511
+ * @inheritDoc
512
+ *
513
+ * @throws {Error} if the swap is in invalid state (not in {@link FromBTCSwapState.CLAIM_COMMITED}), or if
514
+ * the swap bitcoin address already expired.
515
+ */
516
+ async submitPsbt(_psbt) {
517
+ const psbt = (0, BitcoinUtils_1.parsePsbtTransaction)(_psbt);
518
+ if (this._state !== FromBTCSwapState.CLAIM_COMMITED)
519
+ throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
520
+ //Ensure not expired
521
+ if (this.getTimeoutTime() < Date.now()) {
522
+ throw new Error("Swap address expired!");
523
+ }
524
+ const output0 = psbt.getOutput(0);
525
+ if (this.amount != null && output0.amount !== this.amount)
526
+ throw new Error("PSBT output amount invalid, expected: " + this.amount + " got: " + output0.amount);
527
+ if (this.address != null) {
528
+ const expectedOutputScript = (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.address);
529
+ if (output0.script == null || !expectedOutputScript.equals(output0.script))
530
+ throw new Error("PSBT output script invalid!");
531
+ }
532
+ if (!psbt.isFinal)
533
+ psbt.finalize();
534
+ return await this.wrapper._btcRpc.sendRawTransaction(buffer_1.Buffer.from(psbt.toBytes(true, true)).toString("hex"));
535
+ }
536
+ /**
537
+ * @inheritDoc
538
+ */
539
+ async estimateBitcoinFee(_bitcoinWallet, feeRate) {
540
+ if (this.address == null || this.amount == null)
541
+ return null;
542
+ const bitcoinWallet = (0, BitcoinWalletUtils_1.toBitcoinWallet)(_bitcoinWallet, this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork);
543
+ const txFee = await bitcoinWallet.getTransactionFee(this.address, this.amount, feeRate);
544
+ if (txFee == null)
545
+ return null;
546
+ return (0, TokenAmount_1.toTokenAmount)(BigInt(txFee), Token_1.BitcoinTokens.BTC, this.wrapper._prices);
547
+ }
548
+ /**
549
+ * @inheritDoc
550
+ */
551
+ async sendBitcoinTransaction(wallet, feeRate) {
552
+ if (this.address == null || this.amount == null)
553
+ throw new Error("Cannot send bitcoin transaction, because the address is not known! This can happen after a swap is recovered.");
554
+ if (this._state !== FromBTCSwapState.CLAIM_COMMITED)
555
+ throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
556
+ //Ensure not expired
557
+ if (this.getTimeoutTime() < Date.now()) {
558
+ throw new Error("Swap address expired!");
559
+ }
560
+ if ((0, IBitcoinWallet_1.isIBitcoinWallet)(wallet)) {
561
+ return await wallet.sendTransaction(this.address, this.amount, feeRate);
562
+ }
563
+ else {
564
+ const { psbt, psbtHex, psbtBase64, signInputs } = await this.getFundedPsbt(wallet, feeRate);
565
+ const signedPsbt = await wallet.signPsbt({
566
+ psbt, psbtHex, psbtBase64
567
+ }, signInputs);
568
+ return await this.submitPsbt(signedPsbt);
569
+ }
570
+ }
571
+ //////////////////////////////
572
+ //// Execution
573
+ /**
574
+ * Executes the swap with the provided bitcoin wallet,
575
+ *
576
+ * @param dstSigner Signer on the destination network, needs to have the same address as the one specified when
577
+ * quote was created, this is required for legacy swaps because the destination wallet needs to actively open
578
+ * a bitcoin swap address to which the BTC is then sent, this means that the address also needs to have enough
579
+ * native tokens to pay for gas on the destination network
580
+ * @param wallet Bitcoin wallet to use to sign the bitcoin transaction, can also be null - then the execution waits
581
+ * till a transaction is received from an external wallet
582
+ * @param callbacks Callbacks to track the progress of the swap
583
+ * @param options Optional options for the swap like feeRate, AbortSignal, and timeouts/intervals
584
+ *
585
+ * @returns {boolean} Whether a swap was settled automatically by swap watchtowers or requires manual claim by the
586
+ * user, in case `false` is returned the user should call `swap.claim()` to settle the swap on the destination manually
587
+ */
588
+ async execute(dstSigner, wallet, callbacks, options) {
589
+ if (this._state === FromBTCSwapState.FAILED)
590
+ throw new Error("Swap failed!");
591
+ if (this._state === FromBTCSwapState.EXPIRED)
592
+ throw new Error("Swap address expired!");
593
+ if (this._state === FromBTCSwapState.QUOTE_EXPIRED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED)
594
+ throw new Error("Swap quote expired!");
595
+ if (this._state === FromBTCSwapState.CLAIM_CLAIMED)
596
+ throw new Error("Swap already settled!");
597
+ if (this._state === FromBTCSwapState.PR_CREATED) {
598
+ await this.commit(dstSigner, options?.abortSignal, undefined, callbacks?.onDestinationCommitSent);
599
+ }
600
+ if (this._state === FromBTCSwapState.CLAIM_COMMITED) {
601
+ if (wallet != null) {
602
+ const bitcoinPaymentSent = await this.getBitcoinPayment();
603
+ if (bitcoinPaymentSent == null) {
604
+ //Send btc tx
605
+ const txId = await this.sendBitcoinTransaction(wallet, options?.feeRate);
606
+ if (callbacks?.onSourceTransactionSent != null)
607
+ callbacks.onSourceTransactionSent(txId);
608
+ }
609
+ }
610
+ const txId = await this.waitForBitcoinTransaction(callbacks?.onSourceTransactionConfirmationStatus, options?.btcTxCheckIntervalSeconds, options?.abortSignal);
611
+ if (callbacks?.onSourceTransactionConfirmed != null)
612
+ callbacks.onSourceTransactionConfirmed(txId);
613
+ }
614
+ // @ts-ignore
615
+ if (this._state === FromBTCSwapState.CLAIM_CLAIMED)
616
+ return true;
617
+ if (this._state === FromBTCSwapState.BTC_TX_CONFIRMED) {
618
+ const success = await this.waitTillClaimed(options?.maxWaitTillAutomaticSettlementSeconds ?? 60, options?.abortSignal);
619
+ if (success && callbacks?.onSwapSettled != null)
620
+ callbacks.onSwapSettled(this.getOutputTxId());
621
+ return success;
622
+ }
623
+ throw new Error("Invalid state reached!");
624
+ }
625
+ /**
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
636
+ */
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 [
644
+ {
645
+ name: "Commit",
646
+ description: `Opens up the bitcoin swap address on the ${this.chainIdentifier} side`,
647
+ chain: this.chainIdentifier,
648
+ txs: await this.txsCommit(options?.skipChecks)
649
+ },
650
+ {
651
+ name: "Payment",
652
+ description: "Send funds to the bitcoin swap address",
653
+ 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
+ ]
665
+ }
666
+ ];
667
+ }
668
+ if (this._state === FromBTCSwapState.CLAIM_COMMITED) {
669
+ if (this.getTimeoutTime() < Date.now())
670
+ 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
+ ]
687
+ }
688
+ ];
689
+ }
690
+ throw new Error("Invalid swap state to obtain execution txns, required PR_CREATED or CLAIM_COMMITED");
691
+ }
692
+ /**
693
+ * @inheritDoc
694
+ *
695
+ * @param options.bitcoinFeeRate Optional fee rate to use for the created Bitcoin transaction
696
+ * @param options.bitcoinWallet Bitcoin wallet to use, when provided the function returns a funded
697
+ * psbt (`"FUNDED_PSBT"`), if not passed just a bitcoin receive address is returned (`"ADDRESS"`)
698
+ * @param options.skipChecks Skip checks like making sure init signature is still valid and swap
699
+ * wasn't commited yet (this is handled on swap creation, if you commit right after quoting, you
700
+ * can use `skipChecks=true`)
701
+ * @param options.manualSettlementSmartChainSigner Optional smart chain signer to create a manual claim (settlement) transaction
702
+ * @param options.maxWaitTillAutomaticSettlementSeconds Maximum time to wait for an automatic settlement after
703
+ * the bitcoin transaction is confirmed (defaults to 60 seconds)
704
+ */
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 [];
725
+ }
726
+ //////////////////////////////
727
+ //// Commit
728
+ /**
729
+ * @inheritDoc
730
+ *
731
+ * @throws {Error} If invalid signer is provided that doesn't match the swap data
732
+ */
733
+ async commit(_signer, abortSignal, skipChecks, onBeforeTxSent) {
734
+ const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
735
+ this.checkSigner(signer);
736
+ let txCount = 0;
737
+ const txs = await this.txsCommit(skipChecks);
738
+ const result = await this.wrapper._chain.sendAndConfirm(signer, txs, true, abortSignal, undefined, (txId) => {
739
+ txCount++;
740
+ if (onBeforeTxSent != null && txCount === txs.length)
741
+ onBeforeTxSent(txId);
742
+ return Promise.resolve();
743
+ });
744
+ this._commitTxId = result[result.length - 1];
745
+ if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED || this._state === FromBTCSwapState.QUOTE_EXPIRED) {
746
+ await this._saveAndEmit(FromBTCSwapState.CLAIM_COMMITED);
747
+ }
748
+ return this._commitTxId;
749
+ }
750
+ /**
751
+ * @inheritDoc
752
+ */
753
+ async waitTillCommited(abortSignal) {
754
+ if (this._state === FromBTCSwapState.CLAIM_COMMITED || this._state === FromBTCSwapState.CLAIM_CLAIMED)
755
+ return Promise.resolve();
756
+ if (this._state !== FromBTCSwapState.PR_CREATED && this._state !== FromBTCSwapState.QUOTE_SOFT_EXPIRED)
757
+ throw new Error("Invalid state");
758
+ const abortController = (0, Utils_1.extendAbortController)(abortSignal);
759
+ const result = await Promise.race([
760
+ this.watchdogWaitTillCommited(undefined, abortController.signal),
761
+ this.waitTillState(FromBTCSwapState.CLAIM_COMMITED, "gte", abortController.signal).then(() => 0)
762
+ ]);
763
+ abortController.abort();
764
+ if (result === 0)
765
+ this.logger.debug("waitTillCommited(): Resolved from state changed");
766
+ if (result === true)
767
+ this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
768
+ if (result === false) {
769
+ this.logger.debug("waitTillCommited(): Resolved from watchdog - signature expired");
770
+ if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
771
+ await this._saveAndEmit(FromBTCSwapState.QUOTE_EXPIRED);
772
+ }
773
+ return;
774
+ }
775
+ if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
776
+ await this._saveAndEmit(FromBTCSwapState.CLAIM_COMMITED);
777
+ }
778
+ }
779
+ //////////////////////////////
780
+ //// Claim
781
+ /**
782
+ * Returns transactions for settling (claiming) the swap if the swap requires manual settlement, you can check so
783
+ * with isClaimable. After sending the transaction manually be sure to call the waitTillClaimed function to wait
784
+ * till the claim transaction is observed, processed by the SDK and state of the swap properly updated.
785
+ *
786
+ * @remarks
787
+ * Might also return transactions necessary to sync the bitcoin light client.
788
+ *
789
+ * @param _signer Address of the signer to create the claim transactions for
790
+ *
791
+ * @throws {Error} If the swap is in invalid state (must be {@link FromBTCSwapState.BTC_TX_CONFIRMED})
792
+ */
793
+ async txsClaim(_signer) {
794
+ let signer = undefined;
795
+ if (_signer != null) {
796
+ if (typeof (_signer) === "string") {
797
+ signer = _signer;
798
+ }
799
+ else if ((0, base_1.isAbstractSigner)(_signer)) {
800
+ signer = _signer;
801
+ }
802
+ else {
803
+ signer = await this.wrapper._chain.wrapSigner(_signer);
804
+ }
805
+ }
806
+ if (this._state !== FromBTCSwapState.BTC_TX_CONFIRMED)
807
+ throw new Error("Must be in BTC_TX_CONFIRMED state!");
808
+ if (this.txId == null || this.vout == null)
809
+ throw new Error("Bitcoin transaction ID not known!");
810
+ const tx = await this.wrapper._btcRpc.getTransaction(this.txId);
811
+ if (tx == null)
812
+ throw new Error("Bitcoin transaction not found on the network!");
813
+ this.requiredConfirmations ??= this.inferRequiredConfirmationsCount(tx, this.vout);
814
+ if (this.requiredConfirmations == null)
815
+ throw new Error("Cannot create claim transaction, because required confirmations are not known and cannot be infered! This can happen after a swap is recovered.");
816
+ if (tx.blockhash == null || tx.confirmations == null || tx.blockheight == null || tx.confirmations < this.requiredConfirmations)
817
+ throw new Error("Bitcoin transaction not confirmed yet!");
818
+ return await this.wrapper._contract.txsClaimWithTxData(signer ?? this._getInitiator(), this._data, {
819
+ blockhash: tx.blockhash,
820
+ confirmations: tx.confirmations,
821
+ txid: tx.txid,
822
+ hex: tx.hex,
823
+ height: tx.blockheight
824
+ }, this.requiredConfirmations, this.vout, undefined, this.wrapper._synchronizer, true);
825
+ }
826
+ /**
827
+ * Settles the swap by claiming the funds on the destination chain if the swap requires manual settlement, you can
828
+ * check so with isClaimable.
829
+ *
830
+ * @remarks
831
+ * Might also sync the bitcoin light client during the process.
832
+ *
833
+ * @param _signer Signer to use for signing the settlement transactions, can also be different to the recipient
834
+ * @param abortSignal Abort signal
835
+ * @param onBeforeTxSent Optional callback triggered before the claim transaction is broadcasted
836
+ *
837
+ * @returns Transaction ID of the settlement (claim) transaction on the destination smart chain
838
+ */
839
+ async claim(_signer, abortSignal, onBeforeTxSent) {
840
+ const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
841
+ let txIds;
842
+ try {
843
+ let txCount = 0;
844
+ const txs = await this.txsClaim(signer);
845
+ txIds = await this.wrapper._chain.sendAndConfirm(signer, txs, true, abortSignal, undefined, (txId) => {
846
+ txCount++;
847
+ if (onBeforeTxSent != null && txCount === txs.length)
848
+ onBeforeTxSent(txId);
849
+ return Promise.resolve();
850
+ });
851
+ }
852
+ catch (e) {
853
+ this.logger.info("claim(): Failed to claim ourselves, checking swap claim state...");
854
+ if (this._state === FromBTCSwapState.CLAIM_CLAIMED) {
855
+ this.logger.info("claim(): Transaction state is CLAIM_CLAIMED, swap was successfully claimed by the watchtower");
856
+ return this._claimTxId;
857
+ }
858
+ const status = await this.wrapper._contract.getCommitStatus(this._getInitiator(), this._data);
859
+ if (status?.type === base_1.SwapCommitStateType.PAID) {
860
+ this.logger.info("claim(): Transaction commit status is PAID, swap was successfully claimed by the watchtower");
861
+ if (this._claimTxId == null)
862
+ this._claimTxId = await status.getClaimTxId();
863
+ const txId = buffer_1.Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
864
+ await this._setBitcoinTxId(txId);
865
+ await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
866
+ return this._claimTxId;
867
+ }
868
+ throw e;
869
+ }
870
+ this._claimTxId = txIds[txIds.length - 1];
871
+ if (this._state === FromBTCSwapState.CLAIM_COMMITED || this._state === FromBTCSwapState.BTC_TX_CONFIRMED ||
872
+ this._state === FromBTCSwapState.EXPIRED || this._state === FromBTCSwapState.FAILED) {
873
+ await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
874
+ }
875
+ return txIds[txIds.length - 1];
876
+ }
877
+ /**
878
+ * @inheritDoc
879
+ *
880
+ * @throws {Error} If swap is in invalid state (must be {@link FromBTCSwapState.BTC_TX_CONFIRMED})
881
+ * @throws {Error} If the LP refunded sooner than we were able to claim
882
+ */
883
+ async waitTillClaimed(maxWaitTimeSeconds, abortSignal) {
884
+ if (this._state === FromBTCSwapState.CLAIM_CLAIMED)
885
+ return Promise.resolve(true);
886
+ if (this._state !== FromBTCSwapState.BTC_TX_CONFIRMED)
887
+ throw new Error("Invalid state (not BTC_TX_CONFIRMED)");
888
+ const abortController = (0, Utils_1.extendAbortController)(abortSignal);
889
+ let timedOut = false;
890
+ if (maxWaitTimeSeconds != null) {
891
+ const timeout = setTimeout(() => {
892
+ timedOut = true;
893
+ abortController.abort();
894
+ }, maxWaitTimeSeconds * 1000);
895
+ abortController.signal.addEventListener("abort", () => clearTimeout(timeout));
896
+ }
897
+ let res;
898
+ try {
899
+ res = await Promise.race([
900
+ this.watchdogWaitTillResult(undefined, abortController.signal),
901
+ this.waitTillState(FromBTCSwapState.CLAIM_CLAIMED, "eq", abortController.signal).then(() => 0),
902
+ this.waitTillState(FromBTCSwapState.FAILED, "eq", abortController.signal).then(() => 1),
903
+ ]);
904
+ abortController.abort();
905
+ }
906
+ catch (e) {
907
+ abortController.abort();
908
+ if (timedOut)
909
+ return false;
910
+ throw e;
911
+ }
912
+ if (res === 0) {
913
+ this.logger.debug("waitTillClaimed(): Resolved from state change (CLAIM_CLAIMED)");
914
+ return true;
915
+ }
916
+ if (res === 1) {
917
+ this.logger.debug("waitTillClaimed(): Resolved from state change (FAILED)");
918
+ throw new Error("Offerer refunded during claiming");
919
+ }
920
+ this.logger.debug("waitTillClaimed(): Resolved from watchdog");
921
+ if (res?.type === base_1.SwapCommitStateType.PAID) {
922
+ if (this._state !== FromBTCSwapState.CLAIM_CLAIMED) {
923
+ if (this._claimTxId == null)
924
+ this._claimTxId = await res.getClaimTxId();
925
+ const txId = buffer_1.Buffer.from(await res.getClaimResult(), "hex").reverse().toString("hex");
926
+ await this._setBitcoinTxId(txId);
927
+ await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
928
+ }
929
+ }
930
+ if (res?.type === base_1.SwapCommitStateType.NOT_COMMITED || res?.type === base_1.SwapCommitStateType.EXPIRED) {
931
+ if (this._state !== FromBTCSwapState.CLAIM_CLAIMED &&
932
+ this._state !== FromBTCSwapState.FAILED) {
933
+ if (res.getRefundTxId != null)
934
+ this._refundTxId = await res.getRefundTxId();
935
+ await this._saveAndEmit(FromBTCSwapState.FAILED);
936
+ }
937
+ throw new Error("Swap expired while waiting for claim!");
938
+ }
939
+ return true;
940
+ }
941
+ //////////////////////////////
942
+ //// Storage
943
+ /**
944
+ * @inheritDoc
945
+ */
946
+ serialize() {
947
+ return {
948
+ ...super.serialize(),
949
+ address: this.address,
950
+ amount: this.amount == null ? null : this.amount.toString(10),
951
+ requiredConfirmations: this.requiredConfirmations,
952
+ senderAddress: this.senderAddress,
953
+ txId: this.txId,
954
+ vout: this.vout,
955
+ btcTxConfirmedAt: this.btcTxConfirmedAt
956
+ };
957
+ }
958
+ //////////////////////////////
959
+ //// Swap ticks & sync
960
+ /**
961
+ * Checks the swap's state on-chain and compares it to its internal state, updates/changes it according to on-chain
962
+ * data
963
+ *
964
+ * @private
965
+ */
966
+ async syncStateFromChain(quoteDefinitelyExpired, commitStatus) {
967
+ if (this._state === FromBTCSwapState.PR_CREATED ||
968
+ this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED ||
969
+ this._state === FromBTCSwapState.CLAIM_COMMITED ||
970
+ this._state === FromBTCSwapState.BTC_TX_CONFIRMED ||
971
+ this._state === FromBTCSwapState.EXPIRED) {
972
+ let quoteExpired = false;
973
+ if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
974
+ quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired(); //Make sure we check for expiry here, to prevent race conditions
975
+ }
976
+ const status = commitStatus ?? await this.wrapper._contract.getCommitStatus(this._getInitiator(), this._data);
977
+ if (status != null && await this._forciblySetOnchainState(status))
978
+ return true;
979
+ if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
980
+ if (quoteExpired) {
981
+ this._state = FromBTCSwapState.QUOTE_EXPIRED;
982
+ return true;
983
+ }
984
+ }
985
+ }
986
+ return false;
987
+ }
988
+ /**
989
+ * @inheritDoc
990
+ * @internal
991
+ */
992
+ _shouldFetchOnchainState() {
993
+ return this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED ||
994
+ this._state === FromBTCSwapState.CLAIM_COMMITED || this._state === FromBTCSwapState.BTC_TX_CONFIRMED ||
995
+ this._state === FromBTCSwapState.EXPIRED;
996
+ }
997
+ /**
998
+ * @inheritDoc
999
+ * @internal
1000
+ */
1001
+ _shouldFetchExpiryStatus() {
1002
+ return this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED;
1003
+ }
1004
+ /**
1005
+ * @inheritDoc
1006
+ * @internal
1007
+ */
1008
+ async _sync(save, quoteDefinitelyExpired, commitStatus) {
1009
+ const changed = await this.syncStateFromChain(quoteDefinitelyExpired, commitStatus);
1010
+ if (changed && save)
1011
+ await this._saveAndEmit();
1012
+ return changed;
1013
+ }
1014
+ /**
1015
+ * @inheritDoc
1016
+ * @internal
1017
+ */
1018
+ async _forciblySetOnchainState(status) {
1019
+ switch (status.type) {
1020
+ case base_1.SwapCommitStateType.PAID:
1021
+ if (this._claimTxId == null)
1022
+ this._claimTxId = await status.getClaimTxId();
1023
+ const txId = buffer_1.Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
1024
+ await this._setBitcoinTxId(txId);
1025
+ this._state = FromBTCSwapState.CLAIM_CLAIMED;
1026
+ return true;
1027
+ case base_1.SwapCommitStateType.NOT_COMMITED:
1028
+ if (this._refundTxId == null && status.getRefundTxId)
1029
+ this._refundTxId = await status.getRefundTxId();
1030
+ if (this._refundTxId != null) {
1031
+ this._state = FromBTCSwapState.FAILED;
1032
+ return true;
1033
+ }
1034
+ break;
1035
+ case base_1.SwapCommitStateType.EXPIRED:
1036
+ if (this._refundTxId == null && status.getRefundTxId)
1037
+ this._refundTxId = await status.getRefundTxId();
1038
+ this._state = this._refundTxId == null ? FromBTCSwapState.QUOTE_EXPIRED : FromBTCSwapState.FAILED;
1039
+ return true;
1040
+ case base_1.SwapCommitStateType.COMMITED:
1041
+ let save = false;
1042
+ if (this._state !== FromBTCSwapState.CLAIM_COMMITED && this._state !== FromBTCSwapState.BTC_TX_CONFIRMED && this._state !== FromBTCSwapState.EXPIRED) {
1043
+ this._state = FromBTCSwapState.CLAIM_COMMITED;
1044
+ save = true;
1045
+ }
1046
+ if (this.address == null)
1047
+ return save;
1048
+ this.btcTxLastChecked = Date.now();
1049
+ const res = await this.getBitcoinPayment();
1050
+ if (res != null) {
1051
+ if (this.txId !== res.txId) {
1052
+ if (res.inputAddresses != null)
1053
+ this.senderAddress = res.inputAddresses[0];
1054
+ this.txId = res.txId;
1055
+ this.vout = res.vout;
1056
+ save = true;
1057
+ }
1058
+ if (this.requiredConfirmations != null && res.confirmations >= this.requiredConfirmations) {
1059
+ this.btcTxConfirmedAt ??= Date.now();
1060
+ this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
1061
+ save = true;
1062
+ }
1063
+ }
1064
+ return save;
1065
+ }
1066
+ return false;
1067
+ }
1068
+ /**
1069
+ * @inheritDoc
1070
+ * @internal
1071
+ */
1072
+ async _tick(save) {
1073
+ switch (this._state) {
1074
+ case FromBTCSwapState.PR_CREATED:
1075
+ if (this.expiry < Date.now()) {
1076
+ this._state = FromBTCSwapState.QUOTE_SOFT_EXPIRED;
1077
+ if (save)
1078
+ await this._saveAndEmit();
1079
+ return true;
1080
+ }
1081
+ break;
1082
+ case FromBTCSwapState.CLAIM_COMMITED:
1083
+ if (this.getTimeoutTime() < Date.now()) {
1084
+ this._state = FromBTCSwapState.EXPIRED;
1085
+ if (save)
1086
+ await this._saveAndEmit();
1087
+ return true;
1088
+ }
1089
+ case FromBTCSwapState.EXPIRED:
1090
+ //Check if bitcoin payment was received at least every 2 minutes
1091
+ if (this.btcTxLastChecked == null || Date.now() - this.btcTxLastChecked > 120000) {
1092
+ if (this.address != null)
1093
+ try {
1094
+ this.btcTxLastChecked = Date.now();
1095
+ const res = await this.getBitcoinPayment();
1096
+ if (res != null) {
1097
+ let shouldSave = false;
1098
+ if (this.txId !== res.txId) {
1099
+ this.txId = res.txId;
1100
+ this.vout = res.vout;
1101
+ if (res.inputAddresses != null)
1102
+ this.senderAddress = res.inputAddresses[0];
1103
+ shouldSave = true;
1104
+ }
1105
+ if (this.requiredConfirmations != null && res.confirmations >= this.requiredConfirmations) {
1106
+ this.btcTxConfirmedAt ??= Date.now();
1107
+ this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
1108
+ if (save)
1109
+ await this._saveAndEmit();
1110
+ shouldSave = true;
1111
+ }
1112
+ if (shouldSave && save)
1113
+ await this._saveAndEmit();
1114
+ return shouldSave;
1115
+ }
1116
+ }
1117
+ catch (e) {
1118
+ this.logger.warn("tickSwap(" + this.getIdentifierHashString() + "): ", e);
1119
+ }
1120
+ }
1121
+ break;
1122
+ }
1123
+ return false;
1124
+ }
1125
+ }
1126
+ exports.FromBTCSwap = FromBTCSwap;