@atomiqlabs/sdk 8.1.8 → 8.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/dist/bitcoin/wallet/BitcoinWallet.d.ts +41 -5
  2. package/dist/bitcoin/wallet/BitcoinWallet.js +36 -1
  3. package/dist/bitcoin/wallet/IBitcoinWallet.d.ts +52 -2
  4. package/dist/bitcoin/wallet/IBitcoinWallet.js +2 -1
  5. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +42 -7
  6. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +36 -1
  7. package/dist/enums/FeeType.d.ts +7 -0
  8. package/dist/enums/FeeType.js +7 -0
  9. package/dist/enums/SwapAmountType.d.ts +7 -0
  10. package/dist/enums/SwapAmountType.js +7 -0
  11. package/dist/enums/SwapDirection.d.ts +7 -0
  12. package/dist/enums/SwapDirection.js +7 -0
  13. package/dist/enums/SwapType.d.ts +62 -1
  14. package/dist/enums/SwapType.js +62 -1
  15. package/dist/errors/IntermediaryError.d.ts +4 -0
  16. package/dist/errors/IntermediaryError.js +1 -0
  17. package/dist/errors/RequestError.d.ts +15 -1
  18. package/dist/errors/RequestError.js +8 -0
  19. package/dist/errors/UserError.d.ts +1 -0
  20. package/dist/errors/UserError.js +1 -0
  21. package/dist/index.d.ts +4 -5
  22. package/dist/index.js +3 -4
  23. package/dist/intermediaries/Intermediary.d.ts +57 -10
  24. package/dist/intermediaries/Intermediary.js +37 -10
  25. package/dist/intermediaries/IntermediaryDiscovery.d.ts +55 -22
  26. package/dist/intermediaries/IntermediaryDiscovery.js +35 -22
  27. package/dist/prices/RedundantSwapPrice.d.ts +24 -3
  28. package/dist/prices/RedundantSwapPrice.js +21 -1
  29. package/dist/prices/SingleSwapPrice.d.ts +9 -6
  30. package/dist/prices/SingleSwapPrice.js +10 -7
  31. package/dist/prices/SwapPriceWithChain.d.ts +54 -16
  32. package/dist/prices/SwapPriceWithChain.js +58 -20
  33. package/dist/prices/abstract/ISwapPrice.d.ts +94 -45
  34. package/dist/prices/abstract/ISwapPrice.js +103 -55
  35. package/dist/prices/providers/BinancePriceProvider.d.ts +7 -0
  36. package/dist/prices/providers/BinancePriceProvider.js +7 -0
  37. package/dist/prices/providers/CoinGeckoPriceProvider.d.ts +6 -0
  38. package/dist/prices/providers/CoinGeckoPriceProvider.js +6 -0
  39. package/dist/prices/providers/CoinPaprikaPriceProvider.d.ts +6 -0
  40. package/dist/prices/providers/CoinPaprikaPriceProvider.js +6 -0
  41. package/dist/prices/providers/CustomPriceProvider.d.ts +11 -0
  42. package/dist/prices/providers/CustomPriceProvider.js +11 -0
  43. package/dist/prices/providers/KrakenPriceProvider.d.ts +9 -0
  44. package/dist/prices/providers/KrakenPriceProvider.js +9 -0
  45. package/dist/prices/providers/OKXPriceProvider.d.ts +6 -0
  46. package/dist/prices/providers/OKXPriceProvider.js +6 -0
  47. package/dist/prices/providers/abstract/ExchangePriceProvider.d.ts +3 -0
  48. package/dist/prices/providers/abstract/ExchangePriceProvider.js +3 -0
  49. package/dist/storage/IUnifiedStorage.d.ts +19 -7
  50. package/dist/storage/UnifiedSwapStorage.d.ts +33 -3
  51. package/dist/storage/UnifiedSwapStorage.js +29 -1
  52. package/dist/storage-browser/IndexedDBUnifiedStorage.d.ts +31 -7
  53. package/dist/storage-browser/IndexedDBUnifiedStorage.js +29 -6
  54. package/dist/storage-browser/LocalStorageManager.d.ts +25 -1
  55. package/dist/storage-browser/LocalStorageManager.js +25 -1
  56. package/dist/swapper/Swapper.d.ts +302 -221
  57. package/dist/swapper/Swapper.js +376 -344
  58. package/dist/swapper/SwapperFactory.d.ts +41 -17
  59. package/dist/swapper/SwapperFactory.js +23 -2
  60. package/dist/swapper/SwapperUtils.d.ts +75 -28
  61. package/dist/swapper/SwapperUtils.js +107 -60
  62. package/dist/swapper/SwapperWithChain.d.ts +286 -91
  63. package/dist/swapper/SwapperWithChain.js +218 -64
  64. package/dist/swapper/SwapperWithSigner.d.ts +229 -80
  65. package/dist/swapper/SwapperWithSigner.js +190 -44
  66. package/dist/swaps/IAddressSwap.d.ts +10 -1
  67. package/dist/swaps/IAddressSwap.js +2 -1
  68. package/dist/swaps/IBTCWalletSwap.d.ts +24 -6
  69. package/dist/swaps/IBTCWalletSwap.js +2 -1
  70. package/dist/swaps/IClaimableSwap.d.ts +36 -4
  71. package/dist/swaps/IClaimableSwap.js +2 -1
  72. package/dist/swaps/IClaimableSwapWrapper.d.ts +11 -1
  73. package/dist/swaps/IRefundableSwap.d.ts +29 -3
  74. package/dist/swaps/IRefundableSwap.js +2 -1
  75. package/dist/swaps/ISwap.d.ts +159 -21
  76. package/dist/swaps/ISwap.js +90 -33
  77. package/dist/swaps/ISwapWithGasDrop.d.ts +6 -0
  78. package/dist/swaps/ISwapWithGasDrop.js +1 -0
  79. package/dist/swaps/ISwapWrapper.d.ts +157 -48
  80. package/dist/swaps/ISwapWrapper.js +130 -72
  81. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.d.ts +49 -6
  82. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.js +22 -12
  83. package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +65 -12
  84. package/dist/swaps/escrow_swaps/IEscrowSwap.js +38 -19
  85. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +39 -9
  86. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +30 -21
  87. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +31 -15
  88. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +33 -18
  89. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.d.ts +94 -29
  90. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +90 -27
  91. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.d.ts +22 -9
  92. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.js +24 -11
  93. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +275 -58
  94. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +516 -239
  95. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +76 -25
  96. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +131 -49
  97. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +311 -51
  98. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +542 -193
  99. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +87 -26
  100. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +147 -58
  101. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +209 -53
  102. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +449 -242
  103. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +77 -23
  104. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +116 -46
  105. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +197 -56
  106. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +326 -189
  107. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.d.ts +30 -5
  108. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.js +44 -19
  109. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.d.ts +60 -19
  110. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.js +74 -31
  111. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +76 -50
  112. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +106 -101
  113. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.d.ts +36 -13
  114. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.js +65 -19
  115. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +46 -17
  116. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +82 -27
  117. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +328 -92
  118. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +460 -219
  119. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +76 -24
  120. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +244 -124
  121. package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +146 -18
  122. package/dist/swaps/trusted/ln/LnForGasSwap.js +173 -43
  123. package/dist/swaps/trusted/ln/LnForGasWrapper.d.ts +29 -10
  124. package/dist/swaps/trusted/ln/LnForGasWrapper.js +30 -11
  125. package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +200 -47
  126. package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +230 -78
  127. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +34 -12
  128. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +33 -14
  129. package/dist/types/AmountData.d.ts +2 -1
  130. package/dist/types/CustomPriceFunction.d.ts +7 -1
  131. package/dist/types/SwapExecutionAction.d.ts +74 -4
  132. package/dist/types/SwapWithSigner.d.ts +4 -1
  133. package/dist/types/SwapWithSigner.js +5 -2
  134. package/dist/types/Token.d.ts +11 -5
  135. package/dist/types/Token.js +6 -3
  136. package/dist/types/TokenAmount.d.ts +3 -0
  137. package/dist/types/TokenAmount.js +2 -0
  138. package/dist/types/fees/Fee.d.ts +2 -1
  139. package/dist/types/fees/FeeBreakdown.d.ts +2 -1
  140. package/dist/types/fees/PercentagePPM.d.ts +2 -0
  141. package/dist/types/fees/PercentagePPM.js +1 -0
  142. package/dist/types/lnurl/LNURLPay.d.ts +14 -6
  143. package/dist/types/lnurl/LNURLPay.js +6 -2
  144. package/dist/types/lnurl/LNURLWithdraw.d.ts +12 -5
  145. package/dist/types/lnurl/LNURLWithdraw.js +6 -2
  146. package/dist/types/wallets/LightningInvoiceCreateService.d.ts +20 -0
  147. package/dist/types/wallets/LightningInvoiceCreateService.js +15 -0
  148. package/dist/types/wallets/MinimalBitcoinWalletInterface.d.ts +3 -1
  149. package/dist/types/wallets/MinimalLightningNetworkWalletInterface.d.ts +3 -1
  150. package/dist/utils/BitcoinUtils.d.ts +1 -0
  151. package/dist/utils/BitcoinUtils.js +5 -1
  152. package/dist/utils/SwapUtils.d.ts +56 -1
  153. package/dist/utils/SwapUtils.js +53 -1
  154. package/dist/utils/TokenUtils.d.ts +10 -2
  155. package/dist/utils/TokenUtils.js +12 -4
  156. package/package.json +3 -3
  157. package/src/bitcoin/wallet/BitcoinWallet.ts +41 -5
  158. package/src/bitcoin/wallet/IBitcoinWallet.ts +57 -2
  159. package/src/bitcoin/wallet/SingleAddressBitcoinWallet.ts +42 -6
  160. package/src/enums/FeeType.ts +7 -0
  161. package/src/enums/SwapAmountType.ts +7 -0
  162. package/src/enums/SwapDirection.ts +7 -0
  163. package/src/enums/SwapType.ts +62 -2
  164. package/src/errors/IntermediaryError.ts +4 -0
  165. package/src/errors/RequestError.ts +15 -1
  166. package/src/errors/UserError.ts +1 -0
  167. package/src/index.ts +6 -5
  168. package/src/intermediaries/Intermediary.ts +57 -10
  169. package/src/intermediaries/IntermediaryDiscovery.ts +60 -27
  170. package/src/prices/RedundantSwapPrice.ts +24 -4
  171. package/src/prices/SingleSwapPrice.ts +10 -7
  172. package/src/prices/SwapPriceWithChain.ts +59 -21
  173. package/src/prices/abstract/ISwapPrice.ts +114 -65
  174. package/src/prices/providers/BinancePriceProvider.ts +7 -0
  175. package/src/prices/providers/CoinGeckoPriceProvider.ts +6 -0
  176. package/src/prices/providers/CoinPaprikaPriceProvider.ts +6 -0
  177. package/src/prices/providers/CustomPriceProvider.ts +11 -0
  178. package/src/prices/providers/KrakenPriceProvider.ts +9 -0
  179. package/src/prices/providers/OKXPriceProvider.ts +6 -0
  180. package/src/prices/providers/abstract/ExchangePriceProvider.ts +3 -0
  181. package/src/storage/IUnifiedStorage.ts +19 -7
  182. package/src/storage/UnifiedSwapStorage.ts +33 -3
  183. package/src/storage-browser/IndexedDBUnifiedStorage.ts +31 -8
  184. package/src/storage-browser/LocalStorageManager.ts +25 -1
  185. package/src/swapper/Swapper.ts +512 -378
  186. package/src/swapper/SwapperFactory.ts +44 -21
  187. package/src/swapper/SwapperUtils.ts +107 -60
  188. package/src/swapper/SwapperWithChain.ts +320 -81
  189. package/src/swapper/SwapperWithSigner.ts +263 -56
  190. package/src/swaps/IAddressSwap.ts +11 -1
  191. package/src/swaps/IBTCWalletSwap.ts +24 -8
  192. package/src/swaps/IClaimableSwap.ts +39 -4
  193. package/src/swaps/IClaimableSwapWrapper.ts +11 -2
  194. package/src/swaps/IRefundableSwap.ts +32 -3
  195. package/src/swaps/ISwap.ts +221 -82
  196. package/src/swaps/ISwapWithGasDrop.ts +6 -0
  197. package/src/swaps/ISwapWrapper.ts +212 -94
  198. package/src/swaps/escrow_swaps/IEscrowSelfInitSwap.ts +62 -18
  199. package/src/swaps/escrow_swaps/IEscrowSwap.ts +83 -37
  200. package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +61 -30
  201. package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +37 -19
  202. package/src/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.ts +120 -51
  203. package/src/swaps/escrow_swaps/frombtc/IFromBTCWrapper.ts +24 -11
  204. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +559 -256
  205. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +155 -61
  206. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +590 -226
  207. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +177 -74
  208. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +470 -243
  209. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +141 -59
  210. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +352 -193
  211. package/src/swaps/escrow_swaps/tobtc/IToBTCWrapper.ts +48 -23
  212. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.ts +86 -39
  213. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +110 -110
  214. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.ts +88 -33
  215. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +101 -31
  216. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +534 -263
  217. package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +289 -148
  218. package/src/swaps/trusted/ln/LnForGasSwap.ts +184 -45
  219. package/src/swaps/trusted/ln/LnForGasWrapper.ts +34 -15
  220. package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +260 -86
  221. package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +41 -19
  222. package/src/types/AmountData.ts +2 -1
  223. package/src/types/CustomPriceFunction.ts +7 -1
  224. package/src/types/SwapExecutionAction.ts +84 -5
  225. package/src/types/SwapWithSigner.ts +7 -3
  226. package/src/types/Token.ts +12 -5
  227. package/src/types/TokenAmount.ts +3 -0
  228. package/src/types/fees/Fee.ts +2 -1
  229. package/src/types/fees/FeeBreakdown.ts +2 -1
  230. package/src/types/fees/PercentagePPM.ts +2 -0
  231. package/src/types/lnurl/LNURLPay.ts +14 -6
  232. package/src/types/lnurl/LNURLWithdraw.ts +12 -5
  233. package/src/types/wallets/LightningInvoiceCreateService.ts +26 -0
  234. package/src/types/wallets/MinimalBitcoinWalletInterface.ts +3 -1
  235. package/src/types/wallets/MinimalLightningNetworkWalletInterface.ts +3 -1
  236. package/src/utils/BitcoinUtils.ts +5 -0
  237. package/src/utils/SwapUtils.ts +61 -1
  238. package/src/utils/TokenUtils.ts +12 -4
  239. package/dist/bitcoin/BitcoinRpcWithAddressIndex.d.ts +0 -68
  240. package/dist/bitcoin/BitcoinRpcWithAddressIndex.js +0 -2
  241. package/dist/bitcoin/LightningNetworkApi.d.ts +0 -12
  242. package/dist/bitcoin/LightningNetworkApi.js +0 -2
  243. package/dist/bitcoin/mempool/MempoolApi.d.ts +0 -350
  244. package/dist/bitcoin/mempool/MempoolApi.js +0 -311
  245. package/dist/bitcoin/mempool/MempoolBitcoinBlock.d.ts +0 -44
  246. package/dist/bitcoin/mempool/MempoolBitcoinBlock.js +0 -48
  247. package/dist/bitcoin/mempool/MempoolBitcoinRpc.d.ts +0 -119
  248. package/dist/bitcoin/mempool/MempoolBitcoinRpc.js +0 -361
  249. package/dist/bitcoin/mempool/synchronizer/MempoolBtcRelaySynchronizer.d.ts +0 -22
  250. package/dist/bitcoin/mempool/synchronizer/MempoolBtcRelaySynchronizer.js +0 -105
  251. package/dist/errors/PaymentAuthError.d.ts +0 -11
  252. package/dist/errors/PaymentAuthError.js +0 -23
  253. package/src/errors/PaymentAuthError.ts +0 -26
@@ -12,15 +12,16 @@ import {
12
12
  import {Buffer} from "buffer";
13
13
  import {
14
14
  extendAbortController,
15
- getTxoHash
15
+ getTxoHash, toBigInt
16
16
  } from "../../../../utils/Utils";
17
17
  import {
18
+ fromOutputScript,
18
19
  parsePsbtTransaction,
19
20
  toOutputScript,
20
21
  } from "../../../../utils/BitcoinUtils";
21
22
  import {IBitcoinWallet, isIBitcoinWallet} from "../../../../bitcoin/wallet/IBitcoinWallet";
22
23
  import {IBTCWalletSwap} from "../../../IBTCWalletSwap";
23
- import {Address, OutScript, Transaction} from "@scure/btc-signer";
24
+ import {Transaction} from "@scure/btc-signer";
24
25
  import {SingleAddressBitcoinWallet} from "../../../../bitcoin/wallet/SingleAddressBitcoinWallet";
25
26
  import {
26
27
  MinimalBitcoinWalletInterface,
@@ -30,59 +31,111 @@ import {IClaimableSwap} from "../../../IClaimableSwap";
30
31
  import {IEscrowSelfInitSwapInit, isIEscrowSelfInitSwapInit} from "../../IEscrowSelfInitSwap";
31
32
  import {IAddressSwap} from "../../../IAddressSwap";
32
33
  import {TokenAmount, toTokenAmount} from "../../../../types/TokenAmount";
33
- import {BitcoinTokens, BtcToken, SCToken, Token} from "../../../../types/Token";
34
+ import {BitcoinTokens, BtcToken, SCToken} from "../../../../types/Token";
34
35
  import {getLogger, LoggerType} from "../../../../utils/Logger";
35
36
  import {toBitcoinWallet} from "../../../../utils/BitcoinWalletUtils";
36
37
 
37
38
  /**
38
- * State enum for FromBTC swaps
39
+ * State enum for legacy escrow based Bitcoin -> Smart chain swaps.
40
+ *
39
41
  * @category Swaps
40
42
  */
41
43
  export enum FromBTCSwapState {
44
+ /**
45
+ * Bitcoin swap address has expired and the intermediary (LP) has already refunded
46
+ * its funds. No BTC should be sent anymore!
47
+ */
42
48
  FAILED = -4,
49
+ /**
50
+ * Bitcoin swap address has expired, user should not send any BTC anymore! Though
51
+ * the intermediary (LP) hasn't refunded yet. So if there is a transaction already
52
+ * in-flight the swap might still succeed.
53
+ */
43
54
  EXPIRED = -3,
55
+ /**
56
+ * Swap has expired for good and there is no way how it can be executed anymore
57
+ */
44
58
  QUOTE_EXPIRED = -2,
59
+ /**
60
+ * A swap is almost expired, and it should be presented to the user as expired, though
61
+ * there is still a chance that it will be processed
62
+ */
45
63
  QUOTE_SOFT_EXPIRED = -1,
64
+ /**
65
+ * Swap quote was created, use the {@link FromBTCSwap.commit} or {@link FromBTCSwap.txsCommit} functions
66
+ * to initiate it by creating the swap escrow on the destination smart chain
67
+ */
46
68
  PR_CREATED = 0,
69
+ /**
70
+ * Swap escrow was initiated (committed) on the destination chain, user can send the BTC to the
71
+ * swap address with the {@link FromBTCSwap.getFundedPsbt}, {@link FromBTCSwap.getAddress} or
72
+ * {@link FromBTCSwap.getHyperlink} functions.
73
+ */
47
74
  CLAIM_COMMITED = 1,
75
+ /**
76
+ * Input bitcoin transaction was confirmed, wait for automatic settlement by the watchtower
77
+ * or settle manually using the {@link FromBTCSwap.claim} or {@link FromBTCSwap.txsClaim}
78
+ * function.
79
+ */
48
80
  BTC_TX_CONFIRMED = 2,
81
+ /**
82
+ * Swap successfully settled and funds received on the destination chain
83
+ */
49
84
  CLAIM_CLAIMED = 3
50
85
  }
51
86
 
52
87
  export type FromBTCSwapInit<T extends SwapData> = IEscrowSelfInitSwapInit<T> & {
53
88
  data: T;
54
- feeRate: string;
55
- address: string;
56
- amount: bigint;
57
- requiredConfirmations: number;
89
+ address?: string;
90
+ amount?: bigint;
91
+ requiredConfirmations?: number;
58
92
  };
59
93
 
60
94
  export function isFromBTCSwapInit<T extends SwapData>(obj: any): obj is FromBTCSwapInit<T> {
61
- return typeof(obj.address) === "string" &&
62
- typeof(obj.amount) === "bigint" &&
63
- typeof(obj.data) === "object" &&
64
- typeof(obj.requiredConfirmations) === "number" &&
95
+ return typeof(obj.data) === "object" &&
96
+ (obj.address==null || typeof(obj.address) === "string") &&
97
+ (obj.amount==null || typeof(obj.amount) === "bigint") &&
98
+ (obj.requiredConfirmations==null || typeof(obj.requiredConfirmations) === "number") &&
65
99
  isIEscrowSelfInitSwapInit<T>(obj);
66
100
  }
67
101
 
102
+ /**
103
+ * Legacy escrow (PrTLC) based swap for Bitcoin -> Smart chains, requires manual initiation
104
+ * of the swap escrow on the destination chain.
105
+ *
106
+ * @category Swaps
107
+ */
68
108
  export class FromBTCSwap<T extends ChainType = ChainType>
69
109
  extends IFromBTCSelfInitSwap<T, FromBTCDefinition<T>, FromBTCSwapState>
70
110
  implements IBTCWalletSwap, IClaimableSwap<T, FromBTCDefinition<T>, FromBTCSwapState>, IAddressSwap {
71
111
 
112
+ protected readonly TYPE: SwapType.FROM_BTC = SwapType.FROM_BTC;
113
+
114
+ /**
115
+ * @internal
116
+ */
72
117
  protected readonly logger: LoggerType;
118
+ /**
119
+ * @internal
120
+ */
73
121
  protected readonly inputToken: BtcToken<false> = BitcoinTokens.BTC;
74
- protected readonly TYPE = SwapType.FROM_BTC;
122
+ /**
123
+ * @internal
124
+ */
125
+ protected readonly feeRate!: string;
75
126
 
76
- readonly data!: T["Data"];
77
- readonly feeRate!: string;
127
+ /**
128
+ * @internal
129
+ */
130
+ readonly _data!: T["Data"];
78
131
 
79
- address: string;
80
- amount: bigint;
81
- readonly requiredConfirmations: number;
132
+ private address?: string;
133
+ private amount?: bigint;
134
+ private requiredConfirmations?: number;
82
135
 
83
- senderAddress?: string;
84
- txId?: string;
85
- vout?: number;
136
+ private senderAddress?: string;
137
+ private txId?: string;
138
+ private vout?: number;
86
139
 
87
140
  constructor(wrapper: FromBTCWrapper<T>, init: FromBTCSwapInit<T["Data"]>);
88
141
  constructor(wrapper: FromBTCWrapper<T>, obj: any);
@@ -90,48 +143,56 @@ export class FromBTCSwap<T extends ChainType = ChainType>
90
143
  if(isFromBTCSwapInit(initOrObject) && initOrObject.url!=null) initOrObject.url += "/frombtc";
91
144
  super(wrapper, initOrObject);
92
145
  if(isFromBTCSwapInit(initOrObject)) {
93
- this.state = FromBTCSwapState.PR_CREATED;
94
- this.data = initOrObject.data;
146
+ this._state = FromBTCSwapState.PR_CREATED;
147
+ this._data = initOrObject.data;
95
148
  this.feeRate = initOrObject.feeRate;
96
149
  this.address = initOrObject.address;
97
150
  this.amount = initOrObject.amount;
98
151
  this.requiredConfirmations = initOrObject.requiredConfirmations;
99
152
  } else {
100
153
  this.address = initOrObject.address;
101
- this.amount = BigInt(initOrObject.amount);
154
+ this.amount = toBigInt(initOrObject.amount);
102
155
  this.senderAddress = initOrObject.senderAddress;
103
156
  this.txId = initOrObject.txId;
104
157
  this.vout = initOrObject.vout;
105
- this.requiredConfirmations = initOrObject.requiredConfirmations ?? this.data.getConfirmationsHint();
158
+ this.requiredConfirmations = initOrObject.requiredConfirmations ?? this._data.getConfirmationsHint();
106
159
  }
107
160
  this.tryRecomputeSwapPrice();
108
161
  this.logger = getLogger("FromBTC("+this.getIdentifierHashString()+"): ");
109
162
  }
110
163
 
164
+ /**
165
+ * @inheritDoc
166
+ * @internal
167
+ */
111
168
  protected getSwapData(): T["Data"] {
112
- return this.data;
169
+ return this._data;
113
170
  }
114
171
 
172
+ /**
173
+ * @inheritDoc
174
+ * @internal
175
+ */
115
176
  protected upgradeVersion() {
116
177
  if(this.version == null) {
117
- switch(this.state) {
178
+ switch(this._state) {
118
179
  case -2:
119
- this.state = FromBTCSwapState.FAILED
180
+ this._state = FromBTCSwapState.FAILED
120
181
  break;
121
182
  case -1:
122
- this.state = FromBTCSwapState.QUOTE_EXPIRED
183
+ this._state = FromBTCSwapState.QUOTE_EXPIRED
123
184
  break;
124
185
  case 0:
125
- this.state = FromBTCSwapState.PR_CREATED
186
+ this._state = FromBTCSwapState.PR_CREATED
126
187
  break;
127
188
  case 1:
128
- this.state = FromBTCSwapState.CLAIM_COMMITED
189
+ this._state = FromBTCSwapState.CLAIM_COMMITED
129
190
  break;
130
191
  case 2:
131
- this.state = FromBTCSwapState.BTC_TX_CONFIRMED
192
+ this._state = FromBTCSwapState.BTC_TX_CONFIRMED
132
193
  break;
133
194
  case 3:
134
- this.state = FromBTCSwapState.CLAIM_CLAIMED
195
+ this._state = FromBTCSwapState.CLAIM_CLAIMED
135
196
  break;
136
197
  }
137
198
  this.version = 1;
@@ -146,8 +207,8 @@ export class FromBTCSwap<T extends ChainType = ChainType>
146
207
  * Returns bitcoin address where the on-chain BTC should be sent to
147
208
  */
148
209
  getAddress(): string {
149
- if(this.state===FromBTCSwapState.PR_CREATED) throw new Error("Cannot get bitcoin address of non-committed swap");
150
- return this.address;
210
+ if(this._state===FromBTCSwapState.PR_CREATED) throw new Error("Cannot get bitcoin address of non-initiated swaps! Initiate swap first with commit() or txsCommit().");
211
+ return this.address ?? "";
151
212
  }
152
213
 
153
214
  /**
@@ -156,18 +217,27 @@ export class FromBTCSwap<T extends ChainType = ChainType>
156
217
  * @private
157
218
  */
158
219
  private _getHyperlink(): string {
159
- return "bitcoin:"+this.address+"?amount="+encodeURIComponent((Number(this.amount) / 100000000).toString(10));
220
+ return this.address==null || this.amount==null ? "" : "bitcoin:"+this.address+"?amount="+encodeURIComponent((Number(this.amount) / 100000000).toString(10));
160
221
  }
161
222
 
223
+ /**
224
+ * @inheritDoc
225
+ */
162
226
  getHyperlink(): string {
163
- if(this.state===FromBTCSwapState.PR_CREATED) throw new Error("Cannot get bitcoin address of non-committed swap");
227
+ if(this._state===FromBTCSwapState.PR_CREATED) throw new Error("Cannot get bitcoin address of non-initiated swaps! Initiate swap first with commit() or txsCommit().");
164
228
  return this._getHyperlink();
165
229
  }
166
230
 
231
+ /**
232
+ * @inheritDoc
233
+ */
167
234
  getInputAddress(): string | null {
168
235
  return this.senderAddress ?? null;
169
236
  }
170
237
 
238
+ /**
239
+ * @inheritDoc
240
+ */
171
241
  getInputTxId(): string | null {
172
242
  return this.txId ?? null;
173
243
  }
@@ -177,74 +247,136 @@ export class FromBTCSwap<T extends ChainType = ChainType>
177
247
  * to that address anymore
178
248
  */
179
249
  getTimeoutTime(): number {
180
- return Number(this.wrapper.getOnchainSendTimeout(this.data, this.requiredConfirmations)) * 1000;
250
+ return Number(this.wrapper._getOnchainSendTimeout(this._data, this.requiredConfirmations ?? 6)) * 1000;
181
251
  }
182
252
 
253
+ /**
254
+ * @inheritDoc
255
+ */
183
256
  requiresAction(): boolean {
184
- return this.isClaimable() || (this.state===FromBTCSwapState.CLAIM_COMMITED && this.getTimeoutTime()>Date.now() && this.txId==null);
257
+ return this.isClaimable() || (this._state===FromBTCSwapState.CLAIM_COMMITED && this.getTimeoutTime()>Date.now() && this.txId==null);
185
258
  }
186
259
 
260
+ /**
261
+ * @inheritDoc
262
+ */
187
263
  isFinished(): boolean {
188
- return this.state===FromBTCSwapState.CLAIM_CLAIMED || this.state===FromBTCSwapState.QUOTE_EXPIRED || this.state===FromBTCSwapState.FAILED;
264
+ return this._state===FromBTCSwapState.CLAIM_CLAIMED || this._state===FromBTCSwapState.QUOTE_EXPIRED || this._state===FromBTCSwapState.FAILED;
189
265
  }
190
266
 
267
+ /**
268
+ * @inheritDoc
269
+ */
191
270
  isClaimable(): boolean {
192
- return this.state===FromBTCSwapState.BTC_TX_CONFIRMED;
271
+ return this._state===FromBTCSwapState.BTC_TX_CONFIRMED;
193
272
  }
194
273
 
274
+ /**
275
+ * @inheritDoc
276
+ */
195
277
  isSuccessful(): boolean {
196
- return this.state===FromBTCSwapState.CLAIM_CLAIMED;
278
+ return this._state===FromBTCSwapState.CLAIM_CLAIMED;
197
279
  }
198
280
 
281
+ /**
282
+ * @inheritDoc
283
+ */
199
284
  isFailed(): boolean {
200
- return this.state===FromBTCSwapState.FAILED || this.state===FromBTCSwapState.EXPIRED;
285
+ return this._state===FromBTCSwapState.FAILED || this._state===FromBTCSwapState.EXPIRED;
201
286
  }
202
287
 
288
+ /**
289
+ * @inheritDoc
290
+ */
203
291
  isQuoteExpired(): boolean {
204
- return this.state===FromBTCSwapState.QUOTE_EXPIRED;
292
+ return this._state===FromBTCSwapState.QUOTE_EXPIRED;
205
293
  }
206
294
 
295
+ /**
296
+ * @inheritDoc
297
+ */
207
298
  isQuoteSoftExpired(): boolean {
208
- return this.state===FromBTCSwapState.QUOTE_EXPIRED || this.state===FromBTCSwapState.QUOTE_SOFT_EXPIRED;
299
+ return this._state===FromBTCSwapState.QUOTE_EXPIRED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED;
209
300
  }
210
301
 
302
+ /**
303
+ * @inheritDoc
304
+ * @internal
305
+ */
211
306
  protected canCommit(): boolean {
212
- if(this.state!==FromBTCSwapState.PR_CREATED) return false;
213
- const expiry = this.wrapper.getOnchainSendTimeout(this.data, this.requiredConfirmations);
307
+ if(this._state!==FromBTCSwapState.PR_CREATED) return false;
308
+ if(this.requiredConfirmations==null) return false;
309
+ const expiry = this.wrapper._getOnchainSendTimeout(this._data, this.requiredConfirmations);
214
310
  const currentTimestamp = BigInt(Math.floor(Date.now()/1000));
215
311
 
216
- return (expiry - currentTimestamp) >= this.wrapper.options.minSendWindow;
312
+ return (expiry - currentTimestamp) >= this.wrapper._options.minSendWindow;
217
313
  }
218
314
 
219
315
 
220
316
  //////////////////////////////
221
317
  //// Amounts & fees
222
318
 
319
+ /**
320
+ * @inheritDoc
321
+ */
223
322
  getInputToken(): BtcToken<false> {
224
323
  return BitcoinTokens.BTC;
225
324
  }
226
325
 
326
+ /**
327
+ * @inheritDoc
328
+ */
227
329
  getInput(): TokenAmount<T["ChainId"], BtcToken<false>> {
228
- return toTokenAmount(this.amount, this.inputToken, this.wrapper.prices);
330
+ return toTokenAmount(this.amount ?? null, this.inputToken, this.wrapper._prices);
229
331
  }
230
332
 
231
333
  /**
232
- * Returns claimer bounty, acting as a reward for watchtowers to claim the swap automatically
334
+ * Returns claimer bounty, acting as a reward for watchtowers to claim the swap automatically,
335
+ * this amount is pre-funded by the user on the destination chain when the swap escrow
336
+ * is initiated. For total pre-funded deposit amount see {@link getTotalDeposit}.
233
337
  */
234
338
  getClaimerBounty(): TokenAmount<T["ChainId"], SCToken<T["ChainId"]>, true> {
235
- return toTokenAmount(this.data.getClaimerBounty(), this.wrapper.tokens[this.data.getDepositToken()], this.wrapper.prices);
339
+ return toTokenAmount(this._data.getClaimerBounty(), this.wrapper._tokens[this._data.getDepositToken()], this.wrapper._prices);
236
340
  }
237
341
 
238
342
 
239
343
  //////////////////////////////
240
344
  //// Bitcoin tx
241
345
 
346
+ /**
347
+ * If the required number of confirmations is not known, this function tries to infer it by looping through
348
+ * possible confirmation targets and comparing the claim hashes
349
+ *
350
+ * @param btcTx Bitcoin transaction
351
+ * @param vout Output index of the desired output in the bitcoin transaction
352
+ *
353
+ * @private
354
+ */
355
+ private inferRequiredConfirmationsCount(btcTx: Omit<BtcTxWithBlockheight, "hex" | "raw">, vout: number): number | undefined {
356
+ const txOut = btcTx.outs[vout];
357
+ for(let i=1;i<=20;i++) {
358
+ const computedClaimHash = this.wrapper._contract.getHashForOnchain(
359
+ Buffer.from(txOut.scriptPubKey.hex, "hex"),
360
+ BigInt(txOut.value),
361
+ i
362
+ );
363
+ if(computedClaimHash.toString("hex")===this._data.getClaimHash()) {
364
+ return i;
365
+ }
366
+ }
367
+ }
368
+
369
+ /**
370
+ * @inheritDoc
371
+ */
242
372
  getRequiredConfirmationsCount(): number {
243
- return this.requiredConfirmations;
373
+ return this.requiredConfirmations ?? NaN;
244
374
  }
245
375
 
246
376
  /**
247
- * Checks whether a bitcoin payment was already made, returns the payment or null when no payment has been made.
377
+ * Checks whether a bitcoin payment was already made, returns the payment or `null` when no payment has been made.
378
+ *
379
+ * @internal
248
380
  */
249
381
  protected async getBitcoinPayment(): Promise<{
250
382
  txId: string,
@@ -253,32 +385,39 @@ export class FromBTCSwap<T extends ChainType = ChainType>
253
385
  targetConfirmations: number,
254
386
  inputAddresses?: string[]
255
387
  } | null> {
256
- const txoHashHint = this.data.getTxoHashHint();
257
- if(txoHashHint==null) throw new Error("Swap data don't include the txo hash hint! Cannot check btc transaction!");
388
+ const txoHashHint = this._data.getTxoHashHint();
389
+ if(txoHashHint==null) throw new Error("Swap data doesn't include the txo hash hint! Cannot check bitcoin transaction!");
390
+ if(this.address==null) throw new Error("Cannot check bitcoin payment, because the address is not known! This can happen after a swap is recovered.");
258
391
 
259
- const result = await this.wrapper.btcRpc.checkAddressTxos(this.address, Buffer.from(txoHashHint, "hex"));
392
+ const result = await this.wrapper._btcRpc.checkAddressTxos(this.address, Buffer.from(txoHashHint, "hex"));
260
393
  if(result==null) return null;
261
394
 
395
+ if(this.requiredConfirmations==null) {
396
+ this.requiredConfirmations = this.inferRequiredConfirmationsCount(result.tx, result.vout);
397
+ }
398
+
262
399
  return {
263
400
  inputAddresses: result.tx.inputAddresses,
264
401
  txId: result.tx.txid,
265
402
  vout: result.vout,
266
403
  confirmations: result.tx.confirmations ?? 0,
267
- targetConfirmations: this.requiredConfirmations
404
+ targetConfirmations: this.getRequiredConfirmationsCount()
268
405
  }
269
406
  }
270
407
 
271
408
  /**
272
- * For internal use! Used to set the txId of the bitcoin payment from the on-chain events listener
409
+ * Used to set the txId of the bitcoin payment from the on-chain events listener
273
410
  *
274
- * @param txId
411
+ * @param txId Transaction ID that settled the swap on the smart chain
412
+ *
413
+ * @internal
275
414
  */
276
415
  async _setBitcoinTxId(txId: string) {
277
416
  if(this.txId!==txId || this.address==null || this.vout==null || this.senderAddress==null || this.amount==null) {
278
- const btcTx = await this.wrapper.btcRpc.getTransaction(txId);
417
+ const btcTx = await this.wrapper._btcRpc.getTransaction(txId);
279
418
  if(btcTx==null) return;
280
419
 
281
- const txoHashHint = this.data.getTxoHashHint();
420
+ const txoHashHint = this._data.getTxoHashHint();
282
421
  if(txoHashHint!=null) {
283
422
  const expectedTxoHash = Buffer.from(txoHashHint, "hex");
284
423
  const vout = btcTx.outs.findIndex(out => getTxoHash(out.scriptPubKey.hex, out.value).equals(expectedTxoHash));
@@ -289,11 +428,13 @@ export class FromBTCSwap<T extends ChainType = ChainType>
289
428
  // hence doesn't contain the address and amount data
290
429
  if(this.amount==null) this.amount = BigInt(btcTx.outs[vout].value);
291
430
  if(this.address==null) try {
292
- const addressData = OutScript.decode(Buffer.from(btcTx.outs[vout].scriptPubKey.hex, "hex"));
293
- this.address = Address(this.wrapper.options.bitcoinNetwork).encode(addressData);
431
+ this.address = fromOutputScript(this.wrapper._options.bitcoinNetwork, btcTx.outs[vout].scriptPubKey.hex);
294
432
  } catch (e: any) {
295
433
  this.logger.warn("_setBitcoinTxId(): Failed to parse address from output script: ", e);
296
434
  }
435
+ if(this.requiredConfirmations==null) {
436
+ this.requiredConfirmations = this.inferRequiredConfirmationsCount(btcTx, vout);
437
+ }
297
438
  }
298
439
  }
299
440
 
@@ -306,40 +447,67 @@ export class FromBTCSwap<T extends ChainType = ChainType>
306
447
  }
307
448
 
308
449
  /**
309
- * Waits till the bitcoin transaction confirms and swap becomes claimable
450
+ * @inheritDoc
310
451
  *
311
- * @param updateCallback Callback called when txId is found, and also called with subsequent confirmations
312
- * @param checkIntervalSeconds How often to check the bitcoin transaction
313
- * @param abortSignal Abort signal
314
- * @throws {Error} if in invalid state (must be CLAIM_COMMITED)
452
+ * @throws {Error} if in invalid state (must be {@link FromBTCSwapState.CLAIM_COMMITED})
315
453
  */
316
454
  async waitForBitcoinTransaction(
317
455
  updateCallback?: (txId?: string, confirmations?: number, targetConfirmations?: number, txEtaMs?: number) => void,
318
456
  checkIntervalSeconds?: number,
319
457
  abortSignal?: AbortSignal
320
458
  ): Promise<string> {
321
- if(this.state!==FromBTCSwapState.CLAIM_COMMITED && this.state!==FromBTCSwapState.EXPIRED) throw new Error("Must be in COMMITED state!");
322
- const txoHashHint = this.data.getTxoHashHint();
323
- if(txoHashHint==null) throw new Error("Swap data don't include the txo hash hint! Cannot check btc transaction!");
459
+ if(this._state!==FromBTCSwapState.CLAIM_COMMITED && this._state!==FromBTCSwapState.EXPIRED) throw new Error("Must be in COMMITED state!");
460
+ const txoHashHint = this._data.getTxoHashHint();
461
+ if(txoHashHint==null) throw new Error("Swap data doesn't include the txo hash hint! Cannot check bitcoin transaction!");
462
+ if(this.address==null) throw new Error("Cannot check bitcoin payment, because the address is not known! This can happen after a swap is recovered.");
463
+
464
+ let abortedDueToEnoughConfirmationsResult: {
465
+ tx: Omit<BtcTxWithBlockheight, "hex" | "raw">, vout: number
466
+ } | undefined;
467
+ const abortController = extendAbortController(abortSignal);
324
468
 
325
- const result = await this.wrapper.btcRpc.waitForAddressTxo(
469
+ const result = await this.wrapper._btcRpc.waitForAddressTxo(
326
470
  this.address,
327
471
  Buffer.from(txoHashHint, "hex"),
328
- this.requiredConfirmations,
472
+ this.requiredConfirmations ?? 6, //In case confirmation count is not known, we use a conservative estimate
329
473
  (btcTx?: Omit<BtcTxWithBlockheight, "hex" | "raw">, vout?: number, txEtaMs?: number) => {
330
- if(updateCallback!=null) updateCallback(btcTx?.txid, btcTx==null ? undefined : (btcTx?.confirmations ?? 0), this.requiredConfirmations, txEtaMs);
331
- if(btcTx!=null && btcTx.txid!==this.txId) {
474
+ let requiredConfirmations = this.requiredConfirmations;
475
+
476
+ if(btcTx!=null && vout!=null && requiredConfirmations==null) {
477
+ requiredConfirmations = this.inferRequiredConfirmationsCount(btcTx, vout);
478
+ }
479
+
480
+ if(btcTx!=null && (btcTx.txid!==this.txId || (this.requiredConfirmations==null && requiredConfirmations!=null))) {
332
481
  this.txId = btcTx.txid;
333
482
  this.vout = vout;
483
+ this.requiredConfirmations = requiredConfirmations;
334
484
  if(btcTx.inputAddresses!=null) this.senderAddress = btcTx.inputAddresses[0];
335
485
  this._saveAndEmit().catch(e => {
336
486
  this.logger.error("waitForBitcoinTransaction(): Failed to save swap from within waitForAddressTxo callback:", e)
337
487
  });
338
488
  }
489
+
490
+ //Abort the loop as soon as the transaction gets enough confirmations, this is required in case
491
+ // we pass a default 6 confirmations to the fn, but then are able to infer the actual confirmation
492
+ // target from the prior block
493
+ if(btcTx?.confirmations!=null && requiredConfirmations!=null && requiredConfirmations<=btcTx.confirmations && vout!=null) {
494
+ abortedDueToEnoughConfirmationsResult = {
495
+ tx: btcTx,
496
+ vout
497
+ };
498
+ abortController.abort();
499
+ return;
500
+ }
501
+
502
+ if(updateCallback!=null) updateCallback(btcTx?.txid, btcTx==null ? undefined : (btcTx?.confirmations ?? 0), requiredConfirmations ?? NaN, txEtaMs);
339
503
  },
340
- abortSignal,
504
+ abortController.signal,
341
505
  checkIntervalSeconds
342
- );
506
+ ).catch(e => {
507
+ //We catch the case when the loop was aborted due to the transaction getting enough confirmations
508
+ if(abortedDueToEnoughConfirmationsResult!=null) return abortedDueToEnoughConfirmationsResult;
509
+ throw e;
510
+ });
343
511
 
344
512
  if(abortSignal!=null) abortSignal.throwIfAborted();
345
513
 
@@ -348,10 +516,10 @@ export class FromBTCSwap<T extends ChainType = ChainType>
348
516
  if(result.tx.inputAddresses!=null) this.senderAddress = result.tx.inputAddresses[0];
349
517
 
350
518
  if(
351
- (this.state as FromBTCSwapState)!==FromBTCSwapState.CLAIM_CLAIMED &&
352
- (this.state as FromBTCSwapState)!==FromBTCSwapState.FAILED
519
+ (this._state as FromBTCSwapState)!==FromBTCSwapState.CLAIM_CLAIMED &&
520
+ (this._state as FromBTCSwapState)!==FromBTCSwapState.FAILED
353
521
  ) {
354
- this.state = FromBTCSwapState.BTC_TX_CONFIRMED;
522
+ this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
355
523
  }
356
524
 
357
525
  await this._saveAndEmit();
@@ -360,34 +528,26 @@ export class FromBTCSwap<T extends ChainType = ChainType>
360
528
  }
361
529
 
362
530
  /**
363
- * Returns the PSBT that is already funded with wallet's UTXOs (runs a coin-selection algorithm to choose UTXOs to use),
364
- * also returns inputs indices that need to be signed by the wallet before submitting the PSBT back to the SDK with
365
- * `swap.submitPsbt()`
531
+ * Private getter of the funded PSBT that doesn't check current state
532
+ *
533
+ * @param _bitcoinWallet Bitcoin wallet to fund the PSBT with
534
+ * @param feeRate Optional bitcoin fee rate in sats/vB
535
+ * @param additionalOutputs Optional additional outputs that should also be included in the generated PSBT
366
536
  *
367
- * @param _bitcoinWallet Sender's bitcoin wallet
368
- * @param feeRate Optional fee rate for the transaction, needs to be at least as big as {minimumBtcFeeRate} field
369
- * @param additionalOutputs additional outputs to add to the PSBT - can be used to collect fees from users
537
+ * @private
370
538
  */
371
- getFundedPsbt(
372
- _bitcoinWallet: IBitcoinWallet | MinimalBitcoinWalletInterface,
373
- feeRate?: number,
374
- additionalOutputs?: ({amount: bigint, outputScript: Uint8Array} | {amount: bigint, address: string})[]
375
- ) {
376
- if(this.state!==FromBTCSwapState.CLAIM_COMMITED)
377
- throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
378
- return this._getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs);
379
- }
380
-
381
539
  private async _getFundedPsbt(
382
540
  _bitcoinWallet: IBitcoinWallet | MinimalBitcoinWalletInterface,
383
541
  feeRate?: number,
384
542
  additionalOutputs?: ({amount: bigint, outputScript: Uint8Array} | {amount: bigint, address: string})[]
385
543
  ): Promise<{psbt: Transaction, psbtHex: string, psbtBase64: string, signInputs: number[]}> {
544
+ if(this.address==null) throw new Error("Cannot create funded PSBT, because the address is not known! This can happen after a swap is recovered.");
545
+
386
546
  let bitcoinWallet: IBitcoinWallet;
387
547
  if(isIBitcoinWallet(_bitcoinWallet)) {
388
548
  bitcoinWallet = _bitcoinWallet;
389
549
  } else {
390
- bitcoinWallet = new SingleAddressBitcoinWallet(this.wrapper.btcRpc, this.wrapper.options.bitcoinNetwork, _bitcoinWallet);
550
+ bitcoinWallet = new SingleAddressBitcoinWallet(this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork, _bitcoinWallet);
391
551
  }
392
552
  //TODO: Maybe re-introduce fee rate check here if passed from the user
393
553
  if(feeRate==null) {
@@ -400,12 +560,12 @@ export class FromBTCSwap<T extends ChainType = ChainType>
400
560
  });
401
561
  basePsbt.addOutput({
402
562
  amount: this.amount,
403
- script: toOutputScript(this.wrapper.options.bitcoinNetwork, this.address)
563
+ script: toOutputScript(this.wrapper._options.bitcoinNetwork, this.address)
404
564
  });
405
565
  if(additionalOutputs!=null) additionalOutputs.forEach(output => {
406
566
  basePsbt.addOutput({
407
567
  amount: output.amount,
408
- script: (output as {outputScript: Uint8Array}).outputScript ?? toOutputScript(this.wrapper.options.bitcoinNetwork, (output as {address: string}).address)
568
+ script: (output as {outputScript: Uint8Array}).outputScript ?? toOutputScript(this.wrapper._options.bitcoinNetwork, (output as {address: string}).address)
409
569
  });
410
570
  });
411
571
 
@@ -425,13 +585,27 @@ export class FromBTCSwap<T extends ChainType = ChainType>
425
585
  }
426
586
 
427
587
  /**
428
- * Submits a PSBT signed by the wallet back to the SDK
588
+ * @inheritDoc
589
+ */
590
+ getFundedPsbt(
591
+ _bitcoinWallet: IBitcoinWallet | MinimalBitcoinWalletInterface,
592
+ feeRate?: number,
593
+ additionalOutputs?: ({amount: bigint, outputScript: Uint8Array} | {amount: bigint, address: string})[]
594
+ ) {
595
+ if(this._state!==FromBTCSwapState.CLAIM_COMMITED)
596
+ throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
597
+ return this._getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs);
598
+ }
599
+
600
+ /**
601
+ * @inheritDoc
429
602
  *
430
- * @param _psbt A psbt - either a Transaction object or a hex or base64 encoded PSBT string
603
+ * @throws {Error} if the swap is in invalid state (not in {@link FromBTCSwapState.CLAIM_COMMITED}), or if
604
+ * the swap bitcoin address already expired.
431
605
  */
432
606
  async submitPsbt(_psbt: Transaction | string): Promise<string> {
433
607
  const psbt = parsePsbtTransaction(_psbt);
434
- if(this.state!==FromBTCSwapState.CLAIM_COMMITED)
608
+ if(this._state!==FromBTCSwapState.CLAIM_COMMITED)
435
609
  throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
436
610
 
437
611
  //Ensure not expired
@@ -440,26 +614,37 @@ export class FromBTCSwap<T extends ChainType = ChainType>
440
614
  }
441
615
 
442
616
  const output0 = psbt.getOutput(0);
443
- if(output0.amount!==this.amount)
617
+ if(this.amount!=null && output0.amount!==this.amount)
444
618
  throw new Error("PSBT output amount invalid, expected: "+this.amount+" got: "+output0.amount);
445
- const expectedOutputScript = toOutputScript(this.wrapper.options.bitcoinNetwork, this.address);
446
- if(output0.script==null || !expectedOutputScript.equals(output0.script))
447
- throw new Error("PSBT output script invalid!");
619
+ if(this.address!=null) {
620
+ const expectedOutputScript = toOutputScript(this.wrapper._options.bitcoinNetwork, this.address);
621
+ if(output0.script==null || !expectedOutputScript.equals(output0.script))
622
+ throw new Error("PSBT output script invalid!");
623
+ }
448
624
 
449
625
  if(!psbt.isFinal) psbt.finalize();
450
626
 
451
- return await this.wrapper.btcRpc.sendRawTransaction(Buffer.from(psbt.toBytes(true, true)).toString("hex"));
627
+ return await this.wrapper._btcRpc.sendRawTransaction(Buffer.from(psbt.toBytes(true, true)).toString("hex"));
452
628
  }
453
629
 
630
+ /**
631
+ * @inheritDoc
632
+ */
454
633
  async estimateBitcoinFee(_bitcoinWallet: IBitcoinWallet | MinimalBitcoinWalletInterface, feeRate?: number): Promise<TokenAmount<any, BtcToken<false>, true> | null> {
455
- const bitcoinWallet: IBitcoinWallet = toBitcoinWallet(_bitcoinWallet, this.wrapper.btcRpc, this.wrapper.options.bitcoinNetwork);
634
+ if(this.address==null || this.amount==null) return null;
635
+ const bitcoinWallet: IBitcoinWallet = toBitcoinWallet(_bitcoinWallet, this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork);
456
636
  const txFee = await bitcoinWallet.getTransactionFee(this.address, this.amount, feeRate);
457
637
  if(txFee==null) return null;
458
- return toTokenAmount(BigInt(txFee), BitcoinTokens.BTC, this.wrapper.prices);
638
+ return toTokenAmount(BigInt(txFee), BitcoinTokens.BTC, this.wrapper._prices);
459
639
  }
460
640
 
641
+ /**
642
+ * @inheritDoc
643
+ */
461
644
  async sendBitcoinTransaction(wallet: IBitcoinWallet | MinimalBitcoinWalletInterfaceWithSigner, feeRate?: number): Promise<string> {
462
- if(this.state!==FromBTCSwapState.CLAIM_COMMITED)
645
+ if(this.address==null || this.amount==null) throw new Error("Cannot send bitcoin transaction, because the address is not known! This can happen after a swap is recovered.");
646
+
647
+ if(this._state!==FromBTCSwapState.CLAIM_COMMITED)
463
648
  throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
464
649
 
465
650
  //Ensure not expired
@@ -514,15 +699,15 @@ export class FromBTCSwap<T extends ChainType = ChainType>
514
699
  maxWaitTillAutomaticSettlementSeconds?: number
515
700
  }
516
701
  ): Promise<boolean> {
517
- if(this.state===FromBTCSwapState.FAILED) throw new Error("Swap failed!");
518
- if(this.state===FromBTCSwapState.EXPIRED) throw new Error("Swap address expired!");
519
- if(this.state===FromBTCSwapState.QUOTE_EXPIRED || this.state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) throw new Error("Swap quote expired!");
520
- if(this.state===FromBTCSwapState.CLAIM_CLAIMED) throw new Error("Swap already settled!");
702
+ if(this._state===FromBTCSwapState.FAILED) throw new Error("Swap failed!");
703
+ if(this._state===FromBTCSwapState.EXPIRED) throw new Error("Swap address expired!");
704
+ if(this._state===FromBTCSwapState.QUOTE_EXPIRED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) throw new Error("Swap quote expired!");
705
+ if(this._state===FromBTCSwapState.CLAIM_CLAIMED) throw new Error("Swap already settled!");
521
706
 
522
- if(this.state===FromBTCSwapState.PR_CREATED) {
707
+ if(this._state===FromBTCSwapState.PR_CREATED) {
523
708
  await this.commit(dstSigner, options?.abortSignal, undefined, callbacks?.onDestinationCommitSent);
524
709
  }
525
- if(this.state===FromBTCSwapState.CLAIM_COMMITED) {
710
+ if(this._state===FromBTCSwapState.CLAIM_COMMITED) {
526
711
  if(wallet!=null) {
527
712
  const bitcoinPaymentSent = await this.getBitcoinPayment();
528
713
 
@@ -538,9 +723,9 @@ export class FromBTCSwap<T extends ChainType = ChainType>
538
723
  }
539
724
 
540
725
  // @ts-ignore
541
- if(this.state===FromBTCSwapState.CLAIM_CLAIMED) return true;
726
+ if(this._state===FromBTCSwapState.CLAIM_CLAIMED) return true;
542
727
 
543
- if(this.state===FromBTCSwapState.BTC_TX_CONFIRMED) {
728
+ if(this._state===FromBTCSwapState.BTC_TX_CONFIRMED) {
544
729
  const success = await this.waitTillClaimed(options?.maxWaitTillAutomaticSettlementSeconds ?? 60, options?.abortSignal);
545
730
  if(success && callbacks?.onSwapSettled!=null) callbacks.onSwapSettled(this.getOutputTxId()!);
546
731
  return success;
@@ -549,12 +734,23 @@ export class FromBTCSwap<T extends ChainType = ChainType>
549
734
  throw new Error("Invalid state reached!");
550
735
  }
551
736
 
737
+ /**
738
+ * @inheritDoc
739
+ *
740
+ * @param options.bitcoinWallet Bitcoin wallet to use, when provided the function returns a funded
741
+ * psbt (`"FUNDED_PSBT"`), if not passed just a bitcoin receive address is returned (`"ADDRESS"`)
742
+ * @param options.skipChecks Skip checks like making sure init signature is still valid and swap
743
+ * wasn't commited yet (this is handled on swap creation, if you commit right after quoting, you
744
+ * can use `skipChecks=true`)
745
+ *
746
+ * @throws {Error} if the swap or quote is expired, or if triggered in invalid state
747
+ */
552
748
  async txsExecute(options?: {
553
749
  bitcoinWallet?: MinimalBitcoinWalletInterface,
554
750
  skipChecks?: boolean
555
751
  }) {
556
- if(this.state===FromBTCSwapState.PR_CREATED) {
557
- if(!await this.verifyQuoteValid()) throw new Error("Quote already expired or close to expiry!");
752
+ if(this._state===FromBTCSwapState.PR_CREATED) {
753
+ if(!await this._verifyQuoteValid()) throw new Error("Quote already expired or close to expiry!");
558
754
  if(this.getTimeoutTime()<Date.now()) throw new Error("Swap address already expired or close to expiry!");
559
755
  return [
560
756
  {
@@ -566,38 +762,38 @@ export class FromBTCSwap<T extends ChainType = ChainType>
566
762
  {
567
763
  name: "Payment" as const,
568
764
  description: "Send funds to the bitcoin swap address",
569
- chain: "BITCOIN",
765
+ chain: "BITCOIN" as const,
570
766
  txs: [
571
767
  options?.bitcoinWallet==null ? {
572
- address: this.address,
768
+ address: this.address ?? "",
573
769
  amount: Number(this.amount),
574
770
  hyperlink: this._getHyperlink(),
575
- type: "ADDRESS"
771
+ type: "ADDRESS" as const
576
772
  } : {
577
773
  ...await this.getFundedPsbt(options.bitcoinWallet),
578
- type: "FUNDED_PSBT"
774
+ type: "FUNDED_PSBT" as const
579
775
  }
580
776
  ]
581
777
  }
582
778
  ];
583
779
  }
584
780
 
585
- if(this.state===FromBTCSwapState.CLAIM_COMMITED) {
781
+ if(this._state===FromBTCSwapState.CLAIM_COMMITED) {
586
782
  if(this.getTimeoutTime()<Date.now()) throw new Error("Swap address already expired or close to expiry!");
587
783
  return [
588
784
  {
589
785
  name: "Payment" as const,
590
786
  description: "Send funds to the bitcoin swap address",
591
- chain: "BITCOIN",
787
+ chain: "BITCOIN" as const,
592
788
  txs: [
593
789
  options?.bitcoinWallet==null ? {
594
- address: this.address,
790
+ address: this.getAddress(),
595
791
  amount: Number(this.amount),
596
792
  hyperlink: this._getHyperlink(),
597
- type: "ADDRESS"
793
+ type: "ADDRESS" as const
598
794
  } : {
599
795
  ...await this.getFundedPsbt(options.bitcoinWallet),
600
- type: "FUNDED_PSBT"
796
+ type: "FUNDED_PSBT" as const
601
797
  }
602
798
  ]
603
799
  }
@@ -612,21 +808,16 @@ export class FromBTCSwap<T extends ChainType = ChainType>
612
808
  //// Commit
613
809
 
614
810
  /**
615
- * Commits the swap on-chain, locking the tokens from the intermediary in a PTLC
811
+ * @inheritDoc
616
812
  *
617
- * @param _signer Signer to sign the transactions with, must be the same as used in the initialization
618
- * @param abortSignal Abort signal to stop waiting for the transaction confirmation and abort
619
- * @param skipChecks Skip checks like making sure init signature is still valid and swap wasn't commited yet
620
- * (this is handled when swap is created (quoted), if you commit right after quoting, you can use skipChecks=true)
621
- * @param onBeforeTxSent
622
813
  * @throws {Error} If invalid signer is provided that doesn't match the swap data
623
814
  */
624
815
  async commit(_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal, skipChecks?: boolean, onBeforeTxSent?: (txId: string) => void): Promise<string> {
625
- const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper.chain.wrapSigner(_signer);
816
+ const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
626
817
  this.checkSigner(signer);
627
818
  let txCount = 0;
628
819
  const txs = await this.txsCommit(skipChecks);
629
- const result = await this.wrapper.chain.sendAndConfirm(
820
+ const result = await this.wrapper._chain.sendAndConfirm(
630
821
  signer, txs, true, abortSignal, undefined, (txId: string) => {
631
822
  txCount++;
632
823
  if(onBeforeTxSent!=null && txCount===txs.length) onBeforeTxSent(txId);
@@ -634,16 +825,19 @@ export class FromBTCSwap<T extends ChainType = ChainType>
634
825
  }
635
826
  );
636
827
 
637
- this.commitTxId = result[result.length - 1];
638
- if(this.state===FromBTCSwapState.PR_CREATED || this.state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
828
+ this._commitTxId = result[result.length - 1];
829
+ if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
639
830
  await this._saveAndEmit(FromBTCSwapState.CLAIM_COMMITED);
640
831
  }
641
- return this.commitTxId;
832
+ return this._commitTxId;
642
833
  }
643
834
 
835
+ /**
836
+ * @inheritDoc
837
+ */
644
838
  async waitTillCommited(abortSignal?: AbortSignal): Promise<void> {
645
- if(this.state===FromBTCSwapState.CLAIM_COMMITED || this.state===FromBTCSwapState.CLAIM_CLAIMED) return Promise.resolve();
646
- if(this.state!==FromBTCSwapState.PR_CREATED && this.state!==FromBTCSwapState.QUOTE_SOFT_EXPIRED) throw new Error("Invalid state");
839
+ if(this._state===FromBTCSwapState.CLAIM_COMMITED || this._state===FromBTCSwapState.CLAIM_CLAIMED) return Promise.resolve();
840
+ if(this._state!==FromBTCSwapState.PR_CREATED && this._state!==FromBTCSwapState.QUOTE_SOFT_EXPIRED) throw new Error("Invalid state");
647
841
 
648
842
  const abortController = extendAbortController(abortSignal);
649
843
  const result = await Promise.race([
@@ -656,13 +850,13 @@ export class FromBTCSwap<T extends ChainType = ChainType>
656
850
  if(result===true) this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
657
851
  if(result===false) {
658
852
  this.logger.debug("waitTillCommited(): Resolved from watchdog - signature expired");
659
- if(this.state===FromBTCSwapState.PR_CREATED || this.state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
853
+ if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
660
854
  await this._saveAndEmit(FromBTCSwapState.QUOTE_EXPIRED);
661
855
  }
662
856
  return;
663
857
  }
664
858
 
665
- if(this.state===FromBTCSwapState.PR_CREATED || this.state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
859
+ if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
666
860
  await this._saveAndEmit(FromBTCSwapState.CLAIM_COMMITED);
667
861
  }
668
862
  }
@@ -672,10 +866,11 @@ export class FromBTCSwap<T extends ChainType = ChainType>
672
866
  //// Claim
673
867
 
674
868
  /**
675
- * Returns transactions required to claim the swap on-chain (and possibly also sync the bitcoin light client)
676
- * after a bitcoin transaction was sent and confirmed
869
+ * Might also return transactions necessary to sync the bitcoin light client.
677
870
  *
678
- * @throws {Error} If the swap is in invalid state (must be BTC_TX_CONFIRMED)
871
+ * @inheritDoc
872
+ *
873
+ * @throws {Error} If the swap is in invalid state (must be {@link FromBTCSwapState.BTC_TX_CONFIRMED})
679
874
  */
680
875
  async txsClaim(_signer?: string | T["Signer"] | T["NativeSigner"]): Promise<T["TX"][]> {
681
876
  let signer: string | T["Signer"] | undefined = undefined;
@@ -685,41 +880,44 @@ export class FromBTCSwap<T extends ChainType = ChainType>
685
880
  } else if (isAbstractSigner(_signer)) {
686
881
  signer = _signer;
687
882
  } else {
688
- signer = await this.wrapper.chain.wrapSigner(_signer);
883
+ signer = await this.wrapper._chain.wrapSigner(_signer);
689
884
  }
690
885
  }
691
886
 
692
- if(this.state!==FromBTCSwapState.BTC_TX_CONFIRMED) throw new Error("Must be in BTC_TX_CONFIRMED state!");
887
+ if(this._state!==FromBTCSwapState.BTC_TX_CONFIRMED) throw new Error("Must be in BTC_TX_CONFIRMED state!");
693
888
  if(this.txId==null || this.vout==null) throw new Error("Bitcoin transaction ID not known!");
694
889
 
695
- const tx = await this.wrapper.btcRpc.getTransaction(this.txId);
890
+ const tx = await this.wrapper._btcRpc.getTransaction(this.txId);
696
891
  if(tx==null) throw new Error("Bitcoin transaction not found on the network!");
892
+
893
+ this.requiredConfirmations ??= this.inferRequiredConfirmationsCount(tx, this.vout);
894
+ if(this.requiredConfirmations==null)
895
+ 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.");
896
+
697
897
  if(tx.blockhash==null || tx.confirmations==null || tx.blockheight==null || tx.confirmations<this.requiredConfirmations)
698
898
  throw new Error("Bitcoin transaction not confirmed yet!");
699
899
 
700
- return await this.wrapper.contract.txsClaimWithTxData(signer ?? this._getInitiator(), this.data, {
900
+ return await this.wrapper._contract.txsClaimWithTxData(signer ?? this._getInitiator(), this._data, {
701
901
  blockhash: tx.blockhash,
702
902
  confirmations: tx.confirmations,
703
903
  txid: tx.txid,
704
904
  hex: tx.hex,
705
905
  height: tx.blockheight
706
- }, this.requiredConfirmations, this.vout, undefined, this.wrapper.synchronizer, true);
906
+ }, this.requiredConfirmations, this.vout, undefined, this.wrapper._synchronizer, true);
707
907
  }
708
908
 
709
909
  /**
710
- * Claims and finishes the swap
910
+ * Might also sync the bitcoin light client. Signer can also be different to the initializer.
711
911
  *
712
- * @param _signer Signer to sign the transactions with, can also be different to the initializer
713
- * @param abortSignal Abort signal to stop waiting for transaction confirmation
714
- * @param onBeforeTxSent
912
+ * @inheritDoc
715
913
  */
716
914
  async claim(_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal, onBeforeTxSent?: (txId: string) => void): Promise<string> {
717
- const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper.chain.wrapSigner(_signer);
915
+ const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
718
916
  let txIds: string[];
719
917
  try {
720
918
  let txCount = 0;
721
919
  const txs = await this.txsClaim(signer);
722
- txIds = await this.wrapper.chain.sendAndConfirm(
920
+ txIds = await this.wrapper._chain.sendAndConfirm(
723
921
  signer, txs, true, abortSignal, undefined, (txId: string) => {
724
922
  txCount++;
725
923
  if(onBeforeTxSent!=null && txCount===txs.length) onBeforeTxSent(txId);
@@ -728,26 +926,26 @@ export class FromBTCSwap<T extends ChainType = ChainType>
728
926
  );
729
927
  } catch (e) {
730
928
  this.logger.info("claim(): Failed to claim ourselves, checking swap claim state...");
731
- if(this.state===FromBTCSwapState.CLAIM_CLAIMED) {
929
+ if(this._state===FromBTCSwapState.CLAIM_CLAIMED) {
732
930
  this.logger.info("claim(): Transaction state is CLAIM_CLAIMED, swap was successfully claimed by the watchtower");
733
- return this.claimTxId!;
931
+ return this._claimTxId!;
734
932
  }
735
- const status = await this.wrapper.contract.getCommitStatus(this._getInitiator(), this.data);
933
+ const status = await this.wrapper._contract.getCommitStatus(this._getInitiator(), this._data);
736
934
  if(status?.type===SwapCommitStateType.PAID) {
737
935
  this.logger.info("claim(): Transaction commit status is PAID, swap was successfully claimed by the watchtower");
738
- if(this.claimTxId==null) this.claimTxId = await status.getClaimTxId();
936
+ if(this._claimTxId==null) this._claimTxId = await status.getClaimTxId();
739
937
  const txId = Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
740
938
  await this._setBitcoinTxId(txId);
741
939
  await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
742
- return this.claimTxId;
940
+ return this._claimTxId;
743
941
  }
744
942
  throw e;
745
943
  }
746
944
 
747
- this.claimTxId = txIds[txIds.length - 1];
945
+ this._claimTxId = txIds[txIds.length - 1];
748
946
  if(
749
- this.state===FromBTCSwapState.CLAIM_COMMITED || this.state===FromBTCSwapState.BTC_TX_CONFIRMED ||
750
- this.state===FromBTCSwapState.EXPIRED || this.state===FromBTCSwapState.FAILED
947
+ this._state===FromBTCSwapState.CLAIM_COMMITED || this._state===FromBTCSwapState.BTC_TX_CONFIRMED ||
948
+ this._state===FromBTCSwapState.EXPIRED || this._state===FromBTCSwapState.FAILED
751
949
  ) {
752
950
  await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
753
951
  }
@@ -755,17 +953,16 @@ export class FromBTCSwap<T extends ChainType = ChainType>
755
953
  }
756
954
 
757
955
  /**
758
- * Waits till the swap is successfully claimed
956
+ * @inheritDoc
759
957
  *
760
- * @param maxWaitTimeSeconds Maximum time in seconds to wait for the swap to be settled
761
- * @param abortSignal AbortSignal
762
- * @throws {Error} If swap is in invalid state (must be BTC_TX_CONFIRMED)
958
+ * @throws {Error} If swap is in invalid state (must be {@link FromBTCSwapState.BTC_TX_CONFIRMED})
763
959
  * @throws {Error} If the LP refunded sooner than we were able to claim
960
+ *
764
961
  * @returns {boolean} whether the swap was claimed in time or not
765
962
  */
766
963
  async waitTillClaimed(maxWaitTimeSeconds?: number, abortSignal?: AbortSignal): Promise<boolean> {
767
- if(this.state===FromBTCSwapState.CLAIM_CLAIMED) return Promise.resolve(true);
768
- if(this.state!==FromBTCSwapState.BTC_TX_CONFIRMED) throw new Error("Invalid state (not BTC_TX_CONFIRMED)");
964
+ if(this._state===FromBTCSwapState.CLAIM_CLAIMED) return Promise.resolve(true);
965
+ if(this._state!==FromBTCSwapState.BTC_TX_CONFIRMED) throw new Error("Invalid state (not BTC_TX_CONFIRMED)");
769
966
 
770
967
  const abortController = extendAbortController(abortSignal);
771
968
 
@@ -803,8 +1000,8 @@ export class FromBTCSwap<T extends ChainType = ChainType>
803
1000
  this.logger.debug("waitTillClaimed(): Resolved from watchdog");
804
1001
 
805
1002
  if(res?.type===SwapCommitStateType.PAID) {
806
- if((this.state as FromBTCSwapState)!==FromBTCSwapState.CLAIM_CLAIMED) {
807
- if(this.claimTxId==null) this.claimTxId = await res.getClaimTxId();
1003
+ if((this._state as FromBTCSwapState)!==FromBTCSwapState.CLAIM_CLAIMED) {
1004
+ if(this._claimTxId==null) this._claimTxId = await res.getClaimTxId();
808
1005
  const txId = Buffer.from(await res.getClaimResult(), "hex").reverse().toString("hex");
809
1006
  await this._setBitcoinTxId(txId);
810
1007
  await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
@@ -812,10 +1009,10 @@ export class FromBTCSwap<T extends ChainType = ChainType>
812
1009
  }
813
1010
  if(res?.type===SwapCommitStateType.NOT_COMMITED || res?.type===SwapCommitStateType.EXPIRED) {
814
1011
  if(
815
- (this.state as FromBTCSwapState)!==FromBTCSwapState.CLAIM_CLAIMED &&
816
- (this.state as FromBTCSwapState)!==FromBTCSwapState.FAILED
1012
+ (this._state as FromBTCSwapState)!==FromBTCSwapState.CLAIM_CLAIMED &&
1013
+ (this._state as FromBTCSwapState)!==FromBTCSwapState.FAILED
817
1014
  ) {
818
- if(res.getRefundTxId!=null) this.refundTxId = await res.getRefundTxId();
1015
+ if(res.getRefundTxId!=null) this._refundTxId = await res.getRefundTxId();
819
1016
  await this._saveAndEmit(FromBTCSwapState.FAILED);
820
1017
  }
821
1018
  throw new Error("Swap expired while waiting for claim!");
@@ -828,11 +1025,14 @@ export class FromBTCSwap<T extends ChainType = ChainType>
828
1025
  //////////////////////////////
829
1026
  //// Storage
830
1027
 
1028
+ /**
1029
+ * @inheritDoc
1030
+ */
831
1031
  serialize(): any {
832
1032
  return {
833
1033
  ...super.serialize(),
834
1034
  address: this.address,
835
- amount: this.amount.toString(10),
1035
+ amount: this.amount==null ? null: this.amount.toString(10),
836
1036
  requiredConfirmations: this.requiredConfirmations,
837
1037
  senderAddress: this.senderAddress,
838
1038
  txId: this.txId,
@@ -851,105 +1051,132 @@ export class FromBTCSwap<T extends ChainType = ChainType>
851
1051
  * @private
852
1052
  */
853
1053
  private async syncStateFromChain(quoteDefinitelyExpired?: boolean, commitStatus?: SwapCommitState): Promise<boolean> {
854
- if(this.state===FromBTCSwapState.PR_CREATED || this.state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
855
- const quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired(); //Make sure we check for expiry here, to prevent race conditions
856
- const status = commitStatus ?? await this.wrapper.contract.getCommitStatus(this._getInitiator(), this.data);
857
- switch(status?.type) {
858
- case SwapCommitStateType.COMMITED:
859
- this.state = FromBTCSwapState.CLAIM_COMMITED;
860
- return true;
861
- case SwapCommitStateType.EXPIRED:
862
- if(this.refundTxId==null && status.getRefundTxId) this.refundTxId = await status.getRefundTxId();
863
- this.state = FromBTCSwapState.QUOTE_EXPIRED;
864
- return true;
865
- case SwapCommitStateType.PAID:
866
- if(this.claimTxId==null) this.claimTxId = await status.getClaimTxId();
867
- const txId = Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
868
- await this._setBitcoinTxId(txId);
869
- this.state = FromBTCSwapState.CLAIM_CLAIMED;
870
- return true;
871
- }
872
-
873
- if(quoteExpired) {
874
- this.state = FromBTCSwapState.QUOTE_EXPIRED;
875
- return true;
1054
+ if(
1055
+ this._state===FromBTCSwapState.PR_CREATED ||
1056
+ this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED ||
1057
+ this._state===FromBTCSwapState.CLAIM_COMMITED ||
1058
+ this._state===FromBTCSwapState.BTC_TX_CONFIRMED ||
1059
+ this._state===FromBTCSwapState.EXPIRED
1060
+ ) {
1061
+ let quoteExpired: boolean = false;
1062
+ if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
1063
+ quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired(); //Make sure we check for expiry here, to prevent race conditions
876
1064
  }
877
1065
 
878
- return false;
879
- }
1066
+ const status = commitStatus ?? await this.wrapper._contract.getCommitStatus(this._getInitiator(), this._data);
1067
+ if(status!=null && await this._forciblySetOnchainState(status)) return true;
880
1068
 
881
- if(this.state===FromBTCSwapState.CLAIM_COMMITED || this.state===FromBTCSwapState.BTC_TX_CONFIRMED || this.state===FromBTCSwapState.EXPIRED) {
882
- const status = commitStatus ?? await this.wrapper.contract.getCommitStatus(this._getInitiator(), this.data);
883
- switch(status?.type) {
884
- case SwapCommitStateType.PAID:
885
- if(this.claimTxId==null) this.claimTxId = await status.getClaimTxId();
886
- const txId = Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
887
- await this._setBitcoinTxId(txId);
888
- this.state = FromBTCSwapState.CLAIM_CLAIMED;
889
- return true;
890
- case SwapCommitStateType.NOT_COMMITED:
891
- case SwapCommitStateType.EXPIRED:
892
- if(this.refundTxId==null && status.getRefundTxId) this.refundTxId = await status.getRefundTxId();
893
- this.state = FromBTCSwapState.FAILED;
1069
+ if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
1070
+ if(quoteExpired) {
1071
+ this._state = FromBTCSwapState.QUOTE_EXPIRED;
894
1072
  return true;
895
- case SwapCommitStateType.COMMITED:
896
- const res = await this.getBitcoinPayment();
897
- if(res!=null) {
898
- let save: boolean = false;
899
- if(this.txId!==res.txId) {
900
- if(res.inputAddresses!=null) this.senderAddress = res.inputAddresses[0];
901
- this.txId = res.txId;
902
- this.vout = res.vout;
903
- save = true;
904
- }
905
- if(res.confirmations>=this.requiredConfirmations) {
906
- this.state = FromBTCSwapState.BTC_TX_CONFIRMED;
907
- save = true;
908
- }
909
- return save;
910
- }
911
- break;
1073
+ }
912
1074
  }
913
1075
  }
914
1076
 
915
1077
  return false;
916
1078
  }
917
1079
 
918
- _shouldFetchCommitStatus(): boolean {
919
- return this.state===FromBTCSwapState.PR_CREATED || this.state===FromBTCSwapState.QUOTE_SOFT_EXPIRED ||
920
- this.state===FromBTCSwapState.CLAIM_COMMITED || this.state===FromBTCSwapState.BTC_TX_CONFIRMED ||
921
- this.state===FromBTCSwapState.EXPIRED;
1080
+ /**
1081
+ * @inheritDoc
1082
+ * @internal
1083
+ */
1084
+ _shouldFetchOnchainState(): boolean {
1085
+ return this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED ||
1086
+ this._state===FromBTCSwapState.CLAIM_COMMITED || this._state===FromBTCSwapState.BTC_TX_CONFIRMED ||
1087
+ this._state===FromBTCSwapState.EXPIRED;
922
1088
  }
923
1089
 
1090
+ /**
1091
+ * @inheritDoc
1092
+ * @internal
1093
+ */
924
1094
  _shouldFetchExpiryStatus(): boolean {
925
- return this.state===FromBTCSwapState.PR_CREATED || this.state===FromBTCSwapState.QUOTE_SOFT_EXPIRED;
1095
+ return this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED;
926
1096
  }
927
1097
 
1098
+ /**
1099
+ * @inheritDoc
1100
+ * @internal
1101
+ */
928
1102
  async _sync(save?: boolean, quoteDefinitelyExpired?: boolean, commitStatus?: SwapCommitState): Promise<boolean> {
929
1103
  const changed = await this.syncStateFromChain(quoteDefinitelyExpired, commitStatus);
930
1104
  if(changed && save) await this._saveAndEmit();
931
1105
  return changed;
932
1106
  }
933
1107
 
1108
+ /**
1109
+ * @inheritDoc
1110
+ * @internal
1111
+ */
1112
+ async _forciblySetOnchainState(status: SwapCommitState): Promise<boolean> {
1113
+ switch(status.type) {
1114
+ case SwapCommitStateType.PAID:
1115
+ if(this._claimTxId==null) this._claimTxId = await status.getClaimTxId();
1116
+ const txId = Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
1117
+ await this._setBitcoinTxId(txId);
1118
+ this._state = FromBTCSwapState.CLAIM_CLAIMED;
1119
+ return true;
1120
+ case SwapCommitStateType.NOT_COMMITED:
1121
+ if(this._refundTxId==null && status.getRefundTxId) this._refundTxId = await status.getRefundTxId();
1122
+ if(this._refundTxId!=null) {
1123
+ this._state = FromBTCSwapState.FAILED;
1124
+ return true;
1125
+ }
1126
+ break;
1127
+ case SwapCommitStateType.EXPIRED:
1128
+ if(this._refundTxId==null && status.getRefundTxId) this._refundTxId = await status.getRefundTxId();
1129
+ this._state = this._refundTxId==null ? FromBTCSwapState.QUOTE_EXPIRED : FromBTCSwapState.FAILED;
1130
+ return true;
1131
+ case SwapCommitStateType.COMMITED:
1132
+ let save: boolean = false;
1133
+ if(this._state!==FromBTCSwapState.CLAIM_COMMITED && this._state!==FromBTCSwapState.BTC_TX_CONFIRMED && this._state!==FromBTCSwapState.EXPIRED) {
1134
+ this._state = FromBTCSwapState.CLAIM_COMMITED;
1135
+ save = true;
1136
+ }
1137
+ if(this.address==null) return save;
1138
+
1139
+ const res = await this.getBitcoinPayment();
1140
+ if(res!=null) {
1141
+ if(this.txId!==res.txId) {
1142
+ if(res.inputAddresses!=null) this.senderAddress = res.inputAddresses[0];
1143
+ this.txId = res.txId;
1144
+ this.vout = res.vout;
1145
+ save = true;
1146
+ }
1147
+ if(this.requiredConfirmations!=null && res.confirmations>=this.requiredConfirmations) {
1148
+ this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
1149
+ save = true;
1150
+ }
1151
+ }
1152
+ return save;
1153
+ }
1154
+ return false;
1155
+ }
1156
+
1157
+ /**
1158
+ * @inheritDoc
1159
+ * @internal
1160
+ */
934
1161
  async _tick(save?: boolean): Promise<boolean> {
935
- switch(this.state) {
1162
+ switch(this._state) {
936
1163
  case FromBTCSwapState.PR_CREATED:
937
1164
  if(this.expiry<Date.now()) {
938
- this.state = FromBTCSwapState.QUOTE_SOFT_EXPIRED;
1165
+ this._state = FromBTCSwapState.QUOTE_SOFT_EXPIRED;
939
1166
  if(save) await this._saveAndEmit();
940
1167
  return true;
941
1168
  }
942
1169
  break;
943
1170
  case FromBTCSwapState.CLAIM_COMMITED:
944
1171
  if(this.getTimeoutTime()<Date.now()) {
945
- this.state = FromBTCSwapState.EXPIRED;
1172
+ this._state = FromBTCSwapState.EXPIRED;
946
1173
  if(save) await this._saveAndEmit();
947
1174
  return true;
948
1175
  }
949
1176
  case FromBTCSwapState.EXPIRED:
950
1177
  //Check if bitcoin payment was received every 2 minutes
951
1178
  if(Math.floor(Date.now()/1000)%120===0) {
952
- try {
1179
+ if(this.address!=null) try {
953
1180
  const res = await this.getBitcoinPayment();
954
1181
  if(res!=null) {
955
1182
  let shouldSave: boolean = false;
@@ -959,8 +1186,8 @@ export class FromBTCSwap<T extends ChainType = ChainType>
959
1186
  if(res.inputAddresses!=null) this.senderAddress = res.inputAddresses[0];
960
1187
  shouldSave = true;
961
1188
  }
962
- if(res.confirmations>=this.requiredConfirmations) {
963
- this.state = FromBTCSwapState.BTC_TX_CONFIRMED;
1189
+ if(this.requiredConfirmations!=null && res.confirmations>=this.requiredConfirmations) {
1190
+ this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
964
1191
  if(save) await this._saveAndEmit();
965
1192
  shouldSave = true;
966
1193
  }