@atomiqlabs/lp-lib 10.3.11

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 (138) hide show
  1. package/LICENSE +201 -0
  2. package/dist/fees/IBtcFeeEstimator.d.ts +3 -0
  3. package/dist/fees/IBtcFeeEstimator.js +2 -0
  4. package/dist/fees/OneDollarFeeEstimator.d.ts +16 -0
  5. package/dist/fees/OneDollarFeeEstimator.js +71 -0
  6. package/dist/index.d.ts +33 -0
  7. package/dist/index.js +52 -0
  8. package/dist/info/InfoHandler.d.ts +17 -0
  9. package/dist/info/InfoHandler.js +70 -0
  10. package/dist/plugins/IPlugin.d.ts +118 -0
  11. package/dist/plugins/IPlugin.js +33 -0
  12. package/dist/plugins/PluginManager.d.ts +89 -0
  13. package/dist/plugins/PluginManager.js +263 -0
  14. package/dist/prices/BinanceSwapPrice.d.ts +27 -0
  15. package/dist/prices/BinanceSwapPrice.js +106 -0
  16. package/dist/prices/CoinGeckoSwapPrice.d.ts +31 -0
  17. package/dist/prices/CoinGeckoSwapPrice.js +76 -0
  18. package/dist/storage/IIntermediaryStorage.d.ts +15 -0
  19. package/dist/storage/IIntermediaryStorage.js +2 -0
  20. package/dist/storagemanager/IntermediaryStorageManager.d.ts +15 -0
  21. package/dist/storagemanager/IntermediaryStorageManager.js +113 -0
  22. package/dist/storagemanager/StorageManager.d.ts +12 -0
  23. package/dist/storagemanager/StorageManager.js +74 -0
  24. package/dist/swaps/FromBtcBaseSwap.d.ts +12 -0
  25. package/dist/swaps/FromBtcBaseSwap.js +16 -0
  26. package/dist/swaps/FromBtcBaseSwapHandler.d.ts +118 -0
  27. package/dist/swaps/FromBtcBaseSwapHandler.js +294 -0
  28. package/dist/swaps/FromBtcLnBaseSwapHandler.d.ts +25 -0
  29. package/dist/swaps/FromBtcLnBaseSwapHandler.js +55 -0
  30. package/dist/swaps/ISwapPrice.d.ts +44 -0
  31. package/dist/swaps/ISwapPrice.js +73 -0
  32. package/dist/swaps/SwapHandler.d.ts +186 -0
  33. package/dist/swaps/SwapHandler.js +292 -0
  34. package/dist/swaps/SwapHandlerSwap.d.ts +75 -0
  35. package/dist/swaps/SwapHandlerSwap.js +72 -0
  36. package/dist/swaps/ToBtcBaseSwap.d.ts +35 -0
  37. package/dist/swaps/ToBtcBaseSwap.js +61 -0
  38. package/dist/swaps/ToBtcBaseSwapHandler.d.ts +94 -0
  39. package/dist/swaps/ToBtcBaseSwapHandler.js +233 -0
  40. package/dist/swaps/frombtc_abstract/FromBtcAbs.d.ts +92 -0
  41. package/dist/swaps/frombtc_abstract/FromBtcAbs.js +386 -0
  42. package/dist/swaps/frombtc_abstract/FromBtcSwapAbs.d.ts +26 -0
  43. package/dist/swaps/frombtc_abstract/FromBtcSwapAbs.js +63 -0
  44. package/dist/swaps/frombtc_trusted/FromBtcTrusted.d.ts +55 -0
  45. package/dist/swaps/frombtc_trusted/FromBtcTrusted.js +586 -0
  46. package/dist/swaps/frombtc_trusted/FromBtcTrustedSwap.d.ts +43 -0
  47. package/dist/swaps/frombtc_trusted/FromBtcTrustedSwap.js +99 -0
  48. package/dist/swaps/frombtcln_abstract/FromBtcLnAbs.d.ts +105 -0
  49. package/dist/swaps/frombtcln_abstract/FromBtcLnAbs.js +731 -0
  50. package/dist/swaps/frombtcln_abstract/FromBtcLnSwapAbs.d.ts +29 -0
  51. package/dist/swaps/frombtcln_abstract/FromBtcLnSwapAbs.js +64 -0
  52. package/dist/swaps/frombtcln_trusted/FromBtcLnTrusted.d.ts +79 -0
  53. package/dist/swaps/frombtcln_trusted/FromBtcLnTrusted.js +514 -0
  54. package/dist/swaps/frombtcln_trusted/FromBtcLnTrustedSwap.d.ts +28 -0
  55. package/dist/swaps/frombtcln_trusted/FromBtcLnTrustedSwap.js +66 -0
  56. package/dist/swaps/tobtc_abstract/ToBtcAbs.d.ts +290 -0
  57. package/dist/swaps/tobtc_abstract/ToBtcAbs.js +1056 -0
  58. package/dist/swaps/tobtc_abstract/ToBtcSwapAbs.d.ts +29 -0
  59. package/dist/swaps/tobtc_abstract/ToBtcSwapAbs.js +70 -0
  60. package/dist/swaps/tobtcln_abstract/ToBtcLnAbs.d.ts +246 -0
  61. package/dist/swaps/tobtcln_abstract/ToBtcLnAbs.js +1169 -0
  62. package/dist/swaps/tobtcln_abstract/ToBtcLnSwapAbs.d.ts +27 -0
  63. package/dist/swaps/tobtcln_abstract/ToBtcLnSwapAbs.js +65 -0
  64. package/dist/utils/Utils.d.ts +32 -0
  65. package/dist/utils/Utils.js +109 -0
  66. package/dist/utils/coinselect2/accumulative.d.ts +6 -0
  67. package/dist/utils/coinselect2/accumulative.js +44 -0
  68. package/dist/utils/coinselect2/blackjack.d.ts +6 -0
  69. package/dist/utils/coinselect2/blackjack.js +41 -0
  70. package/dist/utils/coinselect2/index.d.ts +16 -0
  71. package/dist/utils/coinselect2/index.js +40 -0
  72. package/dist/utils/coinselect2/utils.d.ts +64 -0
  73. package/dist/utils/coinselect2/utils.js +121 -0
  74. package/dist/utils/paramcoders/IParamReader.d.ts +5 -0
  75. package/dist/utils/paramcoders/IParamReader.js +2 -0
  76. package/dist/utils/paramcoders/IParamWriter.d.ts +4 -0
  77. package/dist/utils/paramcoders/IParamWriter.js +2 -0
  78. package/dist/utils/paramcoders/LegacyParamEncoder.d.ts +10 -0
  79. package/dist/utils/paramcoders/LegacyParamEncoder.js +33 -0
  80. package/dist/utils/paramcoders/ParamDecoder.d.ts +25 -0
  81. package/dist/utils/paramcoders/ParamDecoder.js +234 -0
  82. package/dist/utils/paramcoders/ParamEncoder.d.ts +9 -0
  83. package/dist/utils/paramcoders/ParamEncoder.js +22 -0
  84. package/dist/utils/paramcoders/SchemaVerifier.d.ts +22 -0
  85. package/dist/utils/paramcoders/SchemaVerifier.js +85 -0
  86. package/dist/utils/paramcoders/server/ServerParamDecoder.d.ts +8 -0
  87. package/dist/utils/paramcoders/server/ServerParamDecoder.js +105 -0
  88. package/dist/utils/paramcoders/server/ServerParamEncoder.d.ts +11 -0
  89. package/dist/utils/paramcoders/server/ServerParamEncoder.js +76 -0
  90. package/package.json +43 -0
  91. package/src/fees/IBtcFeeEstimator.ts +7 -0
  92. package/src/fees/OneDollarFeeEstimator.ts +95 -0
  93. package/src/index.ts +46 -0
  94. package/src/info/InfoHandler.ts +106 -0
  95. package/src/plugins/IPlugin.ts +155 -0
  96. package/src/plugins/PluginManager.ts +310 -0
  97. package/src/prices/BinanceSwapPrice.ts +114 -0
  98. package/src/prices/CoinGeckoSwapPrice.ts +88 -0
  99. package/src/storage/IIntermediaryStorage.ts +21 -0
  100. package/src/storagemanager/IntermediaryStorageManager.ts +101 -0
  101. package/src/storagemanager/StorageManager.ts +68 -0
  102. package/src/swaps/FromBtcBaseSwap.ts +21 -0
  103. package/src/swaps/FromBtcBaseSwapHandler.ts +375 -0
  104. package/src/swaps/FromBtcLnBaseSwapHandler.ts +48 -0
  105. package/src/swaps/ISwapPrice.ts +94 -0
  106. package/src/swaps/SwapHandler.ts +404 -0
  107. package/src/swaps/SwapHandlerSwap.ts +133 -0
  108. package/src/swaps/ToBtcBaseSwap.ts +76 -0
  109. package/src/swaps/ToBtcBaseSwapHandler.ts +309 -0
  110. package/src/swaps/frombtc_abstract/FromBtcAbs.ts +484 -0
  111. package/src/swaps/frombtc_abstract/FromBtcSwapAbs.ts +77 -0
  112. package/src/swaps/frombtc_trusted/FromBtcTrusted.ts +661 -0
  113. package/src/swaps/frombtc_trusted/FromBtcTrustedSwap.ts +158 -0
  114. package/src/swaps/frombtcln_abstract/FromBtcLnAbs.ts +864 -0
  115. package/src/swaps/frombtcln_abstract/FromBtcLnSwapAbs.ts +82 -0
  116. package/src/swaps/frombtcln_trusted/FromBtcLnTrusted.ts +592 -0
  117. package/src/swaps/frombtcln_trusted/FromBtcLnTrustedSwap.ts +90 -0
  118. package/src/swaps/tobtc_abstract/ToBtcAbs.ts +1249 -0
  119. package/src/swaps/tobtc_abstract/ToBtcSwapAbs.ts +112 -0
  120. package/src/swaps/tobtcln_abstract/ToBtcLnAbs.ts +1422 -0
  121. package/src/swaps/tobtcln_abstract/ToBtcLnSwapAbs.ts +87 -0
  122. package/src/utils/Utils.ts +108 -0
  123. package/src/utils/coinselect2/accumulative.js +32 -0
  124. package/src/utils/coinselect2/accumulative.ts +58 -0
  125. package/src/utils/coinselect2/blackjack.js +29 -0
  126. package/src/utils/coinselect2/blackjack.ts +54 -0
  127. package/src/utils/coinselect2/index.js +16 -0
  128. package/src/utils/coinselect2/index.ts +50 -0
  129. package/src/utils/coinselect2/utils.js +110 -0
  130. package/src/utils/coinselect2/utils.ts +183 -0
  131. package/src/utils/paramcoders/IParamReader.ts +8 -0
  132. package/src/utils/paramcoders/IParamWriter.ts +8 -0
  133. package/src/utils/paramcoders/LegacyParamEncoder.ts +28 -0
  134. package/src/utils/paramcoders/ParamDecoder.ts +219 -0
  135. package/src/utils/paramcoders/ParamEncoder.ts +30 -0
  136. package/src/utils/paramcoders/SchemaVerifier.ts +97 -0
  137. package/src/utils/paramcoders/server/ServerParamDecoder.ts +115 -0
  138. package/src/utils/paramcoders/server/ServerParamEncoder.ts +76 -0
@@ -0,0 +1,21 @@
1
+ import {SwapData} from "@atomiqlabs/base";
2
+ import {SwapHandlerSwap} from "./SwapHandlerSwap";
3
+ import * as BN from "bn.js";
4
+
5
+ export abstract class FromBtcBaseSwap<T extends SwapData, S = any> extends SwapHandlerSwap<T, S> {
6
+
7
+ getInputAmount(): BN {
8
+ return this.getTotalInputAmount().sub(this.getSwapFee().inInputToken);
9
+ }
10
+
11
+ abstract getTotalInputAmount(): BN;
12
+
13
+ getOutputAmount(): BN {
14
+ return this.data.getAmount();
15
+ }
16
+
17
+ getSwapFee(): { inInputToken: BN; inOutputToken: BN } {
18
+ return {inInputToken: this.swapFee, inOutputToken: this.swapFeeInToken};
19
+ }
20
+
21
+ }
@@ -0,0 +1,375 @@
1
+ import {SwapHandlerSwap} from "./SwapHandlerSwap";
2
+ import {SwapData} from "@atomiqlabs/base";
3
+ import {RequestData, SwapBaseConfig, SwapHandler} from "./SwapHandler";
4
+ import * as BN from "bn.js";
5
+ import {IParamReader} from "../utils/paramcoders/IParamReader";
6
+ import {FieldTypeEnum} from "../utils/paramcoders/SchemaVerifier";
7
+ import {FromBtcLnRequestType} from "./frombtcln_abstract/FromBtcLnAbs";
8
+ import {FromBtcRequestType} from "./frombtc_abstract/FromBtcAbs";
9
+ import {PluginManager} from "../plugins/PluginManager";
10
+ import {
11
+ isPluginQuote,
12
+ isQuoteSetFees
13
+ } from "../plugins/IPlugin";
14
+ import {Request} from "express";
15
+ import {FromBtcLnTrustedRequestType} from "./frombtcln_trusted/FromBtcLnTrusted";
16
+
17
+ const secondsInYear = new BN(365*24*60*60);
18
+
19
+ export type FromBtcBaseConfig = SwapBaseConfig & {
20
+ securityDepositAPY: number
21
+ };
22
+
23
+ export abstract class FromBtcBaseSwapHandler<V extends SwapHandlerSwap<SwapData, S>, S> extends SwapHandler<V, S> {
24
+
25
+ abstract config: FromBtcBaseConfig;
26
+
27
+ /**
28
+ * Starts a pre-fetch for swap price & security deposit price
29
+ *
30
+ * @param chainIdentifier
31
+ * @param useToken
32
+ * @param abortController
33
+ */
34
+ protected getFromBtcPricePrefetches(chainIdentifier: string, useToken: string, abortController: AbortController): {
35
+ pricePrefetchPromise: Promise<BN>,
36
+ securityDepositPricePrefetchPromise: Promise<BN>
37
+ } {
38
+ const pricePrefetchPromise: Promise<BN> = this.swapPricing.preFetchPrice(useToken, chainIdentifier).catch(e => {
39
+ this.logger.error("getFromBtcPricePrefetches(): pricePrefetch error: ", e);
40
+ abortController.abort(e);
41
+ return null;
42
+ });
43
+ const {swapContract} = this.getChain(chainIdentifier);
44
+ const securityDepositPricePrefetchPromise: Promise<BN> = useToken.toString()===swapContract.getNativeCurrencyAddress().toString() ?
45
+ pricePrefetchPromise :
46
+ this.swapPricing.preFetchPrice(swapContract.getNativeCurrencyAddress(), chainIdentifier).catch(e => {
47
+ this.logger.error("getFromBtcPricePrefetches(): securityDepositPricePrefetch error: ", e);
48
+ abortController.abort(e);
49
+ return null;
50
+ });
51
+
52
+ return {pricePrefetchPromise, securityDepositPricePrefetchPromise};
53
+ }
54
+
55
+ /**
56
+ * Starts a pre-fetch for base security deposit (transaction fee for refunding transaction on our side)
57
+ *
58
+ * @param chainIdentifier
59
+ * @param dummySwapData
60
+ * @param abortController
61
+ */
62
+ protected async getBaseSecurityDepositPrefetch(chainIdentifier: string, dummySwapData: SwapData, abortController: AbortController): Promise<BN> {
63
+ //Solana workaround
64
+ const {swapContract} = this.getChain(chainIdentifier);
65
+ if (swapContract.getRawRefundFee != null) {
66
+ try {
67
+ return await swapContract.getRawRefundFee(dummySwapData);
68
+ } catch (e) {
69
+ this.logger.error("getBaseSecurityDepositPrefetch(): pre-fetch error: ", e);
70
+ abortController.abort(e);
71
+ return null;
72
+ }
73
+ } else {
74
+ try {
75
+ const result = await swapContract.getRefundFee(dummySwapData);
76
+ return result.mul(new BN(2));
77
+ } catch (e1) {
78
+ this.logger.error("getBaseSecurityDepositPrefetch(): pre-fetch error: ", e1);
79
+ abortController.abort(e1);
80
+ return null;
81
+ }
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Starts a pre-fetch for vault balance
87
+ *
88
+ * @param chainIdentifier
89
+ * @param useToken
90
+ * @param abortController
91
+ */
92
+ protected async getBalancePrefetch(chainIdentifier: string, useToken: string, abortController: AbortController): Promise<BN> {
93
+ const {swapContract, signer} = this.getChain(chainIdentifier);
94
+ try {
95
+ return await swapContract.getBalance(signer.getAddress(), useToken, true);
96
+ } catch (e) {
97
+ this.logger.error("getBalancePrefetch(): balancePrefetch error: ", e);
98
+ abortController.abort(e);
99
+ return null;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Checks if we have enough balance of the token in the swap vault
105
+ *
106
+ * @param totalInToken
107
+ * @param balancePrefetch
108
+ * @param signal
109
+ * @throws {DefinedRuntimeError} will throw an error if there are not enough funds in the vault
110
+ */
111
+ protected async checkBalance(totalInToken: BN, balancePrefetch: Promise<BN>, signal: AbortSignal | null): Promise<void> {
112
+ const balance = await balancePrefetch;
113
+ if(signal!=null) signal.throwIfAborted();
114
+
115
+ if(balance==null || balance.lt(totalInToken)) {
116
+ throw {
117
+ code: 20002,
118
+ msg: "Not enough liquidity"
119
+ };
120
+ }
121
+ }
122
+
123
+
124
+ /**
125
+ * Checks minimums/maximums, calculates the fee & total amount
126
+ *
127
+ * @param request
128
+ * @param requestedAmount
129
+ * @param useToken
130
+ * @throws {DefinedRuntimeError} will throw an error if the amount is outside minimum/maximum bounds
131
+ */
132
+ protected async preCheckAmounts(
133
+ request: RequestData<FromBtcLnRequestType | FromBtcRequestType | FromBtcLnTrustedRequestType>,
134
+ requestedAmount: {input: boolean, amount: BN},
135
+ useToken: string
136
+ ): Promise<{baseFee: BN, feePPM: BN}> {
137
+ const res = await PluginManager.onHandlePreFromBtcQuote(
138
+ request,
139
+ requestedAmount,
140
+ request.chainIdentifier,
141
+ useToken,
142
+ {minInBtc: this.config.min, maxInBtc: this.config.max},
143
+ {baseFeeInBtc: this.config.baseFee, feePPM: this.config.feePPM},
144
+ );
145
+ if(res!=null) {
146
+ this.handlePluginErrorResponses(res);
147
+ if(isQuoteSetFees(res)) {
148
+ return {
149
+ baseFee: res.baseFee || this.config.baseFee,
150
+ feePPM: res.feePPM || this.config.feePPM
151
+ }
152
+ }
153
+ }
154
+ if(requestedAmount.input) this.checkBtcAmountInBounds(requestedAmount.amount);
155
+ return {
156
+ baseFee: this.config.baseFee,
157
+ feePPM: this.config.feePPM
158
+ };
159
+ }
160
+
161
+ /**
162
+ * Checks minimums/maximums, calculates the fee & total amount
163
+ *
164
+ * @param request
165
+ * @param requestedAmount
166
+ * @param fees
167
+ * @param useToken
168
+ * @param signal
169
+ * @param pricePrefetchPromise
170
+ * @throws {DefinedRuntimeError} will throw an error if the amount is outside minimum/maximum bounds
171
+ */
172
+ protected async checkFromBtcAmount(
173
+ request: RequestData<FromBtcLnRequestType | FromBtcRequestType | FromBtcLnTrustedRequestType>,
174
+ requestedAmount: {input: boolean, amount: BN},
175
+ fees: {baseFee: BN, feePPM: BN},
176
+ useToken: string,
177
+ signal: AbortSignal,
178
+ pricePrefetchPromise: Promise<BN> = Promise.resolve(null)
179
+ ): Promise<{
180
+ amountBD: BN,
181
+ swapFee: BN, //Swap fee in BTC
182
+ swapFeeInToken: BN, //Swap fee in token on top of what should be paid out to the user
183
+ totalInToken: BN //Total to be paid out to the user
184
+ }> {
185
+ const chainIdentifier = request.chainIdentifier;
186
+
187
+ const res = await PluginManager.onHandlePostFromBtcQuote(
188
+ request,
189
+ requestedAmount,
190
+ chainIdentifier,
191
+ useToken,
192
+ {minInBtc: this.config.min, maxInBtc: this.config.max},
193
+ {baseFeeInBtc: fees.baseFee, feePPM: fees.feePPM},
194
+ pricePrefetchPromise
195
+ );
196
+ signal.throwIfAborted();
197
+ if(res!=null) {
198
+ this.handlePluginErrorResponses(res);
199
+ if(isQuoteSetFees(res)) {
200
+ if(res.baseFee!=null) fees.baseFee = res.baseFee;
201
+ if(res.feePPM!=null) fees.feePPM = res.feePPM;
202
+ }
203
+ if(isPluginQuote(res)) {
204
+ if(!requestedAmount.input) {
205
+ return {
206
+ amountBD: res.amount.amount.add(res.swapFee.inInputTokens),
207
+ swapFee: res.swapFee.inInputTokens,
208
+ swapFeeInToken: res.swapFee.inOutputTokens,
209
+ totalInToken: requestedAmount.amount
210
+ }
211
+ } else {
212
+ return {
213
+ amountBD: requestedAmount.amount,
214
+ swapFee: res.swapFee.inInputTokens,
215
+ swapFeeInToken: res.swapFee.inOutputTokens,
216
+ totalInToken: res.amount.amount
217
+ }
218
+ }
219
+ }
220
+ }
221
+
222
+ let amountBD: BN;
223
+ if(!requestedAmount.input) {
224
+ amountBD = await this.swapPricing.getToBtcSwapAmount(requestedAmount.amount, useToken, chainIdentifier, true, pricePrefetchPromise);
225
+ signal.throwIfAborted();
226
+
227
+ // amt = (amt+base_fee)/(1-fee)
228
+ amountBD = amountBD.add(fees.baseFee).mul(new BN(1000000)).div(new BN(1000000).sub(fees.feePPM));
229
+
230
+ const tooLow = amountBD.lt(this.config.min.mul(new BN(95)).div(new BN(100)));
231
+ const tooHigh = amountBD.gt(this.config.max.mul(new BN(105)).div(new BN(100)));
232
+ if(tooLow || tooHigh) {
233
+ const adjustedMin = this.config.min.mul(new BN(1000000).sub(fees.feePPM)).div(new BN(1000000)).sub(fees.baseFee);
234
+ const adjustedMax = this.config.max.mul(new BN(1000000).sub(fees.feePPM)).div(new BN(1000000)).sub(fees.baseFee);
235
+ const minIn = await this.swapPricing.getFromBtcSwapAmount(
236
+ adjustedMin, useToken, chainIdentifier, null, pricePrefetchPromise
237
+ );
238
+ const maxIn = await this.swapPricing.getFromBtcSwapAmount(
239
+ adjustedMax, useToken, chainIdentifier, null, pricePrefetchPromise
240
+ );
241
+ throw {
242
+ code: tooLow ? 20003 : 20004,
243
+ msg: tooLow ? "Amount too low!" : "Amount too high!",
244
+ data: {
245
+ min: minIn.toString(10),
246
+ max: maxIn.toString(10)
247
+ }
248
+ };
249
+ }
250
+ } else {
251
+ amountBD = requestedAmount.amount;
252
+ this.checkBtcAmountInBounds(amountBD);
253
+ }
254
+
255
+ const swapFee = fees.baseFee.add(amountBD.mul(fees.feePPM).div(new BN(1000000)));
256
+ const swapFeeInToken = await this.swapPricing.getFromBtcSwapAmount(swapFee, useToken, chainIdentifier, true, pricePrefetchPromise);
257
+ signal.throwIfAborted();
258
+
259
+ let totalInToken: BN;
260
+ if(!requestedAmount.input) {
261
+ totalInToken = requestedAmount.amount;
262
+ } else {
263
+ const amountInToken = await this.swapPricing.getFromBtcSwapAmount(requestedAmount.amount, useToken, chainIdentifier, null, pricePrefetchPromise);
264
+ totalInToken = amountInToken.sub(swapFeeInToken);
265
+ signal.throwIfAborted();
266
+ }
267
+
268
+ return {
269
+ amountBD,
270
+ swapFee,
271
+ swapFeeInToken,
272
+ totalInToken
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Calculates the required security deposit
278
+ *
279
+ * @param chainIdentifier
280
+ * @param amountBD
281
+ * @param swapFee
282
+ * @param expiryTimeout
283
+ * @param baseSecurityDepositPromise
284
+ * @param securityDepositPricePrefetchPromise
285
+ * @param signal
286
+ * @param metadata
287
+ */
288
+ protected async getSecurityDeposit(
289
+ chainIdentifier: string,
290
+ amountBD: BN,
291
+ swapFee: BN,
292
+ expiryTimeout: BN,
293
+ baseSecurityDepositPromise: Promise<BN>,
294
+ securityDepositPricePrefetchPromise: Promise<BN>,
295
+ signal: AbortSignal,
296
+ metadata: any
297
+ ): Promise<BN> {
298
+ let baseSD: BN = await baseSecurityDepositPromise;
299
+
300
+ signal.throwIfAborted();
301
+
302
+ metadata.times.refundFeeFetched = Date.now();
303
+
304
+ const {swapContract} = this.getChain(chainIdentifier);
305
+
306
+ const swapValueInNativeCurrency = await this.swapPricing.getFromBtcSwapAmount(
307
+ amountBD.sub(swapFee),
308
+ swapContract.getNativeCurrencyAddress(),
309
+ chainIdentifier,
310
+ true,
311
+ securityDepositPricePrefetchPromise
312
+ );
313
+
314
+ signal.throwIfAborted();
315
+
316
+ const apyPPM = new BN(Math.floor(this.config.securityDepositAPY*1000000));
317
+ const variableSD = swapValueInNativeCurrency.mul(apyPPM).mul(expiryTimeout).div(new BN(1000000)).div(secondsInYear);
318
+
319
+ this.logger.debug(
320
+ "getSecurityDeposit(): base security deposit: "+baseSD.toString(10)+
321
+ " swap output in native: "+swapValueInNativeCurrency.toString(10)+
322
+ " apy ppm: "+apyPPM.toString(10)+
323
+ " expiry timeout: "+expiryTimeout.toString(10)+
324
+ " variable security deposit: "+variableSD.toString(10)
325
+ );
326
+
327
+ return baseSD.add(variableSD);
328
+ }
329
+
330
+ /**
331
+ * Signs the created swap
332
+ *
333
+ * @param chainIdentifier
334
+ * @param swapObject
335
+ * @param req
336
+ * @param abortSignal
337
+ * @param signDataPrefetchPromise
338
+ */
339
+ protected async getFromBtcSignatureData(
340
+ chainIdentifier: string,
341
+ swapObject: SwapData,
342
+ req: Request & {paramReader: IParamReader},
343
+ abortSignal: AbortSignal,
344
+ signDataPrefetchPromise?: Promise<any>
345
+ ): Promise<{
346
+ prefix: string,
347
+ timeout: string,
348
+ signature: string
349
+ }> {
350
+ const {swapContract, signer} = this.getChain(chainIdentifier);
351
+
352
+ const prefetchedSignData = signDataPrefetchPromise!=null ? await signDataPrefetchPromise : null;
353
+ if(prefetchedSignData!=null) this.logger.debug("getFromBtcSignatureData(): pre-fetched signature data: ", prefetchedSignData);
354
+ abortSignal.throwIfAborted();
355
+
356
+ const feeRateObj = await req.paramReader.getParams({
357
+ feeRate: FieldTypeEnum.String
358
+ }).catch(() => null);
359
+ abortSignal.throwIfAborted();
360
+
361
+ const feeRate = feeRateObj?.feeRate!=null && typeof(feeRateObj.feeRate)==="string" ? feeRateObj.feeRate : null;
362
+ this.logger.debug("getFromBtcSignatureData(): using fee rate from client: ", feeRate);
363
+ const sigData = await swapContract.getInitSignature(
364
+ signer,
365
+ swapObject,
366
+ this.config.authorizationTimeout,
367
+ prefetchedSignData,
368
+ feeRate
369
+ );
370
+ abortSignal.throwIfAborted();
371
+
372
+ return sigData;
373
+ }
374
+
375
+ }
@@ -0,0 +1,48 @@
1
+ import {SwapHandlerSwap} from "./SwapHandlerSwap";
2
+ import {SwapData} from "@atomiqlabs/base";
3
+ import {FromBtcBaseSwapHandler} from "./FromBtcBaseSwapHandler";
4
+ import * as BN from "bn.js";
5
+ import * as lncli from "ln-service";
6
+
7
+
8
+ export abstract class FromBtcLnBaseSwapHandler<V extends SwapHandlerSwap<SwapData, S>, S> extends FromBtcBaseSwapHandler<V, S> {
9
+
10
+ /**
11
+ * Checks if we have enough inbound liquidity to be able to receive an LN payment (without MPP)
12
+ *
13
+ * @param amountBD
14
+ * @param channelsPrefetch
15
+ * @param signal
16
+ * @throws {DefinedRuntimeError} will throw an error if there isn't enough inbound liquidity to receive the LN payment
17
+ */
18
+ protected async checkInboundLiquidity(amountBD: BN, channelsPrefetch: Promise<{channels: any[]}>, signal: AbortSignal) {
19
+ const channelsResponse = await channelsPrefetch;
20
+
21
+ signal.throwIfAborted();
22
+
23
+ let hasEnoughInboundLiquidity = false;
24
+ channelsResponse.channels.forEach(channel => {
25
+ if(new BN(channel.remote_balance).gte(amountBD)) hasEnoughInboundLiquidity = true;
26
+ });
27
+ if(!hasEnoughInboundLiquidity) {
28
+ throw {
29
+ code: 20050,
30
+ msg: "Not enough LN inbound liquidity"
31
+ };
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Starts LN channels pre-fetch
37
+ *
38
+ * @param abortController
39
+ */
40
+ protected getChannelsPrefetch(abortController: AbortController): Promise<{channels: any[]}> {
41
+ return lncli.getChannels({is_active: true, lnd: this.LND}).catch(e => {
42
+ this.logger.error("getChannelsPrefetch(): error", e);
43
+ abortController.abort(e);
44
+ return null;
45
+ });
46
+ }
47
+
48
+ }
@@ -0,0 +1,94 @@
1
+ import * as BN from "bn.js";
2
+
3
+ export type ISwapPriceCoinsMap<T extends {decimals: number}> = {
4
+ [chainId: string]: {
5
+ [address: string]: T
6
+ }
7
+ };
8
+
9
+ export abstract class ISwapPrice<T extends {decimals: number} = {decimals: number}> {
10
+
11
+ protected coinsMap: ISwapPriceCoinsMap<T>;
12
+
13
+ protected constructor(coinsMap: ISwapPriceCoinsMap<T>) {
14
+ this.coinsMap = coinsMap;
15
+ }
16
+
17
+ /**
18
+ * Returns coin price in mSat
19
+ *
20
+ * @param tokenData
21
+ */
22
+ protected abstract getPrice(tokenData: T): Promise<BN>;
23
+
24
+ getTokenData(tokenAddress: string, chainId: string): T {
25
+ const chainTokens = this.coinsMap[chainId];
26
+ if(chainTokens==null) throw new Error("Chain not found");
27
+
28
+ const tokenAddr: string = tokenAddress.toString();
29
+ const coin: T = chainTokens[tokenAddr];
30
+ if(coin==null) throw new Error("Token not found");
31
+
32
+ return coin;
33
+ }
34
+
35
+ preFetchPrice(token: string, chainId: string): Promise<BN> {
36
+ const coin = this.getTokenData(token, chainId);
37
+ return this.getPrice(coin);
38
+ }
39
+
40
+ /**
41
+ * Returns amount of satoshis that are equivalent to {fromAmount} of {fromToken}
42
+ *
43
+ * @param fromAmount Amount of the token
44
+ * @param fromToken Token
45
+ * @param tokenChainIdentification Chain of the token
46
+ * @param roundUp Whether result should be rounded up
47
+ * @param preFetch Price pre-fetch promise returned from preFetchPrice()
48
+ */
49
+ async getToBtcSwapAmount(
50
+ fromAmount:BN,
51
+ fromToken: string,
52
+ tokenChainIdentification: string,
53
+ roundUp?: boolean,
54
+ preFetch?: Promise<BN>
55
+ ): Promise<BN> {
56
+ const coin = this.getTokenData(fromToken, tokenChainIdentification);
57
+
58
+ const price = (preFetch==null ? null : await preFetch) || await this.getPrice(coin);
59
+
60
+ return fromAmount
61
+ .mul(price)
62
+ .div(new BN(10).pow(new BN(coin.decimals)))
63
+ .add(roundUp ? new BN(999999) : new BN(0))
64
+ .div(new BN(1000000));
65
+ }
66
+
67
+ /**
68
+ * Returns amount of {toToken} that are equivalent to {fromAmount} satoshis
69
+ *
70
+ * @param fromAmount Amount of satoshis
71
+ * @param toToken Token
72
+ * @param tokenChainIdentification Chain of the token
73
+ * @param roundUp Whether result should be rounded up
74
+ * @param preFetch Price pre-fetch promise returned from preFetchPrice()
75
+ */
76
+ async getFromBtcSwapAmount(
77
+ fromAmount:BN,
78
+ toToken: string,
79
+ tokenChainIdentification: string,
80
+ roundUp?: boolean,
81
+ preFetch?: Promise<BN>
82
+ ): Promise<BN> {
83
+ const coin = this.getTokenData(toToken, tokenChainIdentification);
84
+
85
+ const price = (preFetch==null ? null : await preFetch) || await this.getPrice(coin);
86
+
87
+ return fromAmount
88
+ .mul(new BN(10).pow(new BN(coin.decimals)))
89
+ .mul(new BN(1000000)) //To usat
90
+ .add(roundUp ? price.sub(new BN(1)) : new BN(0))
91
+ .div(price)
92
+ }
93
+
94
+ }