@atomiqlabs/lp-lib 17.5.0 → 17.5.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 (170) hide show
  1. package/LICENSE +201 -201
  2. package/dist/fees/IBtcFeeEstimator.d.ts +3 -3
  3. package/dist/fees/IBtcFeeEstimator.js +2 -2
  4. package/dist/index.d.ts +43 -43
  5. package/dist/index.js +59 -59
  6. package/dist/info/InfoHandler.d.ts +17 -17
  7. package/dist/info/InfoHandler.js +61 -61
  8. package/dist/plugins/IPlugin.d.ts +158 -158
  9. package/dist/plugins/IPlugin.js +35 -35
  10. package/dist/plugins/PluginManager.d.ts +115 -115
  11. package/dist/plugins/PluginManager.js +304 -304
  12. package/dist/prices/BinanceSwapPrice.d.ts +29 -29
  13. package/dist/prices/BinanceSwapPrice.js +79 -79
  14. package/dist/prices/CoinGeckoSwapPrice.d.ts +33 -33
  15. package/dist/prices/CoinGeckoSwapPrice.js +51 -51
  16. package/dist/prices/ISwapPrice.d.ts +43 -43
  17. package/dist/prices/ISwapPrice.js +55 -55
  18. package/dist/prices/OKXSwapPrice.d.ts +29 -29
  19. package/dist/prices/OKXSwapPrice.js +79 -79
  20. package/dist/storage/IIntermediaryStorage.d.ts +18 -18
  21. package/dist/storage/IIntermediaryStorage.js +2 -2
  22. package/dist/storagemanager/IntermediaryStorageManager.d.ts +19 -19
  23. package/dist/storagemanager/IntermediaryStorageManager.js +111 -111
  24. package/dist/storagemanager/StorageManager.d.ts +13 -13
  25. package/dist/storagemanager/StorageManager.js +64 -64
  26. package/dist/swaps/SwapHandler.d.ts +178 -178
  27. package/dist/swaps/SwapHandler.js +225 -225
  28. package/dist/swaps/SwapHandlerSwap.d.ts +80 -80
  29. package/dist/swaps/SwapHandlerSwap.js +78 -78
  30. package/dist/swaps/assertions/AmountAssertions.d.ts +37 -37
  31. package/dist/swaps/assertions/AmountAssertions.js +83 -83
  32. package/dist/swaps/assertions/FromBtcAmountAssertions.d.ts +76 -76
  33. package/dist/swaps/assertions/FromBtcAmountAssertions.js +189 -189
  34. package/dist/swaps/assertions/LightningAssertions.d.ts +44 -44
  35. package/dist/swaps/assertions/LightningAssertions.js +86 -86
  36. package/dist/swaps/assertions/ToBtcAmountAssertions.d.ts +53 -53
  37. package/dist/swaps/assertions/ToBtcAmountAssertions.js +154 -154
  38. package/dist/swaps/escrow/EscrowHandler.d.ts +50 -50
  39. package/dist/swaps/escrow/EscrowHandler.js +151 -151
  40. package/dist/swaps/escrow/EscrowHandlerSwap.d.ts +35 -35
  41. package/dist/swaps/escrow/EscrowHandlerSwap.js +69 -69
  42. package/dist/swaps/escrow/FromBtcBaseSwap.d.ts +14 -14
  43. package/dist/swaps/escrow/FromBtcBaseSwap.js +32 -32
  44. package/dist/swaps/escrow/FromBtcBaseSwapHandler.d.ts +102 -102
  45. package/dist/swaps/escrow/FromBtcBaseSwapHandler.js +210 -210
  46. package/dist/swaps/escrow/ToBtcBaseSwap.d.ts +36 -36
  47. package/dist/swaps/escrow/ToBtcBaseSwap.js +67 -67
  48. package/dist/swaps/escrow/ToBtcBaseSwapHandler.d.ts +53 -53
  49. package/dist/swaps/escrow/ToBtcBaseSwapHandler.js +81 -81
  50. package/dist/swaps/escrow/frombtc_abstract/FromBtcAbs.d.ts +84 -84
  51. package/dist/swaps/escrow/frombtc_abstract/FromBtcAbs.js +329 -329
  52. package/dist/swaps/escrow/frombtc_abstract/FromBtcSwapAbs.d.ts +22 -22
  53. package/dist/swaps/escrow/frombtc_abstract/FromBtcSwapAbs.js +53 -53
  54. package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.d.ts +115 -115
  55. package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.js +719 -719
  56. package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnSwapAbs.d.ts +34 -34
  57. package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnSwapAbs.js +94 -94
  58. package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.d.ts +119 -119
  59. package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.js +730 -724
  60. package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAutoSwap.d.ts +56 -56
  61. package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAutoSwap.js +123 -123
  62. package/dist/swaps/escrow/tobtc_abstract/ToBtcAbs.d.ts +170 -170
  63. package/dist/swaps/escrow/tobtc_abstract/ToBtcAbs.js +769 -769
  64. package/dist/swaps/escrow/tobtc_abstract/ToBtcSwapAbs.d.ts +29 -29
  65. package/dist/swaps/escrow/tobtc_abstract/ToBtcSwapAbs.js +67 -67
  66. package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.d.ts +178 -178
  67. package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.js +912 -912
  68. package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnSwapAbs.d.ts +25 -25
  69. package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnSwapAbs.js +61 -61
  70. package/dist/swaps/spv_vault_swap/SpvVault.d.ts +45 -45
  71. package/dist/swaps/spv_vault_swap/SpvVault.js +148 -148
  72. package/dist/swaps/spv_vault_swap/SpvVaultSwap.d.ts +71 -71
  73. package/dist/swaps/spv_vault_swap/SpvVaultSwap.js +165 -165
  74. package/dist/swaps/spv_vault_swap/SpvVaultSwapHandler.d.ts +72 -72
  75. package/dist/swaps/spv_vault_swap/SpvVaultSwapHandler.js +710 -704
  76. package/dist/swaps/spv_vault_swap/SpvVaults.d.ts +70 -70
  77. package/dist/swaps/spv_vault_swap/SpvVaults.js +554 -554
  78. package/dist/swaps/spv_vault_swap/StickyAddress.d.ts +6 -6
  79. package/dist/swaps/spv_vault_swap/StickyAddress.js +19 -19
  80. package/dist/swaps/trusted/frombtc_trusted/FromBtcTrusted.d.ts +52 -52
  81. package/dist/swaps/trusted/frombtc_trusted/FromBtcTrusted.js +677 -677
  82. package/dist/swaps/trusted/frombtc_trusted/FromBtcTrustedSwap.d.ts +53 -53
  83. package/dist/swaps/trusted/frombtc_trusted/FromBtcTrustedSwap.js +121 -121
  84. package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.d.ts +77 -77
  85. package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.js +518 -518
  86. package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrustedSwap.d.ts +35 -35
  87. package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrustedSwap.js +84 -84
  88. package/dist/utils/BitcoinUtils.d.ts +4 -4
  89. package/dist/utils/BitcoinUtils.js +61 -61
  90. package/dist/utils/Utils.d.ts +32 -32
  91. package/dist/utils/Utils.js +130 -130
  92. package/dist/utils/paramcoders/IParamReader.d.ts +5 -5
  93. package/dist/utils/paramcoders/IParamReader.js +2 -2
  94. package/dist/utils/paramcoders/IParamWriter.d.ts +4 -4
  95. package/dist/utils/paramcoders/IParamWriter.js +2 -2
  96. package/dist/utils/paramcoders/LegacyParamEncoder.d.ts +10 -10
  97. package/dist/utils/paramcoders/LegacyParamEncoder.js +22 -22
  98. package/dist/utils/paramcoders/ParamDecoder.d.ts +25 -25
  99. package/dist/utils/paramcoders/ParamDecoder.js +222 -222
  100. package/dist/utils/paramcoders/ParamEncoder.d.ts +9 -9
  101. package/dist/utils/paramcoders/ParamEncoder.js +22 -22
  102. package/dist/utils/paramcoders/SchemaVerifier.d.ts +21 -21
  103. package/dist/utils/paramcoders/SchemaVerifier.js +84 -84
  104. package/dist/utils/paramcoders/server/ServerParamDecoder.d.ts +8 -8
  105. package/dist/utils/paramcoders/server/ServerParamDecoder.js +107 -107
  106. package/dist/utils/paramcoders/server/ServerParamEncoder.d.ts +11 -11
  107. package/dist/utils/paramcoders/server/ServerParamEncoder.js +65 -65
  108. package/dist/wallets/IBitcoinWallet.d.ts +155 -155
  109. package/dist/wallets/IBitcoinWallet.js +100 -100
  110. package/dist/wallets/ILightningWallet.d.ts +137 -137
  111. package/dist/wallets/ILightningWallet.js +37 -37
  112. package/dist/wallets/ISpvVaultSigner.d.ts +7 -7
  113. package/dist/wallets/ISpvVaultSigner.js +2 -2
  114. package/package.json +37 -37
  115. package/src/fees/IBtcFeeEstimator.ts +6 -6
  116. package/src/index.ts +54 -54
  117. package/src/info/InfoHandler.ts +106 -106
  118. package/src/plugins/IPlugin.ts +196 -196
  119. package/src/plugins/PluginManager.ts +389 -389
  120. package/src/prices/BinanceSwapPrice.ts +101 -101
  121. package/src/prices/CoinGeckoSwapPrice.ts +75 -75
  122. package/src/prices/ISwapPrice.ts +88 -88
  123. package/src/prices/OKXSwapPrice.ts +101 -101
  124. package/src/storage/IIntermediaryStorage.ts +19 -19
  125. package/src/storagemanager/IntermediaryStorageManager.ts +118 -118
  126. package/src/storagemanager/StorageManager.ts +78 -78
  127. package/src/swaps/SwapHandler.ts +337 -337
  128. package/src/swaps/SwapHandlerSwap.ts +143 -143
  129. package/src/swaps/assertions/AmountAssertions.ts +97 -97
  130. package/src/swaps/assertions/FromBtcAmountAssertions.ts +257 -257
  131. package/src/swaps/assertions/LightningAssertions.ts +103 -103
  132. package/src/swaps/assertions/ToBtcAmountAssertions.ts +209 -209
  133. package/src/swaps/escrow/EscrowHandler.ts +172 -172
  134. package/src/swaps/escrow/EscrowHandlerSwap.ts +86 -86
  135. package/src/swaps/escrow/FromBtcBaseSwap.ts +38 -38
  136. package/src/swaps/escrow/FromBtcBaseSwapHandler.ts +286 -286
  137. package/src/swaps/escrow/ToBtcBaseSwap.ts +85 -85
  138. package/src/swaps/escrow/ToBtcBaseSwapHandler.ts +129 -129
  139. package/src/swaps/escrow/frombtc_abstract/FromBtcAbs.ts +468 -468
  140. package/src/swaps/escrow/frombtc_abstract/FromBtcSwapAbs.ts +65 -65
  141. package/src/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.ts +900 -900
  142. package/src/swaps/escrow/frombtcln_abstract/FromBtcLnSwapAbs.ts +145 -145
  143. package/src/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.ts +892 -886
  144. package/src/swaps/escrow/frombtcln_autoinit/FromBtcLnAutoSwap.ts +200 -200
  145. package/src/swaps/escrow/tobtc_abstract/ToBtcAbs.ts +947 -947
  146. package/src/swaps/escrow/tobtc_abstract/ToBtcSwapAbs.ts +112 -112
  147. package/src/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.ts +1167 -1167
  148. package/src/swaps/escrow/tobtcln_abstract/ToBtcLnSwapAbs.ts +84 -84
  149. package/src/swaps/spv_vault_swap/SpvVault.ts +182 -182
  150. package/src/swaps/spv_vault_swap/SpvVaultSwap.ts +239 -239
  151. package/src/swaps/spv_vault_swap/SpvVaultSwapHandler.ts +897 -891
  152. package/src/swaps/spv_vault_swap/SpvVaults.ts +640 -640
  153. package/src/swaps/spv_vault_swap/StickyAddress.ts +20 -20
  154. package/src/swaps/trusted/frombtc_trusted/FromBtcTrusted.ts +782 -782
  155. package/src/swaps/trusted/frombtc_trusted/FromBtcTrustedSwap.ts +189 -189
  156. package/src/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.ts +622 -622
  157. package/src/swaps/trusted/frombtcln_trusted/FromBtcLnTrustedSwap.ts +125 -125
  158. package/src/utils/BitcoinUtils.ts +59 -59
  159. package/src/utils/Utils.ts +152 -152
  160. package/src/utils/paramcoders/IParamReader.ts +7 -7
  161. package/src/utils/paramcoders/IParamWriter.ts +8 -8
  162. package/src/utils/paramcoders/LegacyParamEncoder.ts +27 -27
  163. package/src/utils/paramcoders/ParamDecoder.ts +218 -218
  164. package/src/utils/paramcoders/ParamEncoder.ts +29 -29
  165. package/src/utils/paramcoders/SchemaVerifier.ts +96 -96
  166. package/src/utils/paramcoders/server/ServerParamDecoder.ts +118 -118
  167. package/src/utils/paramcoders/server/ServerParamEncoder.ts +75 -75
  168. package/src/wallets/IBitcoinWallet.ts +246 -246
  169. package/src/wallets/ILightningWallet.ts +201 -201
  170. package/src/wallets/ISpvVaultSigner.ts +10 -10
@@ -1,912 +1,912 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ToBtcLnAbs = void 0;
4
- const ToBtcLnSwapAbs_1 = require("./ToBtcLnSwapAbs");
5
- const SwapHandler_1 = require("../../SwapHandler");
6
- const base_1 = require("@atomiqlabs/base");
7
- const Utils_1 = require("../../../utils/Utils");
8
- const PluginManager_1 = require("../../../plugins/PluginManager");
9
- const crypto_1 = require("crypto");
10
- const ServerParamDecoder_1 = require("../../../utils/paramcoders/server/ServerParamDecoder");
11
- const SchemaVerifier_1 = require("../../../utils/paramcoders/SchemaVerifier");
12
- const ToBtcBaseSwapHandler_1 = require("../ToBtcBaseSwapHandler");
13
- const ILightningWallet_1 = require("../../../wallets/ILightningWallet");
14
- const LightningAssertions_1 = require("../../assertions/LightningAssertions");
15
- const IPlugin_1 = require("../../../plugins/IPlugin");
16
- /**
17
- * Swap handler handling to BTCLN swaps using submarine swaps
18
- */
19
- class ToBtcLnAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
20
- constructor(storageDirectory, path, chainData, lightning, swapPricing, config) {
21
- super(storageDirectory, path, chainData, swapPricing, config);
22
- this.type = SwapHandler_1.SwapHandlerType.TO_BTCLN;
23
- this.swapType = base_1.ChainSwapType.HTLC;
24
- this.inflightSwapStates = new Set([ToBtcLnSwapAbs_1.ToBtcLnSwapState.COMMITED, ToBtcLnSwapAbs_1.ToBtcLnSwapState.PAID]);
25
- this.activeSubscriptions = new Set();
26
- this.exactInAuths = {};
27
- this.lightning = lightning;
28
- this.LightningAssertions = new LightningAssertions_1.LightningAssertions(this.logger, lightning);
29
- const anyConfig = config;
30
- anyConfig.minTsSendCltv = config.gracePeriod + (config.bitcoinBlocktime * config.minSendCltv * config.safetyFactor);
31
- this.config = anyConfig;
32
- this.config.minLnRoutingFeePPM = this.config.minLnRoutingFeePPM || 1000n;
33
- this.config.minLnBaseFee = this.config.minLnBaseFee || 5n;
34
- this.config.exactInExpiry = this.config.exactInExpiry || 10 * 1000;
35
- this.config.lnSendBitcoinBlockTimeSafetyFactorPPM = this.config.lnSendBitcoinBlockTimeSafetyFactorPPM ?? (this.config.safetyFactor * 1000000n);
36
- if (this.config.lnSendBitcoinBlockTimeSafetyFactorPPM <= 1100000n) {
37
- throw new Error("Lightning network send block safety factor set below 1.1, this is insecure!");
38
- }
39
- }
40
- /**
41
- * Cleans up exactIn authorization that are already past their expiry
42
- *
43
- * @protected
44
- */
45
- cleanExpiredExactInAuthorizations() {
46
- for (let key in this.exactInAuths) {
47
- const obj = this.exactInAuths[key];
48
- if (obj.expiry < Date.now()) {
49
- this.logger.info("cleanExpiredExactInAuthorizations(): remove expired authorization, reqId: " + key);
50
- delete this.exactInAuths[key];
51
- }
52
- }
53
- }
54
- async processPastSwap(swap) {
55
- const { swapContract, signer } = this.getChain(swap.chainIdentifier);
56
- if (swap.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.SAVED) {
57
- //Cancel the swaps where signature is expired
58
- const isSignatureExpired = await swapContract.isInitAuthorizationExpired(swap.data, swap);
59
- if (isSignatureExpired) {
60
- const isCommitted = await swapContract.isCommited(swap.data);
61
- if (!isCommitted) {
62
- this.swapLogger.info(swap, "processPastSwap(state=SAVED): authorization expired & swap not committed, cancelling swap, invoice: " + swap.pr);
63
- await this.removeSwapData(swap, ToBtcLnSwapAbs_1.ToBtcLnSwapState.CANCELED);
64
- return;
65
- }
66
- else {
67
- this.swapLogger.info(swap, "processPastSwap(state=SAVED): swap committed (detected from processPastSwap), invoice: " + swap.pr);
68
- await swap.setState(ToBtcLnSwapAbs_1.ToBtcLnSwapState.COMMITED);
69
- await this.saveSwapData(swap);
70
- }
71
- }
72
- }
73
- if (swap.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.COMMITED || swap.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.PAID) {
74
- //Process swaps in commited & paid state
75
- await this.processInitialized(swap);
76
- }
77
- if (swap.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE) {
78
- //Remove expired swaps (as these can already be unilaterally refunded by the client), so we don't need
79
- // to be able to cooperatively refund them
80
- if (await swapContract.isExpired(swap.data.getOfferer(), swap.data)) {
81
- this.swapLogger.info(swap, "processPastSwap(state=NON_PAYABLE): swap expired, removing swap data, invoice: " + swap.pr);
82
- await this.removeSwapData(swap);
83
- }
84
- }
85
- }
86
- /**
87
- * Checks past swaps, deletes ones that are already expired, and tries to process ones that are committed.
88
- */
89
- async processPastSwaps() {
90
- this.cleanExpiredExactInAuthorizations();
91
- const queriedData = await this.storageManager.query([
92
- {
93
- key: "state",
94
- value: [
95
- ToBtcLnSwapAbs_1.ToBtcLnSwapState.SAVED,
96
- ToBtcLnSwapAbs_1.ToBtcLnSwapState.COMMITED,
97
- ToBtcLnSwapAbs_1.ToBtcLnSwapState.PAID,
98
- ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE
99
- ]
100
- }
101
- ]);
102
- for (let { obj: swap } of queriedData) {
103
- await this.processPastSwap(swap);
104
- }
105
- }
106
- /**
107
- * Tries to claim the swap funds on the SC side, returns false if the swap is already locked (claim tx is already being sent)
108
- *
109
- * @param swap
110
- * @private
111
- * @returns Whether the transaction was successfully sent
112
- */
113
- async tryClaimSwap(swap) {
114
- if (swap.secret == null)
115
- throw new Error("Invalid swap state, needs payment pre-image!");
116
- const { swapContract, signer } = this.getChain(swap.chainIdentifier);
117
- //Check if escrow state exists
118
- const isCommited = await swapContract.isCommited(swap.data);
119
- if (!isCommited) {
120
- const status = await swapContract.getCommitStatus(signer.getAddress(), swap.data);
121
- if (status?.type === base_1.SwapCommitStateType.PAID) {
122
- //This is alright, we got the money
123
- swap.txIds ?? (swap.txIds = {});
124
- swap.txIds.claim = await status.getClaimTxId();
125
- await this.removeSwapData(swap, ToBtcLnSwapAbs_1.ToBtcLnSwapState.CLAIMED);
126
- return true;
127
- }
128
- else if (status?.type === base_1.SwapCommitStateType.EXPIRED) {
129
- //This means the user was able to refund before we were able to claim, no good
130
- swap.txIds ?? (swap.txIds = {});
131
- swap.txIds.refund = status.getRefundTxId == null ? null : await status.getRefundTxId();
132
- await this.removeSwapData(swap, ToBtcLnSwapAbs_1.ToBtcLnSwapState.REFUNDED);
133
- }
134
- this.swapLogger.warn(swap, "processPaymentResult(): tried to claim but escrow doesn't exist anymore," +
135
- " status: " + status +
136
- " invoice: " + swap.pr);
137
- return false;
138
- }
139
- //Set flag that we are sending the transaction already, so we don't end up with race condition
140
- const unlock = swap.lock(swapContract.claimWithSecretTimeout);
141
- if (unlock == null)
142
- return false;
143
- try {
144
- this.swapLogger.debug(swap, "tryClaimSwap(): initiate claim of swap, secret: " + swap.secret);
145
- const success = await swapContract.claimWithSecret(signer, swap.data, swap.secret, false, false, {
146
- waitForConfirmation: true
147
- });
148
- this.swapLogger.info(swap, "tryClaimSwap(): swap claimed successfully, secret: " + swap.secret + " invoice: " + swap.pr);
149
- if (swap.metadata != null)
150
- swap.metadata.times.txClaimed = Date.now();
151
- unlock();
152
- return true;
153
- }
154
- catch (e) {
155
- this.swapLogger.error(swap, "tryClaimSwap(): error occurred claiming swap, secret: " + swap.secret + " invoice: " + swap.pr, e);
156
- return false;
157
- }
158
- }
159
- /**
160
- * Process the result of attempted lightning network payment
161
- *
162
- * @param swap
163
- * @param lnPaymentStatus
164
- */
165
- async processPaymentResult(swap, lnPaymentStatus) {
166
- switch (lnPaymentStatus.status) {
167
- case "pending":
168
- return;
169
- case "failed":
170
- this.swapLogger.info(swap, "processPaymentResult(): invoice payment failed, cancelling swap, invoice: " + swap.pr);
171
- await swap.setState(ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE);
172
- await this.saveSwapData(swap);
173
- return;
174
- case "confirmed":
175
- swap.secret = lnPaymentStatus.secret;
176
- swap.setRealNetworkFee(lnPaymentStatus.feeMtokens / 1000n);
177
- this.swapLogger.info(swap, "processPaymentResult(): invoice paid, secret: " + swap.secret + " realRoutingFee: " + swap.realNetworkFee.toString(10) + " invoice: " + swap.pr);
178
- await swap.setState(ToBtcLnSwapAbs_1.ToBtcLnSwapState.PAID);
179
- await this.saveSwapData(swap);
180
- const success = await this.tryClaimSwap(swap);
181
- if (success)
182
- this.swapLogger.info(swap, "processPaymentResult(): swap claimed successfully, invoice: " + swap.pr);
183
- return;
184
- default:
185
- throw new Error("Invalid lnPaymentStatus");
186
- }
187
- }
188
- /**
189
- * Subscribe to a pending lightning network payment attempt
190
- *
191
- * @param invoiceData
192
- */
193
- subscribeToPayment(invoiceData) {
194
- const paymentHash = invoiceData.lnPaymentHash;
195
- if (this.activeSubscriptions.has(paymentHash))
196
- return false;
197
- this.lightning.waitForPayment(paymentHash).then(result => {
198
- this.swapLogger.info(invoiceData, "subscribeToPayment(): result callback, outcome: " + result.status + " invoice: " + invoiceData.pr);
199
- this.processPaymentResult(invoiceData, result).catch(e => this.swapLogger.error(invoiceData, "subscribeToPayment(): process payment result", e));
200
- this.activeSubscriptions.delete(paymentHash);
201
- });
202
- this.swapLogger.info(invoiceData, "subscribeToPayment(): subscribe to payment outcome, invoice: " + invoiceData.pr);
203
- this.activeSubscriptions.add(paymentHash);
204
- return true;
205
- }
206
- async sendLightningPayment(swap) {
207
- const decodedPR = await this.lightning.parsePaymentRequest(swap.pr);
208
- const expiryTimestamp = swap.data.getExpiry();
209
- const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
210
- //Run checks
211
- const hasEnoughTimeToPay = (expiryTimestamp - currentTimestamp) >= this.config.minTsSendCltv;
212
- if (!hasEnoughTimeToPay)
213
- throw {
214
- code: 90005,
215
- msg: "Not enough time to reliably pay the invoice"
216
- };
217
- const isInvoiceExpired = decodedPR.expiryEpochMillis < Date.now();
218
- if (isInvoiceExpired)
219
- throw {
220
- code: 90006,
221
- msg: "Invoice already expired"
222
- };
223
- //Compute max cltv delta
224
- const maxFee = swap.quotedNetworkFee;
225
- const maxUsableCLTVdelta = (expiryTimestamp - currentTimestamp - this.config.gracePeriod)
226
- / (this.config.bitcoinBlocktime * this.config.lnSendBitcoinBlockTimeSafetyFactorPPM / 1000000n);
227
- //Initiate payment
228
- this.swapLogger.info(swap, "sendLightningPayment(): paying lightning network invoice," +
229
- " cltvDelta: " + maxUsableCLTVdelta.toString(10) +
230
- " maxFee: " + maxFee.toString(10) +
231
- " invoice: " + swap.pr);
232
- const blockHeight = await this.lightning.getBlockheight();
233
- const pluginCheckResult = await PluginManager_1.PluginManager.onHandlePreToBtcExecute(SwapHandler_1.SwapHandlerType.TO_BTCLN, swap);
234
- if ((0, IPlugin_1.isQuoteThrow)(pluginCheckResult)) {
235
- throw {
236
- code: 29999,
237
- msg: pluginCheckResult.message
238
- };
239
- }
240
- if (swap.state !== ToBtcLnSwapAbs_1.ToBtcLnSwapState.COMMITED)
241
- return false;
242
- swap.payInitiated = true;
243
- await this.saveSwapData(swap);
244
- try {
245
- await this.lightning.pay({
246
- request: swap.pr,
247
- maxFeeMtokens: maxFee * 1000n,
248
- maxTimeoutHeight: blockHeight + Number(maxUsableCLTVdelta)
249
- });
250
- }
251
- catch (e) {
252
- throw {
253
- code: 90007,
254
- msg: "Failed to initiate invoice payment",
255
- data: {
256
- error: JSON.stringify(e)
257
- }
258
- };
259
- }
260
- if (swap.metadata != null)
261
- swap.metadata.times.payComplete = Date.now();
262
- return true;
263
- }
264
- /**
265
- * Begins a lightning network payment attempt, if not attempted already
266
- *
267
- * @param swap
268
- */
269
- async processInitialized(swap) {
270
- //Check if payment was already made
271
- if (swap.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.PAID) {
272
- const success = await this.tryClaimSwap(swap);
273
- if (success)
274
- this.swapLogger.info(swap, "processInitialized(): swap claimed successfully, invoice: " + swap.pr);
275
- return;
276
- }
277
- if (swap.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.COMMITED) {
278
- if (swap.metadata != null)
279
- swap.metadata.times.payPaymentChecked = Date.now();
280
- let lnPaymentStatus = await this.lightning.getPayment(swap.lnPaymentHash);
281
- if (lnPaymentStatus != null) {
282
- if (lnPaymentStatus.status === "pending") {
283
- //Payment still ongoing, process the result
284
- this.subscribeToPayment(swap);
285
- return;
286
- }
287
- else {
288
- //Payment has already concluded, process the result
289
- await this.processPaymentResult(swap, lnPaymentStatus);
290
- return;
291
- }
292
- }
293
- else {
294
- //Payment not founds, try to process again
295
- await swap.setState(ToBtcLnSwapAbs_1.ToBtcLnSwapState.SAVED);
296
- }
297
- }
298
- if (swap.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.SAVED) {
299
- try {
300
- this.checkTooManyInflightSwaps();
301
- }
302
- catch (e) {
303
- this.swapLogger.error(swap, "processInitialized(): checking too many inflight swaps error: ", e);
304
- if ((0, Utils_1.isDefinedRuntimeError)(e)) {
305
- if (swap.metadata != null)
306
- swap.metadata.payError = e;
307
- await swap.setState(ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE);
308
- await this.saveSwapData(swap);
309
- return;
310
- }
311
- else
312
- throw e;
313
- }
314
- await swap.setState(ToBtcLnSwapAbs_1.ToBtcLnSwapState.COMMITED);
315
- await this.saveSwapData(swap);
316
- try {
317
- if (!await this.sendLightningPayment(swap))
318
- return;
319
- }
320
- catch (e) {
321
- this.swapLogger.error(swap, "processInitialized(): lightning payment error", e);
322
- if ((0, Utils_1.isDefinedRuntimeError)(e)) {
323
- if (swap.metadata != null)
324
- swap.metadata.payError = e;
325
- await swap.setState(ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE);
326
- await this.saveSwapData(swap);
327
- return;
328
- }
329
- else
330
- throw e;
331
- }
332
- this.subscribeToPayment(swap);
333
- return;
334
- }
335
- }
336
- async processInitializeEvent(chainIdentifier, swap, event) {
337
- this.swapLogger.info(swap, "SC: InitializeEvent: swap initialized by the client, invoice: " + swap.pr);
338
- //Only process swaps in SAVED state
339
- if (swap.state !== ToBtcLnSwapAbs_1.ToBtcLnSwapState.SAVED)
340
- return;
341
- await this.processInitialized(swap);
342
- }
343
- async processClaimEvent(chainIdentifier, swap, event) {
344
- this.swapLogger.info(swap, "SC: ClaimEvent: swap claimed to us, secret: " + event.result + " invoice: " + swap.pr);
345
- await this.removeSwapData(swap, ToBtcLnSwapAbs_1.ToBtcLnSwapState.CLAIMED);
346
- }
347
- async processRefundEvent(chainIdentifier, swap, event) {
348
- this.swapLogger.info(swap, "SC: RefundEvent: swap refunded back to the client, invoice: " + swap.pr);
349
- await this.removeSwapData(swap, ToBtcLnSwapAbs_1.ToBtcLnSwapState.REFUNDED);
350
- }
351
- /**
352
- * Checks if the amount was supplied in the exactIn request
353
- *
354
- * @param amount
355
- * @param exactIn
356
- * @throws {DefinedRuntimeError} will throw an error if the swap was exactIn, but amount not specified
357
- */
358
- checkAmount(amount, exactIn) {
359
- if (exactIn) {
360
- if (amount == null) {
361
- throw {
362
- code: 20040,
363
- msg: "Invalid request body (amount not specified)!"
364
- };
365
- }
366
- }
367
- }
368
- /**
369
- * Checks if the maxFee parameter is in valid range (>0)
370
- *
371
- * @param maxFee
372
- * @throws {DefinedRuntimeError} will throw an error if the maxFee is zero or negative
373
- */
374
- checkMaxFee(maxFee) {
375
- if (maxFee <= 0) {
376
- throw {
377
- code: 20030,
378
- msg: "Invalid request body (maxFee too low)!"
379
- };
380
- }
381
- }
382
- /**
383
- * Checks and parses a payment request (bolt11 invoice), additionally also checks expiration time of the invoice
384
- *
385
- * @param chainIdentifier
386
- * @param pr
387
- * @throws {DefinedRuntimeError} will throw an error if the pr is invalid, without amount or expired
388
- */
389
- async checkPaymentRequest(chainIdentifier, pr) {
390
- let parsedPR;
391
- try {
392
- parsedPR = await this.lightning.parsePaymentRequest(pr);
393
- }
394
- catch (e) {
395
- throw {
396
- code: 20021,
397
- msg: "Invalid request body (pr - cannot be parsed)"
398
- };
399
- }
400
- if (parsedPR.mtokens == null)
401
- throw {
402
- code: 20022,
403
- msg: "Invalid request body (pr - needs to have amount)"
404
- };
405
- let halfConfidence = false;
406
- if (parsedPR.expiryEpochMillis < Date.now() + ((this.getInitAuthorizationTimeout(chainIdentifier) + (2 * 60)) * 1000)) {
407
- if (!this.config.allowShortExpiry) {
408
- throw {
409
- code: 20020,
410
- msg: "Invalid request body (pr - expired)"
411
- };
412
- }
413
- else if (parsedPR.expiryEpochMillis < Date.now()) {
414
- throw {
415
- code: 20020,
416
- msg: "Invalid request body (pr - expired)"
417
- };
418
- }
419
- halfConfidence = true;
420
- }
421
- return { parsedPR, halfConfidence };
422
- }
423
- /**
424
- * Checks if the request specified too short of an expiry
425
- *
426
- * @param expiryTimestamp
427
- * @param currentTimestamp
428
- * @throws {DefinedRuntimeError} will throw an error if the expiry time is too short
429
- */
430
- checkExpiry(expiryTimestamp, currentTimestamp) {
431
- const expiresTooSoon = (expiryTimestamp - currentTimestamp) < this.config.minTsSendCltv;
432
- if (expiresTooSoon) {
433
- throw {
434
- code: 20001,
435
- msg: "Expiry time too low!"
436
- };
437
- }
438
- }
439
- /**
440
- * Estimates the routing fee & confidence by either probing or routing (if probing fails), the fee is also adjusted
441
- * according to routing fee multiplier, and subject to minimums set in config
442
- *
443
- * @param amountBD
444
- * @param maxFee
445
- * @param expiryTimestamp
446
- * @param currentTimestamp
447
- * @param pr
448
- * @param metadata
449
- * @param abortSignal
450
- * @throws {DefinedRuntimeError} will throw an error if the destination is unreachable
451
- */
452
- async checkAndGetNetworkFee(amountBD, maxFee, expiryTimestamp, currentTimestamp, pr, metadata, abortSignal) {
453
- const maxUsableCLTV = (expiryTimestamp - currentTimestamp - this.config.gracePeriod)
454
- / (this.config.bitcoinBlocktime * this.config.lnSendBitcoinBlockTimeSafetyFactorPPM / 1000000n);
455
- const blockHeight = await this.lightning.getBlockheight();
456
- abortSignal.throwIfAborted();
457
- metadata.times.blockheightFetched = Date.now();
458
- metadata.probeAndRouteTimeoutDelta = maxUsableCLTV;
459
- const maxTimeoutBlockheight = BigInt(blockHeight) + maxUsableCLTV;
460
- const req = {
461
- request: pr,
462
- amountMtokens: amountBD * 1000n,
463
- maxFeeMtokens: maxFee * 1000n,
464
- maxTimeoutHeight: Number(maxTimeoutBlockheight)
465
- };
466
- this.logger.debug(`checkAndGetNetworkFee(): Attempting to probe/route the payment amount: ${amountBD.toString(10)}` +
467
- `, maxFee: ${maxFee.toString(10)}, expiryDelta: ${maxUsableCLTV.toString(10)}, expiryHeight: ${maxTimeoutBlockheight.toString(10)}, pr: ${pr}`);
468
- let probeOrRouteResp = await this.lightning.probe(req);
469
- metadata.times.probeResult = Date.now();
470
- metadata.probeResponse = { ...probeOrRouteResp };
471
- abortSignal.throwIfAborted();
472
- if (probeOrRouteResp == null) {
473
- if (!this.config.allowProbeFailedSwaps)
474
- throw {
475
- code: 20002,
476
- msg: "Cannot route the payment!"
477
- };
478
- const routeResp = await this.lightning.route(req);
479
- metadata.times.routingResult = Date.now();
480
- metadata.routeResponse = { ...routeResp };
481
- abortSignal.throwIfAborted();
482
- if (routeResp == null)
483
- throw {
484
- code: 20002,
485
- msg: "Cannot route the payment!"
486
- };
487
- this.logger.info("checkAndGetNetworkFee(): routing result," +
488
- " destination: " + routeResp.destination +
489
- " confidence: " + routeResp.confidence +
490
- " fee mtokens: " + routeResp.feeMtokens.toString(10));
491
- probeOrRouteResp = routeResp;
492
- }
493
- else {
494
- this.logger.info("checkAndGetNetworkFee(): route probed," +
495
- " destination: " + probeOrRouteResp.destination +
496
- " confidence: " + probeOrRouteResp.confidence +
497
- " fee mtokens: " + probeOrRouteResp.feeMtokens.toString(10));
498
- }
499
- const safeFeeTokens = (probeOrRouteResp.feeMtokens + 999n) / 1000n;
500
- let actualRoutingFee = safeFeeTokens * this.config.routingFeeMultiplier;
501
- const minRoutingFee = (amountBD * this.config.minLnRoutingFeePPM / 1000000n) + this.config.minLnBaseFee;
502
- if (actualRoutingFee < minRoutingFee) {
503
- actualRoutingFee = minRoutingFee;
504
- if (actualRoutingFee > maxFee) {
505
- probeOrRouteResp.confidence = 0;
506
- }
507
- }
508
- if (actualRoutingFee > maxFee) {
509
- actualRoutingFee = maxFee;
510
- }
511
- this.logger.debug("checkAndGetNetworkFee(): network fee calculated, amount: " + amountBD.toString(10) + " fee: " + actualRoutingFee.toString(10));
512
- return {
513
- networkFee: actualRoutingFee,
514
- confidence: probeOrRouteResp.confidence
515
- };
516
- }
517
- /**
518
- * Checks and consumes (deletes & returns) exactIn authorizaton with a specific reqId
519
- *
520
- * @param reqId
521
- * @throws {DefinedRuntimeError} will throw an error if the authorization doesn't exist
522
- */
523
- checkExactInAuthorization(reqId) {
524
- const parsedAuth = this.exactInAuths[reqId];
525
- if (parsedAuth == null) {
526
- throw {
527
- code: 20070,
528
- msg: "Invalid reqId"
529
- };
530
- }
531
- delete this.exactInAuths[reqId];
532
- if (parsedAuth.expiry < Date.now()) {
533
- throw {
534
- code: 20200,
535
- msg: "Authorization already expired!"
536
- };
537
- }
538
- return parsedAuth;
539
- }
540
- /**
541
- * Checks if the newly submitted PR has the same parameters (destination, cltv_delta, routes) as the initial dummy
542
- * invoice sent for exactIn swap quote
543
- *
544
- * @param parsedRequest
545
- * @param parsedAuth
546
- */
547
- isPaymentRequestMatchingInitial(parsedRequest, parsedAuth) {
548
- return parsedRequest.destination === parsedAuth.initialInvoice.destination &&
549
- parsedRequest.cltvDelta === parsedAuth.initialInvoice.cltvDelta &&
550
- (0, ILightningWallet_1.routesMatch)(parsedRequest.routes, parsedAuth.initialInvoice.routes);
551
- }
552
- startRestServer(restServer) {
553
- restServer.use(this.path + "/payInvoiceExactIn", (0, ServerParamDecoder_1.serverParamDecoder)(10 * 1000));
554
- restServer.post(this.path + "/payInvoiceExactIn", (0, Utils_1.expressHandlerWrapper)(async (req, res) => {
555
- /**
556
- * pr: string bolt11 lightning invoice
557
- * reqId: string Identifier of the swap
558
- * feeRate: string Fee rate to use for the init tx
559
- */
560
- const parsedBody = await req.paramReader.getParams({
561
- pr: SchemaVerifier_1.FieldTypeEnum.String,
562
- reqId: SchemaVerifier_1.FieldTypeEnum.String,
563
- feeRate: SchemaVerifier_1.FieldTypeEnum.String
564
- });
565
- if (parsedBody == null) {
566
- throw {
567
- code: 20100,
568
- msg: "Invalid request body"
569
- };
570
- }
571
- this.checkTooManyInflightSwaps();
572
- const parsedAuth = this.checkExactInAuthorization(parsedBody.reqId);
573
- const responseStream = res.responseStream;
574
- const abortSignal = responseStream.getAbortSignal();
575
- //Check request params
576
- const { parsedPR, halfConfidence } = await this.checkPaymentRequest(parsedAuth.chainIdentifier, parsedBody.pr);
577
- if (parsedPR.mtokens !== parsedAuth.amount * 1000n)
578
- throw {
579
- code: 20102,
580
- msg: "Provided PR doesn't match requested (amount)!"
581
- };
582
- if (!this.isPaymentRequestMatchingInitial(parsedPR, parsedAuth)) {
583
- //The provided payment request doesn't match the parameters from the initial one, try to probe/route again
584
- // with the same max fee parameters
585
- const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
586
- const { networkFee, confidence } = await this.checkAndGetNetworkFee(parsedAuth.amount, parsedAuth.quotedNetworkFee, parsedAuth.swapExpiry, currentTimestamp, parsedBody.pr, parsedAuth.metadata, abortSignal);
587
- this.logger.info("REST: /payInvoiceExactIn: re-checked network fee for exact-in swap," +
588
- " reqId: " + parsedBody.reqId +
589
- " initialNetworkFee: " + parsedAuth.quotedNetworkFee.toString(10) +
590
- " newNetworkFee: " + networkFee.toString(10) +
591
- " oldConfidence: " + parsedAuth.confidence.toString(10) +
592
- " newConfidence: " + confidence.toString(10) +
593
- " invoice: " + parsedBody.pr);
594
- parsedAuth.confidence = confidence;
595
- }
596
- const metadata = parsedAuth.metadata;
597
- const sequence = base_1.BigIntBufferUtils.fromBuffer((0, crypto_1.randomBytes)(8));
598
- const { swapContract, signer } = this.getChain(parsedAuth.chainIdentifier);
599
- const claimHash = swapContract.getHashForHtlc(Buffer.from(parsedPR.id, "hex"));
600
- //Create swap data
601
- const payObject = await swapContract.createSwapData(base_1.ChainSwapType.HTLC, parsedAuth.offerer, signer.getAddress(), parsedAuth.token, parsedAuth.total, claimHash.toString("hex"), sequence, parsedAuth.swapExpiry, true, false, 0n, 0n);
602
- metadata.times.swapCreated = Date.now();
603
- //Sign swap data
604
- const prefetchedSignData = parsedAuth.preFetchSignData;
605
- const sigData = await this.getToBtcSignatureData(parsedAuth.chainIdentifier, payObject, req, abortSignal, prefetchedSignData);
606
- metadata.times.swapSigned = Date.now();
607
- //Create swap
608
- const createdSwap = new ToBtcLnSwapAbs_1.ToBtcLnSwapAbs(parsedAuth.chainIdentifier, parsedPR.id, parsedBody.pr, parsedPR.mtokens, parsedAuth.swapFee, parsedAuth.swapFeeInToken, parsedAuth.quotedNetworkFee, parsedAuth.quotedNetworkFeeInToken);
609
- createdSwap.data = payObject;
610
- createdSwap.metadata = metadata;
611
- createdSwap.prefix = sigData.prefix;
612
- createdSwap.timeout = sigData.timeout;
613
- createdSwap.signature = sigData.signature;
614
- createdSwap.feeRate = sigData.feeRate;
615
- await PluginManager_1.PluginManager.swapCreate(createdSwap);
616
- await this.saveSwapData(createdSwap);
617
- this.swapLogger.info(createdSwap, "REST: /payInvoiceExactIn: created exact in swap," +
618
- " reqId: " + parsedBody.reqId +
619
- " mtokens: " + parsedPR.mtokens.toString(10) +
620
- " invoice: " + createdSwap.pr);
621
- await responseStream.writeParamsAndEnd({
622
- code: 20000,
623
- msg: "Success",
624
- data: {
625
- maxFee: parsedAuth.quotedNetworkFeeInToken.toString(10),
626
- swapFee: parsedAuth.swapFeeInToken.toString(10),
627
- total: parsedAuth.total.toString(10),
628
- confidence: halfConfidence ? parsedAuth.confidence / 2000000 : parsedAuth.confidence / 1000000,
629
- address: signer.getAddress(),
630
- routingFeeSats: parsedAuth.quotedNetworkFee.toString(10),
631
- data: payObject.serialize(),
632
- prefix: sigData.prefix,
633
- timeout: sigData.timeout,
634
- signature: sigData.signature
635
- }
636
- });
637
- }));
638
- restServer.use(this.path + "/payInvoice", (0, ServerParamDecoder_1.serverParamDecoder)(10 * 1000));
639
- restServer.post(this.path + "/payInvoice", (0, Utils_1.expressHandlerWrapper)(async (req, res) => {
640
- const metadata = { request: {}, times: {} };
641
- const chainIdentifier = req.query.chain;
642
- const { swapContract, signer, chainInterface } = this.getChain(chainIdentifier);
643
- metadata.times.requestReceived = Date.now();
644
- /**
645
- *Sent initially:
646
- * pr: string bolt11 lightning invoice
647
- * maxFee: string maximum routing fee
648
- * expiryTimestamp: string expiry timestamp of the to be created HTLC, determines how many LN paths can be considered
649
- * token: string Desired token to use
650
- * offerer: string Address of the caller
651
- * exactIn: boolean Whether to do an exact in swap instead of exact out
652
- * amount: string Input amount for exactIn swaps
653
- *
654
- *Sent later:
655
- * feeRate: string Fee rate to use for the init signature
656
- */
657
- const parsedBody = await req.paramReader.getParams({
658
- pr: SchemaVerifier_1.FieldTypeEnum.String,
659
- maxFee: SchemaVerifier_1.FieldTypeEnum.BigInt,
660
- expiryTimestamp: SchemaVerifier_1.FieldTypeEnum.BigInt,
661
- token: (val) => val != null &&
662
- typeof (val) === "string" &&
663
- this.isTokenSupported(chainIdentifier, val) ? val : null,
664
- offerer: (val) => val != null &&
665
- typeof (val) === "string" &&
666
- chainInterface.isValidAddress(val, true) ? val : null,
667
- exactIn: SchemaVerifier_1.FieldTypeEnum.BooleanOptional,
668
- amount: SchemaVerifier_1.FieldTypeEnum.BigIntOptional
669
- });
670
- if (parsedBody == null) {
671
- throw {
672
- code: 20100,
673
- msg: "Invalid request body"
674
- };
675
- }
676
- metadata.request = parsedBody;
677
- const request = {
678
- chainIdentifier,
679
- raw: req,
680
- parsed: parsedBody,
681
- metadata
682
- };
683
- const useToken = parsedBody.token;
684
- const responseStream = res.responseStream;
685
- const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
686
- //Check request params
687
- this.checkTooManyInflightSwaps();
688
- this.checkAmount(parsedBody.amount, parsedBody.exactIn);
689
- this.checkMaxFee(parsedBody.maxFee);
690
- this.checkExpiry(parsedBody.expiryTimestamp, currentTimestamp);
691
- await this.checkVaultInitialized(chainIdentifier, parsedBody.token);
692
- const { parsedPR, halfConfidence } = await this.checkPaymentRequest(chainIdentifier, parsedBody.pr);
693
- const requestedAmount = {
694
- input: !!parsedBody.exactIn,
695
- amount: !!parsedBody.exactIn ? parsedBody.amount : (parsedPR.mtokens + 999n) / 1000n,
696
- token: useToken
697
- };
698
- const fees = await this.AmountAssertions.preCheckToBtcAmounts(this.type, request, requestedAmount);
699
- metadata.times.requestChecked = Date.now();
700
- //Create abort controller for parallel pre-fetches
701
- const abortController = (0, Utils_1.getAbortController)(responseStream);
702
- //Pre-fetch
703
- const { pricePrefetchPromise, signDataPrefetchPromise } = this.getToBtcPrefetches(chainIdentifier, useToken, responseStream, abortController);
704
- const nativeBalancePrefetch = this.prefetchNativeBalanceIfNeeded(chainIdentifier, abortController);
705
- //Check if prior payment has been made
706
- await this.LightningAssertions.checkPriorPayment(parsedPR.id, abortController.signal);
707
- metadata.times.priorPaymentChecked = Date.now();
708
- //Check if we still have enough native balance
709
- await this.checkNativeBalance(chainIdentifier, nativeBalancePrefetch, abortController.signal);
710
- //Check amounts
711
- const { amountBD, networkFeeData, totalInToken, swapFee, swapFeeInToken, networkFeeInToken } = await this.AmountAssertions.checkToBtcAmount(this.type, request, { ...requestedAmount, pricePrefetch: pricePrefetchPromise }, fees, async (amountBD) => {
712
- //Check if we have enough liquidity to process the swap
713
- await this.LightningAssertions.checkLiquidity(amountBD, abortController.signal, true);
714
- metadata.times.liquidityChecked = Date.now();
715
- const maxFee = parsedBody.exactIn ?
716
- await this.swapPricing.getToBtcSwapAmount(parsedBody.maxFee, useToken, chainIdentifier, null, pricePrefetchPromise) :
717
- parsedBody.maxFee;
718
- return await this.checkAndGetNetworkFee(amountBD, maxFee, parsedBody.expiryTimestamp, currentTimestamp, parsedBody.pr, metadata, abortController.signal);
719
- }, abortController.signal);
720
- metadata.times.priceCalculated = Date.now();
721
- //For exactIn swap, just save and wait for the actual invoice to be submitted
722
- if (parsedBody.exactIn) {
723
- const reqId = (0, crypto_1.randomBytes)(32).toString("hex");
724
- this.exactInAuths[reqId] = {
725
- chainIdentifier,
726
- reqId,
727
- expiry: Date.now() + this.config.exactInExpiry,
728
- amount: amountBD,
729
- initialInvoice: parsedPR,
730
- quotedNetworkFeeInToken: networkFeeInToken,
731
- swapFeeInToken,
732
- total: totalInToken,
733
- confidence: networkFeeData.confidence,
734
- quotedNetworkFee: networkFeeData.networkFee,
735
- swapFee,
736
- token: useToken,
737
- swapExpiry: parsedBody.expiryTimestamp,
738
- offerer: parsedBody.offerer,
739
- preFetchSignData: signDataPrefetchPromise != null ? await signDataPrefetchPromise : null,
740
- metadata
741
- };
742
- this.logger.info("REST: /payInvoice: created exact in swap," +
743
- " reqId: " + reqId +
744
- " amount: " + amountBD.toString(10) +
745
- " destination: " + parsedPR.destination);
746
- await responseStream.writeParamsAndEnd({
747
- code: 20000,
748
- msg: "Success",
749
- data: {
750
- amount: amountBD.toString(10),
751
- reqId
752
- }
753
- });
754
- return;
755
- }
756
- const sequence = base_1.BigIntBufferUtils.fromBuffer((0, crypto_1.randomBytes)(8));
757
- const claimHash = swapContract.getHashForHtlc(Buffer.from(parsedPR.id, "hex"));
758
- //Create swap data
759
- const payObject = await swapContract.createSwapData(base_1.ChainSwapType.HTLC, parsedBody.offerer, signer.getAddress(), useToken, totalInToken, claimHash.toString("hex"), sequence, parsedBody.expiryTimestamp, true, false, 0n, 0n);
760
- abortController.signal.throwIfAborted();
761
- metadata.times.swapCreated = Date.now();
762
- //Sign swap data
763
- const sigData = await this.getToBtcSignatureData(chainIdentifier, payObject, req, abortController.signal, signDataPrefetchPromise);
764
- metadata.times.swapSigned = Date.now();
765
- //Create swap
766
- const createdSwap = new ToBtcLnSwapAbs_1.ToBtcLnSwapAbs(chainIdentifier, parsedPR.id, parsedBody.pr, parsedPR.mtokens, swapFee, swapFeeInToken, networkFeeData.networkFee, networkFeeInToken);
767
- createdSwap.data = payObject;
768
- createdSwap.metadata = metadata;
769
- createdSwap.prefix = sigData.prefix;
770
- createdSwap.timeout = sigData.timeout;
771
- createdSwap.signature = sigData.signature;
772
- createdSwap.feeRate = sigData.feeRate;
773
- await PluginManager_1.PluginManager.swapCreate(createdSwap);
774
- await this.saveSwapData(createdSwap);
775
- this.swapLogger.info(createdSwap, "REST: /payInvoice: created swap," +
776
- " amount: " + amountBD.toString(10) +
777
- " invoice: " + createdSwap.pr);
778
- await responseStream.writeParamsAndEnd({
779
- code: 20000,
780
- msg: "Success",
781
- data: {
782
- maxFee: networkFeeInToken.toString(10),
783
- swapFee: swapFeeInToken.toString(10),
784
- total: totalInToken.toString(10),
785
- confidence: halfConfidence ? networkFeeData.confidence / 2000000 : networkFeeData.confidence / 1000000,
786
- address: signer.getAddress(),
787
- routingFeeSats: networkFeeData.networkFee.toString(10),
788
- data: payObject.serialize(),
789
- prefix: sigData.prefix,
790
- timeout: sigData.timeout,
791
- signature: sigData.signature
792
- }
793
- });
794
- }));
795
- const getRefundAuthorization = (0, Utils_1.expressHandlerWrapper)(async (req, res) => {
796
- /**
797
- * paymentHash: string Identifier of the swap
798
- * sequence: BN Sequence identifier of the swap
799
- */
800
- const parsedBody = (0, SchemaVerifier_1.verifySchema)({ ...req.body, ...req.query }, {
801
- paymentHash: (val) => val != null &&
802
- typeof (val) === "string" &&
803
- val.length === 64 &&
804
- Utils_1.HEX_REGEX.test(val) ? val : null,
805
- sequence: SchemaVerifier_1.FieldTypeEnum.BigInt
806
- });
807
- if (parsedBody == null)
808
- throw {
809
- code: 20100,
810
- msg: "Invalid request body/query (paymentHash/sequence)"
811
- };
812
- this.checkSequence(parsedBody.sequence);
813
- let data = await this.storageManager.getData(parsedBody.paymentHash, parsedBody.sequence);
814
- if (data == null) {
815
- for (let chainId in this.chains.chains) {
816
- const _data = this.getSwapByEscrowHash(chainId, parsedBody.paymentHash);
817
- if (_data != null && _data.getSequence() === parsedBody.sequence) {
818
- data = _data;
819
- break;
820
- }
821
- }
822
- }
823
- const isSwapFound = data != null;
824
- if (isSwapFound) {
825
- const { signer, swapContract } = this.getChain(data.chainIdentifier);
826
- if (await swapContract.isExpired(signer.getAddress(), data.data))
827
- throw {
828
- _httpStatus: 200,
829
- code: 20010,
830
- msg: "Payment expired"
831
- };
832
- if (data.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE) {
833
- const refundSigData = await swapContract.getRefundSignature(signer, data.data, this.config.refundAuthorizationTimeout);
834
- //Double check the state after promise result
835
- if (data.state !== ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE)
836
- throw {
837
- code: 20005,
838
- msg: "Not committed"
839
- };
840
- this.swapLogger.info(data, "REST: /getRefundAuthorization: returning refund authorization, because invoice in NON_PAYABLE state, invoice: " + data.pr);
841
- res.status(200).json({
842
- code: 20000,
843
- msg: "Success",
844
- data: {
845
- address: signer.getAddress(),
846
- prefix: refundSigData.prefix,
847
- timeout: refundSigData.timeout,
848
- signature: refundSigData.signature
849
- }
850
- });
851
- return;
852
- }
853
- }
854
- const payment = await this.lightning.getPayment(parsedBody.paymentHash);
855
- if (payment == null)
856
- throw {
857
- _httpStatus: 200,
858
- code: 20007,
859
- msg: "Payment not found"
860
- };
861
- if (payment.status === "pending") {
862
- res.status(200).json({
863
- code: 20008,
864
- msg: "Payment in-flight"
865
- });
866
- return;
867
- }
868
- if (payment.status === "confirmed") {
869
- res.status(200).json({
870
- code: 20006,
871
- msg: "Already paid",
872
- data: {
873
- secret: payment.secret
874
- }
875
- });
876
- return;
877
- }
878
- if (payment.status === "failed")
879
- throw {
880
- _httpStatus: 200,
881
- code: 20010,
882
- msg: "Payment expired",
883
- data: {
884
- reason: payment.failedReason
885
- }
886
- };
887
- });
888
- restServer.post(this.path + '/getRefundAuthorization', getRefundAuthorization);
889
- restServer.get(this.path + '/getRefundAuthorization', getRefundAuthorization);
890
- this.logger.info("started at path: ", this.path);
891
- }
892
- async init() {
893
- await this.loadData(ToBtcLnSwapAbs_1.ToBtcLnSwapAbs);
894
- //Check if all swaps contain a valid amount
895
- for (let { obj: swap } of await this.storageManager.query([])) {
896
- if (swap.amount == null || swap.lnPaymentHash == null) {
897
- const parsedPR = await this.lightning.parsePaymentRequest(swap.pr);
898
- swap.amount = (parsedPR.mtokens + 999n) / 1000n;
899
- swap.lnPaymentHash = parsedPR.id;
900
- }
901
- }
902
- this.subscribeToEvents();
903
- await PluginManager_1.PluginManager.serviceInitialize(this);
904
- }
905
- getInfoData() {
906
- return {
907
- minCltv: Number(this.config.minSendCltv),
908
- minTimestampCltv: Number(this.config.minTsSendCltv)
909
- };
910
- }
911
- }
912
- exports.ToBtcLnAbs = ToBtcLnAbs;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ToBtcLnAbs = void 0;
4
+ const ToBtcLnSwapAbs_1 = require("./ToBtcLnSwapAbs");
5
+ const SwapHandler_1 = require("../../SwapHandler");
6
+ const base_1 = require("@atomiqlabs/base");
7
+ const Utils_1 = require("../../../utils/Utils");
8
+ const PluginManager_1 = require("../../../plugins/PluginManager");
9
+ const crypto_1 = require("crypto");
10
+ const ServerParamDecoder_1 = require("../../../utils/paramcoders/server/ServerParamDecoder");
11
+ const SchemaVerifier_1 = require("../../../utils/paramcoders/SchemaVerifier");
12
+ const ToBtcBaseSwapHandler_1 = require("../ToBtcBaseSwapHandler");
13
+ const ILightningWallet_1 = require("../../../wallets/ILightningWallet");
14
+ const LightningAssertions_1 = require("../../assertions/LightningAssertions");
15
+ const IPlugin_1 = require("../../../plugins/IPlugin");
16
+ /**
17
+ * Swap handler handling to BTCLN swaps using submarine swaps
18
+ */
19
+ class ToBtcLnAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
20
+ constructor(storageDirectory, path, chainData, lightning, swapPricing, config) {
21
+ super(storageDirectory, path, chainData, swapPricing, config);
22
+ this.type = SwapHandler_1.SwapHandlerType.TO_BTCLN;
23
+ this.swapType = base_1.ChainSwapType.HTLC;
24
+ this.inflightSwapStates = new Set([ToBtcLnSwapAbs_1.ToBtcLnSwapState.COMMITED, ToBtcLnSwapAbs_1.ToBtcLnSwapState.PAID]);
25
+ this.activeSubscriptions = new Set();
26
+ this.exactInAuths = {};
27
+ this.lightning = lightning;
28
+ this.LightningAssertions = new LightningAssertions_1.LightningAssertions(this.logger, lightning);
29
+ const anyConfig = config;
30
+ anyConfig.minTsSendCltv = config.gracePeriod + (config.bitcoinBlocktime * config.minSendCltv * config.safetyFactor);
31
+ this.config = anyConfig;
32
+ this.config.minLnRoutingFeePPM = this.config.minLnRoutingFeePPM || 1000n;
33
+ this.config.minLnBaseFee = this.config.minLnBaseFee || 5n;
34
+ this.config.exactInExpiry = this.config.exactInExpiry || 10 * 1000;
35
+ this.config.lnSendBitcoinBlockTimeSafetyFactorPPM = this.config.lnSendBitcoinBlockTimeSafetyFactorPPM ?? (this.config.safetyFactor * 1000000n);
36
+ if (this.config.lnSendBitcoinBlockTimeSafetyFactorPPM <= 1100000n) {
37
+ throw new Error("Lightning network send block safety factor set below 1.1, this is insecure!");
38
+ }
39
+ }
40
+ /**
41
+ * Cleans up exactIn authorization that are already past their expiry
42
+ *
43
+ * @protected
44
+ */
45
+ cleanExpiredExactInAuthorizations() {
46
+ for (let key in this.exactInAuths) {
47
+ const obj = this.exactInAuths[key];
48
+ if (obj.expiry < Date.now()) {
49
+ this.logger.info("cleanExpiredExactInAuthorizations(): remove expired authorization, reqId: " + key);
50
+ delete this.exactInAuths[key];
51
+ }
52
+ }
53
+ }
54
+ async processPastSwap(swap) {
55
+ const { swapContract, signer } = this.getChain(swap.chainIdentifier);
56
+ if (swap.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.SAVED) {
57
+ //Cancel the swaps where signature is expired
58
+ const isSignatureExpired = await swapContract.isInitAuthorizationExpired(swap.data, swap);
59
+ if (isSignatureExpired) {
60
+ const isCommitted = await swapContract.isCommited(swap.data);
61
+ if (!isCommitted) {
62
+ this.swapLogger.info(swap, "processPastSwap(state=SAVED): authorization expired & swap not committed, cancelling swap, invoice: " + swap.pr);
63
+ await this.removeSwapData(swap, ToBtcLnSwapAbs_1.ToBtcLnSwapState.CANCELED);
64
+ return;
65
+ }
66
+ else {
67
+ this.swapLogger.info(swap, "processPastSwap(state=SAVED): swap committed (detected from processPastSwap), invoice: " + swap.pr);
68
+ await swap.setState(ToBtcLnSwapAbs_1.ToBtcLnSwapState.COMMITED);
69
+ await this.saveSwapData(swap);
70
+ }
71
+ }
72
+ }
73
+ if (swap.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.COMMITED || swap.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.PAID) {
74
+ //Process swaps in commited & paid state
75
+ await this.processInitialized(swap);
76
+ }
77
+ if (swap.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE) {
78
+ //Remove expired swaps (as these can already be unilaterally refunded by the client), so we don't need
79
+ // to be able to cooperatively refund them
80
+ if (await swapContract.isExpired(swap.data.getOfferer(), swap.data)) {
81
+ this.swapLogger.info(swap, "processPastSwap(state=NON_PAYABLE): swap expired, removing swap data, invoice: " + swap.pr);
82
+ await this.removeSwapData(swap);
83
+ }
84
+ }
85
+ }
86
+ /**
87
+ * Checks past swaps, deletes ones that are already expired, and tries to process ones that are committed.
88
+ */
89
+ async processPastSwaps() {
90
+ this.cleanExpiredExactInAuthorizations();
91
+ const queriedData = await this.storageManager.query([
92
+ {
93
+ key: "state",
94
+ value: [
95
+ ToBtcLnSwapAbs_1.ToBtcLnSwapState.SAVED,
96
+ ToBtcLnSwapAbs_1.ToBtcLnSwapState.COMMITED,
97
+ ToBtcLnSwapAbs_1.ToBtcLnSwapState.PAID,
98
+ ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE
99
+ ]
100
+ }
101
+ ]);
102
+ for (let { obj: swap } of queriedData) {
103
+ await this.processPastSwap(swap);
104
+ }
105
+ }
106
+ /**
107
+ * Tries to claim the swap funds on the SC side, returns false if the swap is already locked (claim tx is already being sent)
108
+ *
109
+ * @param swap
110
+ * @private
111
+ * @returns Whether the transaction was successfully sent
112
+ */
113
+ async tryClaimSwap(swap) {
114
+ if (swap.secret == null)
115
+ throw new Error("Invalid swap state, needs payment pre-image!");
116
+ const { swapContract, signer } = this.getChain(swap.chainIdentifier);
117
+ //Check if escrow state exists
118
+ const isCommited = await swapContract.isCommited(swap.data);
119
+ if (!isCommited) {
120
+ const status = await swapContract.getCommitStatus(signer.getAddress(), swap.data);
121
+ if (status?.type === base_1.SwapCommitStateType.PAID) {
122
+ //This is alright, we got the money
123
+ swap.txIds ?? (swap.txIds = {});
124
+ swap.txIds.claim = await status.getClaimTxId();
125
+ await this.removeSwapData(swap, ToBtcLnSwapAbs_1.ToBtcLnSwapState.CLAIMED);
126
+ return true;
127
+ }
128
+ else if (status?.type === base_1.SwapCommitStateType.EXPIRED) {
129
+ //This means the user was able to refund before we were able to claim, no good
130
+ swap.txIds ?? (swap.txIds = {});
131
+ swap.txIds.refund = status.getRefundTxId == null ? null : await status.getRefundTxId();
132
+ await this.removeSwapData(swap, ToBtcLnSwapAbs_1.ToBtcLnSwapState.REFUNDED);
133
+ }
134
+ this.swapLogger.warn(swap, "processPaymentResult(): tried to claim but escrow doesn't exist anymore," +
135
+ " status: " + status +
136
+ " invoice: " + swap.pr);
137
+ return false;
138
+ }
139
+ //Set flag that we are sending the transaction already, so we don't end up with race condition
140
+ const unlock = swap.lock(swapContract.claimWithSecretTimeout);
141
+ if (unlock == null)
142
+ return false;
143
+ try {
144
+ this.swapLogger.debug(swap, "tryClaimSwap(): initiate claim of swap, secret: " + swap.secret);
145
+ const success = await swapContract.claimWithSecret(signer, swap.data, swap.secret, false, false, {
146
+ waitForConfirmation: true
147
+ });
148
+ this.swapLogger.info(swap, "tryClaimSwap(): swap claimed successfully, secret: " + swap.secret + " invoice: " + swap.pr);
149
+ if (swap.metadata != null)
150
+ swap.metadata.times.txClaimed = Date.now();
151
+ unlock();
152
+ return true;
153
+ }
154
+ catch (e) {
155
+ this.swapLogger.error(swap, "tryClaimSwap(): error occurred claiming swap, secret: " + swap.secret + " invoice: " + swap.pr, e);
156
+ return false;
157
+ }
158
+ }
159
+ /**
160
+ * Process the result of attempted lightning network payment
161
+ *
162
+ * @param swap
163
+ * @param lnPaymentStatus
164
+ */
165
+ async processPaymentResult(swap, lnPaymentStatus) {
166
+ switch (lnPaymentStatus.status) {
167
+ case "pending":
168
+ return;
169
+ case "failed":
170
+ this.swapLogger.info(swap, "processPaymentResult(): invoice payment failed, cancelling swap, invoice: " + swap.pr);
171
+ await swap.setState(ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE);
172
+ await this.saveSwapData(swap);
173
+ return;
174
+ case "confirmed":
175
+ swap.secret = lnPaymentStatus.secret;
176
+ swap.setRealNetworkFee(lnPaymentStatus.feeMtokens / 1000n);
177
+ this.swapLogger.info(swap, "processPaymentResult(): invoice paid, secret: " + swap.secret + " realRoutingFee: " + swap.realNetworkFee.toString(10) + " invoice: " + swap.pr);
178
+ await swap.setState(ToBtcLnSwapAbs_1.ToBtcLnSwapState.PAID);
179
+ await this.saveSwapData(swap);
180
+ const success = await this.tryClaimSwap(swap);
181
+ if (success)
182
+ this.swapLogger.info(swap, "processPaymentResult(): swap claimed successfully, invoice: " + swap.pr);
183
+ return;
184
+ default:
185
+ throw new Error("Invalid lnPaymentStatus");
186
+ }
187
+ }
188
+ /**
189
+ * Subscribe to a pending lightning network payment attempt
190
+ *
191
+ * @param invoiceData
192
+ */
193
+ subscribeToPayment(invoiceData) {
194
+ const paymentHash = invoiceData.lnPaymentHash;
195
+ if (this.activeSubscriptions.has(paymentHash))
196
+ return false;
197
+ this.lightning.waitForPayment(paymentHash).then(result => {
198
+ this.swapLogger.info(invoiceData, "subscribeToPayment(): result callback, outcome: " + result.status + " invoice: " + invoiceData.pr);
199
+ this.processPaymentResult(invoiceData, result).catch(e => this.swapLogger.error(invoiceData, "subscribeToPayment(): process payment result", e));
200
+ this.activeSubscriptions.delete(paymentHash);
201
+ });
202
+ this.swapLogger.info(invoiceData, "subscribeToPayment(): subscribe to payment outcome, invoice: " + invoiceData.pr);
203
+ this.activeSubscriptions.add(paymentHash);
204
+ return true;
205
+ }
206
+ async sendLightningPayment(swap) {
207
+ const decodedPR = await this.lightning.parsePaymentRequest(swap.pr);
208
+ const expiryTimestamp = swap.data.getExpiry();
209
+ const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
210
+ //Run checks
211
+ const hasEnoughTimeToPay = (expiryTimestamp - currentTimestamp) >= this.config.minTsSendCltv;
212
+ if (!hasEnoughTimeToPay)
213
+ throw {
214
+ code: 90005,
215
+ msg: "Not enough time to reliably pay the invoice"
216
+ };
217
+ const isInvoiceExpired = decodedPR.expiryEpochMillis < Date.now();
218
+ if (isInvoiceExpired)
219
+ throw {
220
+ code: 90006,
221
+ msg: "Invoice already expired"
222
+ };
223
+ //Compute max cltv delta
224
+ const maxFee = swap.quotedNetworkFee;
225
+ const maxUsableCLTVdelta = (expiryTimestamp - currentTimestamp - this.config.gracePeriod)
226
+ / (this.config.bitcoinBlocktime * this.config.lnSendBitcoinBlockTimeSafetyFactorPPM / 1000000n);
227
+ //Initiate payment
228
+ this.swapLogger.info(swap, "sendLightningPayment(): paying lightning network invoice," +
229
+ " cltvDelta: " + maxUsableCLTVdelta.toString(10) +
230
+ " maxFee: " + maxFee.toString(10) +
231
+ " invoice: " + swap.pr);
232
+ const blockHeight = await this.lightning.getBlockheight();
233
+ const pluginCheckResult = await PluginManager_1.PluginManager.onHandlePreToBtcExecute(SwapHandler_1.SwapHandlerType.TO_BTCLN, swap);
234
+ if ((0, IPlugin_1.isQuoteThrow)(pluginCheckResult)) {
235
+ throw {
236
+ code: 29999,
237
+ msg: pluginCheckResult.message
238
+ };
239
+ }
240
+ if (swap.state !== ToBtcLnSwapAbs_1.ToBtcLnSwapState.COMMITED)
241
+ return false;
242
+ swap.payInitiated = true;
243
+ await this.saveSwapData(swap);
244
+ try {
245
+ await this.lightning.pay({
246
+ request: swap.pr,
247
+ maxFeeMtokens: maxFee * 1000n,
248
+ maxTimeoutHeight: blockHeight + Number(maxUsableCLTVdelta)
249
+ });
250
+ }
251
+ catch (e) {
252
+ throw {
253
+ code: 90007,
254
+ msg: "Failed to initiate invoice payment",
255
+ data: {
256
+ error: JSON.stringify(e)
257
+ }
258
+ };
259
+ }
260
+ if (swap.metadata != null)
261
+ swap.metadata.times.payComplete = Date.now();
262
+ return true;
263
+ }
264
+ /**
265
+ * Begins a lightning network payment attempt, if not attempted already
266
+ *
267
+ * @param swap
268
+ */
269
+ async processInitialized(swap) {
270
+ //Check if payment was already made
271
+ if (swap.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.PAID) {
272
+ const success = await this.tryClaimSwap(swap);
273
+ if (success)
274
+ this.swapLogger.info(swap, "processInitialized(): swap claimed successfully, invoice: " + swap.pr);
275
+ return;
276
+ }
277
+ if (swap.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.COMMITED) {
278
+ if (swap.metadata != null)
279
+ swap.metadata.times.payPaymentChecked = Date.now();
280
+ let lnPaymentStatus = await this.lightning.getPayment(swap.lnPaymentHash);
281
+ if (lnPaymentStatus != null) {
282
+ if (lnPaymentStatus.status === "pending") {
283
+ //Payment still ongoing, process the result
284
+ this.subscribeToPayment(swap);
285
+ return;
286
+ }
287
+ else {
288
+ //Payment has already concluded, process the result
289
+ await this.processPaymentResult(swap, lnPaymentStatus);
290
+ return;
291
+ }
292
+ }
293
+ else {
294
+ //Payment not founds, try to process again
295
+ await swap.setState(ToBtcLnSwapAbs_1.ToBtcLnSwapState.SAVED);
296
+ }
297
+ }
298
+ if (swap.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.SAVED) {
299
+ try {
300
+ this.checkTooManyInflightSwaps();
301
+ }
302
+ catch (e) {
303
+ this.swapLogger.error(swap, "processInitialized(): checking too many inflight swaps error: ", e);
304
+ if ((0, Utils_1.isDefinedRuntimeError)(e)) {
305
+ if (swap.metadata != null)
306
+ swap.metadata.payError = e;
307
+ await swap.setState(ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE);
308
+ await this.saveSwapData(swap);
309
+ return;
310
+ }
311
+ else
312
+ throw e;
313
+ }
314
+ await swap.setState(ToBtcLnSwapAbs_1.ToBtcLnSwapState.COMMITED);
315
+ await this.saveSwapData(swap);
316
+ try {
317
+ if (!await this.sendLightningPayment(swap))
318
+ return;
319
+ }
320
+ catch (e) {
321
+ this.swapLogger.error(swap, "processInitialized(): lightning payment error", e);
322
+ if ((0, Utils_1.isDefinedRuntimeError)(e)) {
323
+ if (swap.metadata != null)
324
+ swap.metadata.payError = e;
325
+ await swap.setState(ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE);
326
+ await this.saveSwapData(swap);
327
+ return;
328
+ }
329
+ else
330
+ throw e;
331
+ }
332
+ this.subscribeToPayment(swap);
333
+ return;
334
+ }
335
+ }
336
+ async processInitializeEvent(chainIdentifier, swap, event) {
337
+ this.swapLogger.info(swap, "SC: InitializeEvent: swap initialized by the client, invoice: " + swap.pr);
338
+ //Only process swaps in SAVED state
339
+ if (swap.state !== ToBtcLnSwapAbs_1.ToBtcLnSwapState.SAVED)
340
+ return;
341
+ await this.processInitialized(swap);
342
+ }
343
+ async processClaimEvent(chainIdentifier, swap, event) {
344
+ this.swapLogger.info(swap, "SC: ClaimEvent: swap claimed to us, secret: " + event.result + " invoice: " + swap.pr);
345
+ await this.removeSwapData(swap, ToBtcLnSwapAbs_1.ToBtcLnSwapState.CLAIMED);
346
+ }
347
+ async processRefundEvent(chainIdentifier, swap, event) {
348
+ this.swapLogger.info(swap, "SC: RefundEvent: swap refunded back to the client, invoice: " + swap.pr);
349
+ await this.removeSwapData(swap, ToBtcLnSwapAbs_1.ToBtcLnSwapState.REFUNDED);
350
+ }
351
+ /**
352
+ * Checks if the amount was supplied in the exactIn request
353
+ *
354
+ * @param amount
355
+ * @param exactIn
356
+ * @throws {DefinedRuntimeError} will throw an error if the swap was exactIn, but amount not specified
357
+ */
358
+ checkAmount(amount, exactIn) {
359
+ if (exactIn) {
360
+ if (amount == null) {
361
+ throw {
362
+ code: 20040,
363
+ msg: "Invalid request body (amount not specified)!"
364
+ };
365
+ }
366
+ }
367
+ }
368
+ /**
369
+ * Checks if the maxFee parameter is in valid range (>0)
370
+ *
371
+ * @param maxFee
372
+ * @throws {DefinedRuntimeError} will throw an error if the maxFee is zero or negative
373
+ */
374
+ checkMaxFee(maxFee) {
375
+ if (maxFee <= 0) {
376
+ throw {
377
+ code: 20030,
378
+ msg: "Invalid request body (maxFee too low)!"
379
+ };
380
+ }
381
+ }
382
+ /**
383
+ * Checks and parses a payment request (bolt11 invoice), additionally also checks expiration time of the invoice
384
+ *
385
+ * @param chainIdentifier
386
+ * @param pr
387
+ * @throws {DefinedRuntimeError} will throw an error if the pr is invalid, without amount or expired
388
+ */
389
+ async checkPaymentRequest(chainIdentifier, pr) {
390
+ let parsedPR;
391
+ try {
392
+ parsedPR = await this.lightning.parsePaymentRequest(pr);
393
+ }
394
+ catch (e) {
395
+ throw {
396
+ code: 20021,
397
+ msg: "Invalid request body (pr - cannot be parsed)"
398
+ };
399
+ }
400
+ if (parsedPR.mtokens == null)
401
+ throw {
402
+ code: 20022,
403
+ msg: "Invalid request body (pr - needs to have amount)"
404
+ };
405
+ let halfConfidence = false;
406
+ if (parsedPR.expiryEpochMillis < Date.now() + ((this.getInitAuthorizationTimeout(chainIdentifier) + (2 * 60)) * 1000)) {
407
+ if (!this.config.allowShortExpiry) {
408
+ throw {
409
+ code: 20020,
410
+ msg: "Invalid request body (pr - expired)"
411
+ };
412
+ }
413
+ else if (parsedPR.expiryEpochMillis < Date.now()) {
414
+ throw {
415
+ code: 20020,
416
+ msg: "Invalid request body (pr - expired)"
417
+ };
418
+ }
419
+ halfConfidence = true;
420
+ }
421
+ return { parsedPR, halfConfidence };
422
+ }
423
+ /**
424
+ * Checks if the request specified too short of an expiry
425
+ *
426
+ * @param expiryTimestamp
427
+ * @param currentTimestamp
428
+ * @throws {DefinedRuntimeError} will throw an error if the expiry time is too short
429
+ */
430
+ checkExpiry(expiryTimestamp, currentTimestamp) {
431
+ const expiresTooSoon = (expiryTimestamp - currentTimestamp) < this.config.minTsSendCltv;
432
+ if (expiresTooSoon) {
433
+ throw {
434
+ code: 20001,
435
+ msg: "Expiry time too low!"
436
+ };
437
+ }
438
+ }
439
+ /**
440
+ * Estimates the routing fee & confidence by either probing or routing (if probing fails), the fee is also adjusted
441
+ * according to routing fee multiplier, and subject to minimums set in config
442
+ *
443
+ * @param amountBD
444
+ * @param maxFee
445
+ * @param expiryTimestamp
446
+ * @param currentTimestamp
447
+ * @param pr
448
+ * @param metadata
449
+ * @param abortSignal
450
+ * @throws {DefinedRuntimeError} will throw an error if the destination is unreachable
451
+ */
452
+ async checkAndGetNetworkFee(amountBD, maxFee, expiryTimestamp, currentTimestamp, pr, metadata, abortSignal) {
453
+ const maxUsableCLTV = (expiryTimestamp - currentTimestamp - this.config.gracePeriod)
454
+ / (this.config.bitcoinBlocktime * this.config.lnSendBitcoinBlockTimeSafetyFactorPPM / 1000000n);
455
+ const blockHeight = await this.lightning.getBlockheight();
456
+ abortSignal.throwIfAborted();
457
+ metadata.times.blockheightFetched = Date.now();
458
+ metadata.probeAndRouteTimeoutDelta = maxUsableCLTV;
459
+ const maxTimeoutBlockheight = BigInt(blockHeight) + maxUsableCLTV;
460
+ const req = {
461
+ request: pr,
462
+ amountMtokens: amountBD * 1000n,
463
+ maxFeeMtokens: maxFee * 1000n,
464
+ maxTimeoutHeight: Number(maxTimeoutBlockheight)
465
+ };
466
+ this.logger.debug(`checkAndGetNetworkFee(): Attempting to probe/route the payment amount: ${amountBD.toString(10)}` +
467
+ `, maxFee: ${maxFee.toString(10)}, expiryDelta: ${maxUsableCLTV.toString(10)}, expiryHeight: ${maxTimeoutBlockheight.toString(10)}, pr: ${pr}`);
468
+ let probeOrRouteResp = await this.lightning.probe(req);
469
+ metadata.times.probeResult = Date.now();
470
+ metadata.probeResponse = { ...probeOrRouteResp };
471
+ abortSignal.throwIfAborted();
472
+ if (probeOrRouteResp == null) {
473
+ if (!this.config.allowProbeFailedSwaps)
474
+ throw {
475
+ code: 20002,
476
+ msg: "Cannot route the payment!"
477
+ };
478
+ const routeResp = await this.lightning.route(req);
479
+ metadata.times.routingResult = Date.now();
480
+ metadata.routeResponse = { ...routeResp };
481
+ abortSignal.throwIfAborted();
482
+ if (routeResp == null)
483
+ throw {
484
+ code: 20002,
485
+ msg: "Cannot route the payment!"
486
+ };
487
+ this.logger.info("checkAndGetNetworkFee(): routing result," +
488
+ " destination: " + routeResp.destination +
489
+ " confidence: " + routeResp.confidence +
490
+ " fee mtokens: " + routeResp.feeMtokens.toString(10));
491
+ probeOrRouteResp = routeResp;
492
+ }
493
+ else {
494
+ this.logger.info("checkAndGetNetworkFee(): route probed," +
495
+ " destination: " + probeOrRouteResp.destination +
496
+ " confidence: " + probeOrRouteResp.confidence +
497
+ " fee mtokens: " + probeOrRouteResp.feeMtokens.toString(10));
498
+ }
499
+ const safeFeeTokens = (probeOrRouteResp.feeMtokens + 999n) / 1000n;
500
+ let actualRoutingFee = safeFeeTokens * this.config.routingFeeMultiplier;
501
+ const minRoutingFee = (amountBD * this.config.minLnRoutingFeePPM / 1000000n) + this.config.minLnBaseFee;
502
+ if (actualRoutingFee < minRoutingFee) {
503
+ actualRoutingFee = minRoutingFee;
504
+ if (actualRoutingFee > maxFee) {
505
+ probeOrRouteResp.confidence = 0;
506
+ }
507
+ }
508
+ if (actualRoutingFee > maxFee) {
509
+ actualRoutingFee = maxFee;
510
+ }
511
+ this.logger.debug("checkAndGetNetworkFee(): network fee calculated, amount: " + amountBD.toString(10) + " fee: " + actualRoutingFee.toString(10));
512
+ return {
513
+ networkFee: actualRoutingFee,
514
+ confidence: probeOrRouteResp.confidence
515
+ };
516
+ }
517
+ /**
518
+ * Checks and consumes (deletes & returns) exactIn authorizaton with a specific reqId
519
+ *
520
+ * @param reqId
521
+ * @throws {DefinedRuntimeError} will throw an error if the authorization doesn't exist
522
+ */
523
+ checkExactInAuthorization(reqId) {
524
+ const parsedAuth = this.exactInAuths[reqId];
525
+ if (parsedAuth == null) {
526
+ throw {
527
+ code: 20070,
528
+ msg: "Invalid reqId"
529
+ };
530
+ }
531
+ delete this.exactInAuths[reqId];
532
+ if (parsedAuth.expiry < Date.now()) {
533
+ throw {
534
+ code: 20200,
535
+ msg: "Authorization already expired!"
536
+ };
537
+ }
538
+ return parsedAuth;
539
+ }
540
+ /**
541
+ * Checks if the newly submitted PR has the same parameters (destination, cltv_delta, routes) as the initial dummy
542
+ * invoice sent for exactIn swap quote
543
+ *
544
+ * @param parsedRequest
545
+ * @param parsedAuth
546
+ */
547
+ isPaymentRequestMatchingInitial(parsedRequest, parsedAuth) {
548
+ return parsedRequest.destination === parsedAuth.initialInvoice.destination &&
549
+ parsedRequest.cltvDelta === parsedAuth.initialInvoice.cltvDelta &&
550
+ (0, ILightningWallet_1.routesMatch)(parsedRequest.routes, parsedAuth.initialInvoice.routes);
551
+ }
552
+ startRestServer(restServer) {
553
+ restServer.use(this.path + "/payInvoiceExactIn", (0, ServerParamDecoder_1.serverParamDecoder)(10 * 1000));
554
+ restServer.post(this.path + "/payInvoiceExactIn", (0, Utils_1.expressHandlerWrapper)(async (req, res) => {
555
+ /**
556
+ * pr: string bolt11 lightning invoice
557
+ * reqId: string Identifier of the swap
558
+ * feeRate: string Fee rate to use for the init tx
559
+ */
560
+ const parsedBody = await req.paramReader.getParams({
561
+ pr: SchemaVerifier_1.FieldTypeEnum.String,
562
+ reqId: SchemaVerifier_1.FieldTypeEnum.String,
563
+ feeRate: SchemaVerifier_1.FieldTypeEnum.String
564
+ });
565
+ if (parsedBody == null) {
566
+ throw {
567
+ code: 20100,
568
+ msg: "Invalid request body"
569
+ };
570
+ }
571
+ this.checkTooManyInflightSwaps();
572
+ const parsedAuth = this.checkExactInAuthorization(parsedBody.reqId);
573
+ const responseStream = res.responseStream;
574
+ const abortSignal = responseStream.getAbortSignal();
575
+ //Check request params
576
+ const { parsedPR, halfConfidence } = await this.checkPaymentRequest(parsedAuth.chainIdentifier, parsedBody.pr);
577
+ if (parsedPR.mtokens !== parsedAuth.amount * 1000n)
578
+ throw {
579
+ code: 20102,
580
+ msg: "Provided PR doesn't match requested (amount)!"
581
+ };
582
+ if (!this.isPaymentRequestMatchingInitial(parsedPR, parsedAuth)) {
583
+ //The provided payment request doesn't match the parameters from the initial one, try to probe/route again
584
+ // with the same max fee parameters
585
+ const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
586
+ const { networkFee, confidence } = await this.checkAndGetNetworkFee(parsedAuth.amount, parsedAuth.quotedNetworkFee, parsedAuth.swapExpiry, currentTimestamp, parsedBody.pr, parsedAuth.metadata, abortSignal);
587
+ this.logger.info("REST: /payInvoiceExactIn: re-checked network fee for exact-in swap," +
588
+ " reqId: " + parsedBody.reqId +
589
+ " initialNetworkFee: " + parsedAuth.quotedNetworkFee.toString(10) +
590
+ " newNetworkFee: " + networkFee.toString(10) +
591
+ " oldConfidence: " + parsedAuth.confidence.toString(10) +
592
+ " newConfidence: " + confidence.toString(10) +
593
+ " invoice: " + parsedBody.pr);
594
+ parsedAuth.confidence = confidence;
595
+ }
596
+ const metadata = parsedAuth.metadata;
597
+ const sequence = base_1.BigIntBufferUtils.fromBuffer((0, crypto_1.randomBytes)(8));
598
+ const { swapContract, signer } = this.getChain(parsedAuth.chainIdentifier);
599
+ const claimHash = swapContract.getHashForHtlc(Buffer.from(parsedPR.id, "hex"));
600
+ //Create swap data
601
+ const payObject = await swapContract.createSwapData(base_1.ChainSwapType.HTLC, parsedAuth.offerer, signer.getAddress(), parsedAuth.token, parsedAuth.total, claimHash.toString("hex"), sequence, parsedAuth.swapExpiry, true, false, 0n, 0n);
602
+ metadata.times.swapCreated = Date.now();
603
+ //Sign swap data
604
+ const prefetchedSignData = parsedAuth.preFetchSignData;
605
+ const sigData = await this.getToBtcSignatureData(parsedAuth.chainIdentifier, payObject, req, abortSignal, prefetchedSignData);
606
+ metadata.times.swapSigned = Date.now();
607
+ //Create swap
608
+ const createdSwap = new ToBtcLnSwapAbs_1.ToBtcLnSwapAbs(parsedAuth.chainIdentifier, parsedPR.id, parsedBody.pr, parsedPR.mtokens, parsedAuth.swapFee, parsedAuth.swapFeeInToken, parsedAuth.quotedNetworkFee, parsedAuth.quotedNetworkFeeInToken);
609
+ createdSwap.data = payObject;
610
+ createdSwap.metadata = metadata;
611
+ createdSwap.prefix = sigData.prefix;
612
+ createdSwap.timeout = sigData.timeout;
613
+ createdSwap.signature = sigData.signature;
614
+ createdSwap.feeRate = sigData.feeRate;
615
+ await PluginManager_1.PluginManager.swapCreate(createdSwap);
616
+ await this.saveSwapData(createdSwap);
617
+ this.swapLogger.info(createdSwap, "REST: /payInvoiceExactIn: created exact in swap," +
618
+ " reqId: " + parsedBody.reqId +
619
+ " mtokens: " + parsedPR.mtokens.toString(10) +
620
+ " invoice: " + createdSwap.pr);
621
+ await responseStream.writeParamsAndEnd({
622
+ code: 20000,
623
+ msg: "Success",
624
+ data: {
625
+ maxFee: parsedAuth.quotedNetworkFeeInToken.toString(10),
626
+ swapFee: parsedAuth.swapFeeInToken.toString(10),
627
+ total: parsedAuth.total.toString(10),
628
+ confidence: halfConfidence ? parsedAuth.confidence / 2000000 : parsedAuth.confidence / 1000000,
629
+ address: signer.getAddress(),
630
+ routingFeeSats: parsedAuth.quotedNetworkFee.toString(10),
631
+ data: payObject.serialize(),
632
+ prefix: sigData.prefix,
633
+ timeout: sigData.timeout,
634
+ signature: sigData.signature
635
+ }
636
+ });
637
+ }));
638
+ restServer.use(this.path + "/payInvoice", (0, ServerParamDecoder_1.serverParamDecoder)(10 * 1000));
639
+ restServer.post(this.path + "/payInvoice", (0, Utils_1.expressHandlerWrapper)(async (req, res) => {
640
+ const metadata = { request: {}, times: {} };
641
+ const chainIdentifier = req.query.chain;
642
+ const { swapContract, signer, chainInterface } = this.getChain(chainIdentifier);
643
+ metadata.times.requestReceived = Date.now();
644
+ /**
645
+ *Sent initially:
646
+ * pr: string bolt11 lightning invoice
647
+ * maxFee: string maximum routing fee
648
+ * expiryTimestamp: string expiry timestamp of the to be created HTLC, determines how many LN paths can be considered
649
+ * token: string Desired token to use
650
+ * offerer: string Address of the caller
651
+ * exactIn: boolean Whether to do an exact in swap instead of exact out
652
+ * amount: string Input amount for exactIn swaps
653
+ *
654
+ *Sent later:
655
+ * feeRate: string Fee rate to use for the init signature
656
+ */
657
+ const parsedBody = await req.paramReader.getParams({
658
+ pr: SchemaVerifier_1.FieldTypeEnum.String,
659
+ maxFee: SchemaVerifier_1.FieldTypeEnum.BigInt,
660
+ expiryTimestamp: SchemaVerifier_1.FieldTypeEnum.BigInt,
661
+ token: (val) => val != null &&
662
+ typeof (val) === "string" &&
663
+ this.isTokenSupported(chainIdentifier, val) ? val : null,
664
+ offerer: (val) => val != null &&
665
+ typeof (val) === "string" &&
666
+ chainInterface.isValidAddress(val, true) ? val : null,
667
+ exactIn: SchemaVerifier_1.FieldTypeEnum.BooleanOptional,
668
+ amount: SchemaVerifier_1.FieldTypeEnum.BigIntOptional
669
+ });
670
+ if (parsedBody == null) {
671
+ throw {
672
+ code: 20100,
673
+ msg: "Invalid request body"
674
+ };
675
+ }
676
+ metadata.request = parsedBody;
677
+ const request = {
678
+ chainIdentifier,
679
+ raw: req,
680
+ parsed: parsedBody,
681
+ metadata
682
+ };
683
+ const useToken = parsedBody.token;
684
+ const responseStream = res.responseStream;
685
+ const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
686
+ //Check request params
687
+ this.checkTooManyInflightSwaps();
688
+ this.checkAmount(parsedBody.amount, parsedBody.exactIn);
689
+ this.checkMaxFee(parsedBody.maxFee);
690
+ this.checkExpiry(parsedBody.expiryTimestamp, currentTimestamp);
691
+ await this.checkVaultInitialized(chainIdentifier, parsedBody.token);
692
+ const { parsedPR, halfConfidence } = await this.checkPaymentRequest(chainIdentifier, parsedBody.pr);
693
+ const requestedAmount = {
694
+ input: !!parsedBody.exactIn,
695
+ amount: !!parsedBody.exactIn ? parsedBody.amount : (parsedPR.mtokens + 999n) / 1000n,
696
+ token: useToken
697
+ };
698
+ const fees = await this.AmountAssertions.preCheckToBtcAmounts(this.type, request, requestedAmount);
699
+ metadata.times.requestChecked = Date.now();
700
+ //Create abort controller for parallel pre-fetches
701
+ const abortController = (0, Utils_1.getAbortController)(responseStream);
702
+ //Pre-fetch
703
+ const { pricePrefetchPromise, signDataPrefetchPromise } = this.getToBtcPrefetches(chainIdentifier, useToken, responseStream, abortController);
704
+ const nativeBalancePrefetch = this.prefetchNativeBalanceIfNeeded(chainIdentifier, abortController);
705
+ //Check if prior payment has been made
706
+ await this.LightningAssertions.checkPriorPayment(parsedPR.id, abortController.signal);
707
+ metadata.times.priorPaymentChecked = Date.now();
708
+ //Check if we still have enough native balance
709
+ await this.checkNativeBalance(chainIdentifier, nativeBalancePrefetch, abortController.signal);
710
+ //Check amounts
711
+ const { amountBD, networkFeeData, totalInToken, swapFee, swapFeeInToken, networkFeeInToken } = await this.AmountAssertions.checkToBtcAmount(this.type, request, { ...requestedAmount, pricePrefetch: pricePrefetchPromise }, fees, async (amountBD) => {
712
+ //Check if we have enough liquidity to process the swap
713
+ await this.LightningAssertions.checkLiquidity(amountBD, abortController.signal, true);
714
+ metadata.times.liquidityChecked = Date.now();
715
+ const maxFee = parsedBody.exactIn ?
716
+ await this.swapPricing.getToBtcSwapAmount(parsedBody.maxFee, useToken, chainIdentifier, null, pricePrefetchPromise) :
717
+ parsedBody.maxFee;
718
+ return await this.checkAndGetNetworkFee(amountBD, maxFee, parsedBody.expiryTimestamp, currentTimestamp, parsedBody.pr, metadata, abortController.signal);
719
+ }, abortController.signal);
720
+ metadata.times.priceCalculated = Date.now();
721
+ //For exactIn swap, just save and wait for the actual invoice to be submitted
722
+ if (parsedBody.exactIn) {
723
+ const reqId = (0, crypto_1.randomBytes)(32).toString("hex");
724
+ this.exactInAuths[reqId] = {
725
+ chainIdentifier,
726
+ reqId,
727
+ expiry: Date.now() + this.config.exactInExpiry,
728
+ amount: amountBD,
729
+ initialInvoice: parsedPR,
730
+ quotedNetworkFeeInToken: networkFeeInToken,
731
+ swapFeeInToken,
732
+ total: totalInToken,
733
+ confidence: networkFeeData.confidence,
734
+ quotedNetworkFee: networkFeeData.networkFee,
735
+ swapFee,
736
+ token: useToken,
737
+ swapExpiry: parsedBody.expiryTimestamp,
738
+ offerer: parsedBody.offerer,
739
+ preFetchSignData: signDataPrefetchPromise != null ? await signDataPrefetchPromise : null,
740
+ metadata
741
+ };
742
+ this.logger.info("REST: /payInvoice: created exact in swap," +
743
+ " reqId: " + reqId +
744
+ " amount: " + amountBD.toString(10) +
745
+ " destination: " + parsedPR.destination);
746
+ await responseStream.writeParamsAndEnd({
747
+ code: 20000,
748
+ msg: "Success",
749
+ data: {
750
+ amount: amountBD.toString(10),
751
+ reqId
752
+ }
753
+ });
754
+ return;
755
+ }
756
+ const sequence = base_1.BigIntBufferUtils.fromBuffer((0, crypto_1.randomBytes)(8));
757
+ const claimHash = swapContract.getHashForHtlc(Buffer.from(parsedPR.id, "hex"));
758
+ //Create swap data
759
+ const payObject = await swapContract.createSwapData(base_1.ChainSwapType.HTLC, parsedBody.offerer, signer.getAddress(), useToken, totalInToken, claimHash.toString("hex"), sequence, parsedBody.expiryTimestamp, true, false, 0n, 0n);
760
+ abortController.signal.throwIfAborted();
761
+ metadata.times.swapCreated = Date.now();
762
+ //Sign swap data
763
+ const sigData = await this.getToBtcSignatureData(chainIdentifier, payObject, req, abortController.signal, signDataPrefetchPromise);
764
+ metadata.times.swapSigned = Date.now();
765
+ //Create swap
766
+ const createdSwap = new ToBtcLnSwapAbs_1.ToBtcLnSwapAbs(chainIdentifier, parsedPR.id, parsedBody.pr, parsedPR.mtokens, swapFee, swapFeeInToken, networkFeeData.networkFee, networkFeeInToken);
767
+ createdSwap.data = payObject;
768
+ createdSwap.metadata = metadata;
769
+ createdSwap.prefix = sigData.prefix;
770
+ createdSwap.timeout = sigData.timeout;
771
+ createdSwap.signature = sigData.signature;
772
+ createdSwap.feeRate = sigData.feeRate;
773
+ await PluginManager_1.PluginManager.swapCreate(createdSwap);
774
+ await this.saveSwapData(createdSwap);
775
+ this.swapLogger.info(createdSwap, "REST: /payInvoice: created swap," +
776
+ " amount: " + amountBD.toString(10) +
777
+ " invoice: " + createdSwap.pr);
778
+ await responseStream.writeParamsAndEnd({
779
+ code: 20000,
780
+ msg: "Success",
781
+ data: {
782
+ maxFee: networkFeeInToken.toString(10),
783
+ swapFee: swapFeeInToken.toString(10),
784
+ total: totalInToken.toString(10),
785
+ confidence: halfConfidence ? networkFeeData.confidence / 2000000 : networkFeeData.confidence / 1000000,
786
+ address: signer.getAddress(),
787
+ routingFeeSats: networkFeeData.networkFee.toString(10),
788
+ data: payObject.serialize(),
789
+ prefix: sigData.prefix,
790
+ timeout: sigData.timeout,
791
+ signature: sigData.signature
792
+ }
793
+ });
794
+ }));
795
+ const getRefundAuthorization = (0, Utils_1.expressHandlerWrapper)(async (req, res) => {
796
+ /**
797
+ * paymentHash: string Identifier of the swap
798
+ * sequence: BN Sequence identifier of the swap
799
+ */
800
+ const parsedBody = (0, SchemaVerifier_1.verifySchema)({ ...req.body, ...req.query }, {
801
+ paymentHash: (val) => val != null &&
802
+ typeof (val) === "string" &&
803
+ val.length === 64 &&
804
+ Utils_1.HEX_REGEX.test(val) ? val : null,
805
+ sequence: SchemaVerifier_1.FieldTypeEnum.BigInt
806
+ });
807
+ if (parsedBody == null)
808
+ throw {
809
+ code: 20100,
810
+ msg: "Invalid request body/query (paymentHash/sequence)"
811
+ };
812
+ this.checkSequence(parsedBody.sequence);
813
+ let data = await this.storageManager.getData(parsedBody.paymentHash, parsedBody.sequence);
814
+ if (data == null) {
815
+ for (let chainId in this.chains.chains) {
816
+ const _data = this.getSwapByEscrowHash(chainId, parsedBody.paymentHash);
817
+ if (_data != null && _data.getSequence() === parsedBody.sequence) {
818
+ data = _data;
819
+ break;
820
+ }
821
+ }
822
+ }
823
+ const isSwapFound = data != null;
824
+ if (isSwapFound) {
825
+ const { signer, swapContract } = this.getChain(data.chainIdentifier);
826
+ if (await swapContract.isExpired(signer.getAddress(), data.data))
827
+ throw {
828
+ _httpStatus: 200,
829
+ code: 20010,
830
+ msg: "Payment expired"
831
+ };
832
+ if (data.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE) {
833
+ const refundSigData = await swapContract.getRefundSignature(signer, data.data, this.config.refundAuthorizationTimeout);
834
+ //Double check the state after promise result
835
+ if (data.state !== ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE)
836
+ throw {
837
+ code: 20005,
838
+ msg: "Not committed"
839
+ };
840
+ this.swapLogger.info(data, "REST: /getRefundAuthorization: returning refund authorization, because invoice in NON_PAYABLE state, invoice: " + data.pr);
841
+ res.status(200).json({
842
+ code: 20000,
843
+ msg: "Success",
844
+ data: {
845
+ address: signer.getAddress(),
846
+ prefix: refundSigData.prefix,
847
+ timeout: refundSigData.timeout,
848
+ signature: refundSigData.signature
849
+ }
850
+ });
851
+ return;
852
+ }
853
+ }
854
+ const payment = await this.lightning.getPayment(parsedBody.paymentHash);
855
+ if (payment == null)
856
+ throw {
857
+ _httpStatus: 200,
858
+ code: 20007,
859
+ msg: "Payment not found"
860
+ };
861
+ if (payment.status === "pending") {
862
+ res.status(200).json({
863
+ code: 20008,
864
+ msg: "Payment in-flight"
865
+ });
866
+ return;
867
+ }
868
+ if (payment.status === "confirmed") {
869
+ res.status(200).json({
870
+ code: 20006,
871
+ msg: "Already paid",
872
+ data: {
873
+ secret: payment.secret
874
+ }
875
+ });
876
+ return;
877
+ }
878
+ if (payment.status === "failed")
879
+ throw {
880
+ _httpStatus: 200,
881
+ code: 20010,
882
+ msg: "Payment expired",
883
+ data: {
884
+ reason: payment.failedReason
885
+ }
886
+ };
887
+ });
888
+ restServer.post(this.path + '/getRefundAuthorization', getRefundAuthorization);
889
+ restServer.get(this.path + '/getRefundAuthorization', getRefundAuthorization);
890
+ this.logger.info("started at path: ", this.path);
891
+ }
892
+ async init() {
893
+ await this.loadData(ToBtcLnSwapAbs_1.ToBtcLnSwapAbs);
894
+ //Check if all swaps contain a valid amount
895
+ for (let { obj: swap } of await this.storageManager.query([])) {
896
+ if (swap.amount == null || swap.lnPaymentHash == null) {
897
+ const parsedPR = await this.lightning.parsePaymentRequest(swap.pr);
898
+ swap.amount = (parsedPR.mtokens + 999n) / 1000n;
899
+ swap.lnPaymentHash = parsedPR.id;
900
+ }
901
+ }
902
+ this.subscribeToEvents();
903
+ await PluginManager_1.PluginManager.serviceInitialize(this);
904
+ }
905
+ getInfoData() {
906
+ return {
907
+ minCltv: Number(this.config.minSendCltv),
908
+ minTimestampCltv: Number(this.config.minTsSendCltv)
909
+ };
910
+ }
911
+ }
912
+ exports.ToBtcLnAbs = ToBtcLnAbs;