@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.
- package/LICENSE +201 -0
- package/dist/fees/IBtcFeeEstimator.d.ts +3 -0
- package/dist/fees/IBtcFeeEstimator.js +2 -0
- package/dist/fees/OneDollarFeeEstimator.d.ts +16 -0
- package/dist/fees/OneDollarFeeEstimator.js +71 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +52 -0
- package/dist/info/InfoHandler.d.ts +17 -0
- package/dist/info/InfoHandler.js +70 -0
- package/dist/plugins/IPlugin.d.ts +118 -0
- package/dist/plugins/IPlugin.js +33 -0
- package/dist/plugins/PluginManager.d.ts +89 -0
- package/dist/plugins/PluginManager.js +263 -0
- package/dist/prices/BinanceSwapPrice.d.ts +27 -0
- package/dist/prices/BinanceSwapPrice.js +106 -0
- package/dist/prices/CoinGeckoSwapPrice.d.ts +31 -0
- package/dist/prices/CoinGeckoSwapPrice.js +76 -0
- package/dist/storage/IIntermediaryStorage.d.ts +15 -0
- package/dist/storage/IIntermediaryStorage.js +2 -0
- package/dist/storagemanager/IntermediaryStorageManager.d.ts +15 -0
- package/dist/storagemanager/IntermediaryStorageManager.js +113 -0
- package/dist/storagemanager/StorageManager.d.ts +12 -0
- package/dist/storagemanager/StorageManager.js +74 -0
- package/dist/swaps/FromBtcBaseSwap.d.ts +12 -0
- package/dist/swaps/FromBtcBaseSwap.js +16 -0
- package/dist/swaps/FromBtcBaseSwapHandler.d.ts +118 -0
- package/dist/swaps/FromBtcBaseSwapHandler.js +294 -0
- package/dist/swaps/FromBtcLnBaseSwapHandler.d.ts +25 -0
- package/dist/swaps/FromBtcLnBaseSwapHandler.js +55 -0
- package/dist/swaps/ISwapPrice.d.ts +44 -0
- package/dist/swaps/ISwapPrice.js +73 -0
- package/dist/swaps/SwapHandler.d.ts +186 -0
- package/dist/swaps/SwapHandler.js +292 -0
- package/dist/swaps/SwapHandlerSwap.d.ts +75 -0
- package/dist/swaps/SwapHandlerSwap.js +72 -0
- package/dist/swaps/ToBtcBaseSwap.d.ts +35 -0
- package/dist/swaps/ToBtcBaseSwap.js +61 -0
- package/dist/swaps/ToBtcBaseSwapHandler.d.ts +94 -0
- package/dist/swaps/ToBtcBaseSwapHandler.js +233 -0
- package/dist/swaps/frombtc_abstract/FromBtcAbs.d.ts +92 -0
- package/dist/swaps/frombtc_abstract/FromBtcAbs.js +386 -0
- package/dist/swaps/frombtc_abstract/FromBtcSwapAbs.d.ts +26 -0
- package/dist/swaps/frombtc_abstract/FromBtcSwapAbs.js +63 -0
- package/dist/swaps/frombtc_trusted/FromBtcTrusted.d.ts +55 -0
- package/dist/swaps/frombtc_trusted/FromBtcTrusted.js +586 -0
- package/dist/swaps/frombtc_trusted/FromBtcTrustedSwap.d.ts +43 -0
- package/dist/swaps/frombtc_trusted/FromBtcTrustedSwap.js +99 -0
- package/dist/swaps/frombtcln_abstract/FromBtcLnAbs.d.ts +105 -0
- package/dist/swaps/frombtcln_abstract/FromBtcLnAbs.js +731 -0
- package/dist/swaps/frombtcln_abstract/FromBtcLnSwapAbs.d.ts +29 -0
- package/dist/swaps/frombtcln_abstract/FromBtcLnSwapAbs.js +64 -0
- package/dist/swaps/frombtcln_trusted/FromBtcLnTrusted.d.ts +79 -0
- package/dist/swaps/frombtcln_trusted/FromBtcLnTrusted.js +514 -0
- package/dist/swaps/frombtcln_trusted/FromBtcLnTrustedSwap.d.ts +28 -0
- package/dist/swaps/frombtcln_trusted/FromBtcLnTrustedSwap.js +66 -0
- package/dist/swaps/tobtc_abstract/ToBtcAbs.d.ts +290 -0
- package/dist/swaps/tobtc_abstract/ToBtcAbs.js +1056 -0
- package/dist/swaps/tobtc_abstract/ToBtcSwapAbs.d.ts +29 -0
- package/dist/swaps/tobtc_abstract/ToBtcSwapAbs.js +70 -0
- package/dist/swaps/tobtcln_abstract/ToBtcLnAbs.d.ts +246 -0
- package/dist/swaps/tobtcln_abstract/ToBtcLnAbs.js +1169 -0
- package/dist/swaps/tobtcln_abstract/ToBtcLnSwapAbs.d.ts +27 -0
- package/dist/swaps/tobtcln_abstract/ToBtcLnSwapAbs.js +65 -0
- package/dist/utils/Utils.d.ts +32 -0
- package/dist/utils/Utils.js +109 -0
- package/dist/utils/coinselect2/accumulative.d.ts +6 -0
- package/dist/utils/coinselect2/accumulative.js +44 -0
- package/dist/utils/coinselect2/blackjack.d.ts +6 -0
- package/dist/utils/coinselect2/blackjack.js +41 -0
- package/dist/utils/coinselect2/index.d.ts +16 -0
- package/dist/utils/coinselect2/index.js +40 -0
- package/dist/utils/coinselect2/utils.d.ts +64 -0
- package/dist/utils/coinselect2/utils.js +121 -0
- package/dist/utils/paramcoders/IParamReader.d.ts +5 -0
- package/dist/utils/paramcoders/IParamReader.js +2 -0
- package/dist/utils/paramcoders/IParamWriter.d.ts +4 -0
- package/dist/utils/paramcoders/IParamWriter.js +2 -0
- package/dist/utils/paramcoders/LegacyParamEncoder.d.ts +10 -0
- package/dist/utils/paramcoders/LegacyParamEncoder.js +33 -0
- package/dist/utils/paramcoders/ParamDecoder.d.ts +25 -0
- package/dist/utils/paramcoders/ParamDecoder.js +234 -0
- package/dist/utils/paramcoders/ParamEncoder.d.ts +9 -0
- package/dist/utils/paramcoders/ParamEncoder.js +22 -0
- package/dist/utils/paramcoders/SchemaVerifier.d.ts +22 -0
- package/dist/utils/paramcoders/SchemaVerifier.js +85 -0
- package/dist/utils/paramcoders/server/ServerParamDecoder.d.ts +8 -0
- package/dist/utils/paramcoders/server/ServerParamDecoder.js +105 -0
- package/dist/utils/paramcoders/server/ServerParamEncoder.d.ts +11 -0
- package/dist/utils/paramcoders/server/ServerParamEncoder.js +76 -0
- package/package.json +43 -0
- package/src/fees/IBtcFeeEstimator.ts +7 -0
- package/src/fees/OneDollarFeeEstimator.ts +95 -0
- package/src/index.ts +46 -0
- package/src/info/InfoHandler.ts +106 -0
- package/src/plugins/IPlugin.ts +155 -0
- package/src/plugins/PluginManager.ts +310 -0
- package/src/prices/BinanceSwapPrice.ts +114 -0
- package/src/prices/CoinGeckoSwapPrice.ts +88 -0
- package/src/storage/IIntermediaryStorage.ts +21 -0
- package/src/storagemanager/IntermediaryStorageManager.ts +101 -0
- package/src/storagemanager/StorageManager.ts +68 -0
- package/src/swaps/FromBtcBaseSwap.ts +21 -0
- package/src/swaps/FromBtcBaseSwapHandler.ts +375 -0
- package/src/swaps/FromBtcLnBaseSwapHandler.ts +48 -0
- package/src/swaps/ISwapPrice.ts +94 -0
- package/src/swaps/SwapHandler.ts +404 -0
- package/src/swaps/SwapHandlerSwap.ts +133 -0
- package/src/swaps/ToBtcBaseSwap.ts +76 -0
- package/src/swaps/ToBtcBaseSwapHandler.ts +309 -0
- package/src/swaps/frombtc_abstract/FromBtcAbs.ts +484 -0
- package/src/swaps/frombtc_abstract/FromBtcSwapAbs.ts +77 -0
- package/src/swaps/frombtc_trusted/FromBtcTrusted.ts +661 -0
- package/src/swaps/frombtc_trusted/FromBtcTrustedSwap.ts +158 -0
- package/src/swaps/frombtcln_abstract/FromBtcLnAbs.ts +864 -0
- package/src/swaps/frombtcln_abstract/FromBtcLnSwapAbs.ts +82 -0
- package/src/swaps/frombtcln_trusted/FromBtcLnTrusted.ts +592 -0
- package/src/swaps/frombtcln_trusted/FromBtcLnTrustedSwap.ts +90 -0
- package/src/swaps/tobtc_abstract/ToBtcAbs.ts +1249 -0
- package/src/swaps/tobtc_abstract/ToBtcSwapAbs.ts +112 -0
- package/src/swaps/tobtcln_abstract/ToBtcLnAbs.ts +1422 -0
- package/src/swaps/tobtcln_abstract/ToBtcLnSwapAbs.ts +87 -0
- package/src/utils/Utils.ts +108 -0
- package/src/utils/coinselect2/accumulative.js +32 -0
- package/src/utils/coinselect2/accumulative.ts +58 -0
- package/src/utils/coinselect2/blackjack.js +29 -0
- package/src/utils/coinselect2/blackjack.ts +54 -0
- package/src/utils/coinselect2/index.js +16 -0
- package/src/utils/coinselect2/index.ts +50 -0
- package/src/utils/coinselect2/utils.js +110 -0
- package/src/utils/coinselect2/utils.ts +183 -0
- package/src/utils/paramcoders/IParamReader.ts +8 -0
- package/src/utils/paramcoders/IParamWriter.ts +8 -0
- package/src/utils/paramcoders/LegacyParamEncoder.ts +28 -0
- package/src/utils/paramcoders/ParamDecoder.ts +219 -0
- package/src/utils/paramcoders/ParamEncoder.ts +30 -0
- package/src/utils/paramcoders/SchemaVerifier.ts +97 -0
- package/src/utils/paramcoders/server/ServerParamDecoder.ts +115 -0
- 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
|
+
}
|