@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,310 @@
|
|
|
1
|
+
import {BitcoinRpc, SwapData} from "@atomiqlabs/base";
|
|
2
|
+
import {
|
|
3
|
+
IPlugin, isPluginQuote, isQuoteAmountTooHigh, isQuoteAmountTooLow, isQuoteSetFees,
|
|
4
|
+
isQuoteThrow, isToBtcPluginQuote, PluginQuote,
|
|
5
|
+
QuoteAmountTooHigh,
|
|
6
|
+
QuoteAmountTooLow,
|
|
7
|
+
QuoteSetFees,
|
|
8
|
+
QuoteThrow, ToBtcPluginQuote
|
|
9
|
+
} from "./IPlugin";
|
|
10
|
+
import {
|
|
11
|
+
FromBtcLnRequestType,
|
|
12
|
+
FromBtcRequestType,
|
|
13
|
+
ISwapPrice, MultichainData, RequestData,
|
|
14
|
+
SwapHandler,
|
|
15
|
+
ToBtcLnRequestType,
|
|
16
|
+
ToBtcRequestType
|
|
17
|
+
} from "..";
|
|
18
|
+
import {SwapHandlerSwap} from "../swaps/SwapHandlerSwap";
|
|
19
|
+
import {AuthenticatedLnd} from "lightning";
|
|
20
|
+
import * as BN from "bn.js";
|
|
21
|
+
import * as fs from "fs";
|
|
22
|
+
import {getLogger} from "../utils/Utils";
|
|
23
|
+
import {FromBtcLnTrustedRequestType} from "../swaps/frombtcln_trusted/FromBtcLnTrusted";
|
|
24
|
+
|
|
25
|
+
export type FailSwapResponse = {
|
|
26
|
+
type: "fail",
|
|
27
|
+
code?: number,
|
|
28
|
+
msg?: string
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type FeeSwapResponse = {
|
|
32
|
+
type: "fee",
|
|
33
|
+
baseFee: BN,
|
|
34
|
+
feePPM: BN
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type AmountAndFeeSwapResponse = {
|
|
38
|
+
type: "amountAndFee",
|
|
39
|
+
baseFee?: BN,
|
|
40
|
+
feePPM?: BN,
|
|
41
|
+
amount: BN
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type SwapResponse = FailSwapResponse | FeeSwapResponse | AmountAndFeeSwapResponse;
|
|
45
|
+
|
|
46
|
+
const logger = getLogger("PluginManager: ");
|
|
47
|
+
const pluginLogger = {
|
|
48
|
+
debug: (plugin: IPlugin, msg, ...args) => logger.debug(plugin.name+": "+msg, ...args),
|
|
49
|
+
info: (plugin: IPlugin, msg, ...args) => logger.info(plugin.name+": "+msg, ...args),
|
|
50
|
+
warn: (plugin: IPlugin, msg, ...args) => logger.warn(plugin.name+": "+msg, ...args),
|
|
51
|
+
error: (plugin: IPlugin, msg, ...args) => logger.error(plugin.name+": "+msg, ...args)
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export class PluginManager {
|
|
55
|
+
|
|
56
|
+
static plugins: Map<string, IPlugin> = new Map();
|
|
57
|
+
|
|
58
|
+
static registerPlugin(name: string, plugin: IPlugin) {
|
|
59
|
+
PluginManager.plugins.set(name, plugin);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static unregisterPlugin(name: string): boolean {
|
|
63
|
+
return PluginManager.plugins.delete(name);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static async enable<T extends SwapData>(
|
|
67
|
+
chainsData: MultichainData,
|
|
68
|
+
|
|
69
|
+
bitcoinRpc: BitcoinRpc<any>,
|
|
70
|
+
lnd: AuthenticatedLnd,
|
|
71
|
+
|
|
72
|
+
swapPricing: ISwapPrice,
|
|
73
|
+
tokens: {
|
|
74
|
+
[ticker: string]: {
|
|
75
|
+
[chainId: string]: {
|
|
76
|
+
address: string,
|
|
77
|
+
decimals: number
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
directory: string
|
|
83
|
+
): Promise<void> {
|
|
84
|
+
try {
|
|
85
|
+
fs.mkdirSync(directory);
|
|
86
|
+
} catch (e) {}
|
|
87
|
+
for(let [name, plugin] of PluginManager.plugins.entries()) {
|
|
88
|
+
try {
|
|
89
|
+
try {
|
|
90
|
+
fs.mkdirSync(directory+"/"+name);
|
|
91
|
+
} catch (e) {}
|
|
92
|
+
await plugin.onEnable(
|
|
93
|
+
chainsData,
|
|
94
|
+
bitcoinRpc,
|
|
95
|
+
lnd,
|
|
96
|
+
swapPricing,
|
|
97
|
+
tokens,
|
|
98
|
+
directory+"/"+name
|
|
99
|
+
);
|
|
100
|
+
} catch (e) {
|
|
101
|
+
pluginLogger.error(plugin, "enable(): plugin enable error", e);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static async disable() {
|
|
107
|
+
for(let plugin of PluginManager.plugins.values()) {
|
|
108
|
+
try {
|
|
109
|
+
await plugin.onDisable();
|
|
110
|
+
} catch (e) {
|
|
111
|
+
pluginLogger.error(plugin, "disable(): plugin disable error", e);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static async serviceInitialize(handler: SwapHandler<any>) {
|
|
117
|
+
for(let plugin of PluginManager.plugins.values()) {
|
|
118
|
+
try {
|
|
119
|
+
await plugin.onServiceInitialize(handler);
|
|
120
|
+
} catch (e) {
|
|
121
|
+
pluginLogger.error(plugin, "serviceInitialize(): plugin error", e);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
static async onHttpServerStarted(httpServer: any): Promise<void> {
|
|
127
|
+
for(let plugin of PluginManager.plugins.values()) {
|
|
128
|
+
try {
|
|
129
|
+
if(plugin.onHttpServerStarted!=null) await plugin.onHttpServerStarted(httpServer);
|
|
130
|
+
} catch (e) {
|
|
131
|
+
pluginLogger.error(plugin, "onHttpServerStarted(): plugin error", e);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
static async swapStateChange<T extends SwapData>(swap: SwapHandlerSwap<T>, oldState?: any) {
|
|
137
|
+
for(let plugin of PluginManager.plugins.values()) {
|
|
138
|
+
try {
|
|
139
|
+
if(plugin.onSwapStateChange!=null) await plugin.onSwapStateChange(swap);
|
|
140
|
+
} catch (e) {
|
|
141
|
+
pluginLogger.error(plugin, "swapStateChange(): plugin error", e);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static async swapCreate<T extends SwapData>(swap: SwapHandlerSwap<T>) {
|
|
147
|
+
for(let plugin of PluginManager.plugins.values()) {
|
|
148
|
+
try {
|
|
149
|
+
if(plugin.onSwapCreate!=null) await plugin.onSwapCreate(swap);
|
|
150
|
+
} catch (e) {
|
|
151
|
+
pluginLogger.error(plugin, "swapCreate(): plugin error", e);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
static async swapRemove<T extends SwapData>(swap: SwapHandlerSwap<T>) {
|
|
157
|
+
for(let plugin of PluginManager.plugins.values()) {
|
|
158
|
+
try {
|
|
159
|
+
if(plugin.onSwapRemove!=null) await plugin.onSwapRemove(swap);
|
|
160
|
+
} catch (e) {
|
|
161
|
+
pluginLogger.error(plugin, "swapRemove(): plugin error", e);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
static async onHandlePostFromBtcQuote(
|
|
167
|
+
request: RequestData<FromBtcLnRequestType | FromBtcRequestType | FromBtcLnTrustedRequestType>,
|
|
168
|
+
requestedAmount: {input: boolean, amount: BN},
|
|
169
|
+
chainIdentifier: string,
|
|
170
|
+
token: string,
|
|
171
|
+
constraints: {minInBtc: BN, maxInBtc: BN},
|
|
172
|
+
fees: {baseFeeInBtc: BN, feePPM: BN},
|
|
173
|
+
pricePrefetchPromise?: Promise<BN> | null
|
|
174
|
+
): Promise<QuoteThrow | QuoteSetFees | QuoteAmountTooLow | QuoteAmountTooHigh | PluginQuote> {
|
|
175
|
+
for(let plugin of PluginManager.plugins.values()) {
|
|
176
|
+
try {
|
|
177
|
+
if(plugin.onHandlePostFromBtcQuote!=null) {
|
|
178
|
+
const result = await plugin.onHandlePostFromBtcQuote(request, requestedAmount, chainIdentifier, token, constraints, fees, pricePrefetchPromise);
|
|
179
|
+
if(result!=null) {
|
|
180
|
+
if(isQuoteSetFees(result)) return result;
|
|
181
|
+
if(isQuoteThrow(result)) return result;
|
|
182
|
+
if(isQuoteAmountTooHigh(result)) return result;
|
|
183
|
+
if(isQuoteAmountTooLow(result)) return result;
|
|
184
|
+
if(isPluginQuote(result)) {
|
|
185
|
+
if(result.amount.input===requestedAmount.input) throw new Error("Invalid quoting response returned, when input is set, output must be returned, and vice-versa!");
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
} catch (e) {
|
|
191
|
+
pluginLogger.error(plugin, "onSwapRequestToBtcLn(): plugin error", e);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
static async onHandlePreFromBtcQuote(
|
|
198
|
+
request: RequestData<FromBtcLnRequestType | FromBtcRequestType | FromBtcLnTrustedRequestType>,
|
|
199
|
+
requestedAmount: {input: boolean, amount: BN},
|
|
200
|
+
chainIdentifier: string,
|
|
201
|
+
token: string,
|
|
202
|
+
constraints: {minInBtc: BN, maxInBtc: BN},
|
|
203
|
+
fees: {baseFeeInBtc: BN, feePPM: BN}
|
|
204
|
+
): Promise<QuoteThrow | QuoteSetFees | QuoteAmountTooLow | QuoteAmountTooHigh> {
|
|
205
|
+
for(let plugin of PluginManager.plugins.values()) {
|
|
206
|
+
try {
|
|
207
|
+
if(plugin.onHandlePreFromBtcQuote!=null) {
|
|
208
|
+
const result = await plugin.onHandlePreFromBtcQuote(request, requestedAmount, chainIdentifier, token, constraints, fees);
|
|
209
|
+
if(result!=null) {
|
|
210
|
+
if(isQuoteSetFees(result)) return result;
|
|
211
|
+
if(isQuoteThrow(result)) return result;
|
|
212
|
+
if(isQuoteAmountTooHigh(result)) return result;
|
|
213
|
+
if(isQuoteAmountTooLow(result)) return result;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
} catch (e) {
|
|
217
|
+
pluginLogger.error(plugin, "onSwapRequestToBtcLn(): plugin error", e);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
static async onHandlePostToBtcQuote<T extends {networkFee: BN}>(
|
|
224
|
+
request: RequestData<ToBtcLnRequestType | ToBtcRequestType>,
|
|
225
|
+
requestedAmount: {input: boolean, amount: BN},
|
|
226
|
+
chainIdentifier: string,
|
|
227
|
+
token: string,
|
|
228
|
+
constraints: {minInBtc: BN, maxInBtc: BN},
|
|
229
|
+
fees: {baseFeeInBtc: BN, feePPM: BN, networkFeeGetter: (amount: BN) => Promise<T>},
|
|
230
|
+
pricePrefetchPromise?: Promise<BN> | null
|
|
231
|
+
): Promise<QuoteThrow | QuoteSetFees | QuoteAmountTooLow | QuoteAmountTooHigh | (ToBtcPluginQuote & {networkFeeData: T})> {
|
|
232
|
+
for(let plugin of PluginManager.plugins.values()) {
|
|
233
|
+
try {
|
|
234
|
+
if(plugin.onHandlePostToBtcQuote!=null) {
|
|
235
|
+
let networkFeeData: T;
|
|
236
|
+
const result = await plugin.onHandlePostToBtcQuote(request, requestedAmount, chainIdentifier, token, constraints, {
|
|
237
|
+
baseFeeInBtc: fees.baseFeeInBtc,
|
|
238
|
+
feePPM: fees.feePPM,
|
|
239
|
+
networkFeeGetter: async (amount: BN) => {
|
|
240
|
+
networkFeeData = await fees.networkFeeGetter(amount);
|
|
241
|
+
return networkFeeData.networkFee;
|
|
242
|
+
}
|
|
243
|
+
}, pricePrefetchPromise);
|
|
244
|
+
if(result!=null) {
|
|
245
|
+
if(isQuoteSetFees(result)) return result;
|
|
246
|
+
if(isQuoteThrow(result)) return result;
|
|
247
|
+
if(isQuoteAmountTooHigh(result)) return result;
|
|
248
|
+
if(isQuoteAmountTooLow(result)) return result;
|
|
249
|
+
if(isToBtcPluginQuote(result)) {
|
|
250
|
+
if(result.amount.input===requestedAmount.input) throw new Error("Invalid quoting response returned, when input is set, output must be returned, and vice-versa!");
|
|
251
|
+
return {
|
|
252
|
+
...result,
|
|
253
|
+
networkFeeData: networkFeeData
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
} catch (e) {
|
|
259
|
+
pluginLogger.error(plugin, "onSwapRequestToBtcLn(): plugin error", e);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
static async onHandlePreToBtcQuote(
|
|
266
|
+
request: RequestData<ToBtcLnRequestType | ToBtcRequestType>,
|
|
267
|
+
requestedAmount: {input: boolean, amount: BN},
|
|
268
|
+
chainIdentifier: string,
|
|
269
|
+
token: string,
|
|
270
|
+
constraints: {minInBtc: BN, maxInBtc: BN},
|
|
271
|
+
fees: {baseFeeInBtc: BN, feePPM: BN}
|
|
272
|
+
): Promise<QuoteThrow | QuoteSetFees | QuoteAmountTooLow | QuoteAmountTooHigh> {
|
|
273
|
+
for(let plugin of PluginManager.plugins.values()) {
|
|
274
|
+
try {
|
|
275
|
+
if(plugin.onHandlePreToBtcQuote!=null) {
|
|
276
|
+
const result = await plugin.onHandlePreToBtcQuote(request, requestedAmount, chainIdentifier, token, constraints, fees);
|
|
277
|
+
if(result!=null) {
|
|
278
|
+
if(isQuoteSetFees(result)) return result;
|
|
279
|
+
if(isQuoteThrow(result)) return result;
|
|
280
|
+
if(isQuoteAmountTooHigh(result)) return result;
|
|
281
|
+
if(isQuoteAmountTooLow(result)) return result;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
} catch (e) {
|
|
285
|
+
pluginLogger.error(plugin, "onSwapRequestToBtcLn(): plugin error", e);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
static getWhitelistedTxIds(): Set<string> {
|
|
292
|
+
const whitelist: Set<string> = new Set<string>();
|
|
293
|
+
|
|
294
|
+
for(let plugin of PluginManager.plugins.values()) {
|
|
295
|
+
try {
|
|
296
|
+
if(plugin.getWhitelistedTxIds!=null) {
|
|
297
|
+
const result: string[] = plugin.getWhitelistedTxIds();
|
|
298
|
+
if(result!=null) {
|
|
299
|
+
result.forEach(e => whitelist.add(e));
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
} catch (e) {
|
|
303
|
+
pluginLogger.error(plugin, "getWhitelistedTxIds(): plugin error", e);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return whitelist;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import * as BN from "bn.js";
|
|
2
|
+
import {ISwapPrice} from "../swaps/ISwapPrice";
|
|
3
|
+
|
|
4
|
+
const CACHE_DURATION = 15000;
|
|
5
|
+
|
|
6
|
+
export type BinancePriceData = {
|
|
7
|
+
[pair: string]: {
|
|
8
|
+
[chainId: string]: {
|
|
9
|
+
address: string,
|
|
10
|
+
decimals: number
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export class BinanceSwapPrice extends ISwapPrice<{ pair: string, decimals: number }> {
|
|
16
|
+
|
|
17
|
+
url: string;
|
|
18
|
+
cache: {
|
|
19
|
+
[pair: string]: {
|
|
20
|
+
price: number,
|
|
21
|
+
expiry: number
|
|
22
|
+
}
|
|
23
|
+
} = {};
|
|
24
|
+
|
|
25
|
+
constructor(url: string, coins: BinancePriceData) {
|
|
26
|
+
const coinsMap = {};
|
|
27
|
+
for(let pair in coins) {
|
|
28
|
+
const chains = coins[pair];
|
|
29
|
+
for(let chainId in chains) {
|
|
30
|
+
const tokenData = chains[chainId];
|
|
31
|
+
if(coinsMap[chainId]==null) coinsMap[chainId] = {};
|
|
32
|
+
coinsMap[chainId][tokenData.address] = {
|
|
33
|
+
pair,
|
|
34
|
+
decimals: tokenData.decimals
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
super(coinsMap);
|
|
39
|
+
this.url = url || "https://api.binance.com/api/v3";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async fetchPrice(pair: string) {
|
|
43
|
+
const response: Response = await fetch(this.url + "/ticker/price?symbol=" + pair, {
|
|
44
|
+
method: "GET"
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (response.status !== 200) {
|
|
48
|
+
let resp: string;
|
|
49
|
+
try {
|
|
50
|
+
resp = await response.text();
|
|
51
|
+
} catch (e) {
|
|
52
|
+
throw new Error(response.statusText);
|
|
53
|
+
}
|
|
54
|
+
throw new Error(resp);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let jsonBody: any = await response.json();
|
|
58
|
+
|
|
59
|
+
return parseFloat(jsonBody.price);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async getPrice(tokenData: {pair: string}): Promise<BN> {
|
|
63
|
+
const pair = tokenData.pair;
|
|
64
|
+
if(pair.startsWith("$fixed-")) {
|
|
65
|
+
const amt: number = parseFloat(pair.substring(7));
|
|
66
|
+
return new BN(Math.floor(amt*1000000));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const arr = pair.split(";");
|
|
70
|
+
|
|
71
|
+
const promises = [];
|
|
72
|
+
const cachedValue = this.cache[pair];
|
|
73
|
+
if(cachedValue==null || cachedValue.expiry<Date.now()) {
|
|
74
|
+
let resultPrice = 1;
|
|
75
|
+
for (let pair of arr) {
|
|
76
|
+
let invert = false
|
|
77
|
+
if (pair.startsWith("!")) {
|
|
78
|
+
invert = true;
|
|
79
|
+
pair = pair.substring(1);
|
|
80
|
+
}
|
|
81
|
+
const cachedValue = this.cache[pair];
|
|
82
|
+
if (cachedValue == null || cachedValue.expiry < Date.now()) {
|
|
83
|
+
promises.push(this.fetchPrice(pair).then(price => {
|
|
84
|
+
this.cache[pair] = {
|
|
85
|
+
price,
|
|
86
|
+
expiry: Date.now() + CACHE_DURATION
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
if (invert) {
|
|
90
|
+
resultPrice /= price;
|
|
91
|
+
} else {
|
|
92
|
+
resultPrice *= price;
|
|
93
|
+
}
|
|
94
|
+
}));
|
|
95
|
+
} else {
|
|
96
|
+
if (invert) {
|
|
97
|
+
resultPrice /= cachedValue.price;
|
|
98
|
+
} else {
|
|
99
|
+
resultPrice *= cachedValue.price;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
await Promise.all(promises);
|
|
105
|
+
|
|
106
|
+
this.cache[pair] = {
|
|
107
|
+
price: resultPrice,
|
|
108
|
+
expiry: Date.now() + CACHE_DURATION
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return new BN(Math.floor(this.cache[pair].price*100000000000000));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as BN from "bn.js";
|
|
2
|
+
import {ISwapPrice} from "../swaps/ISwapPrice";
|
|
3
|
+
|
|
4
|
+
const CACHE_DURATION = 15000;
|
|
5
|
+
|
|
6
|
+
export type CoinGeckoPriceData = {
|
|
7
|
+
[coinId: string]: {
|
|
8
|
+
[chainId: string]: {
|
|
9
|
+
address: string,
|
|
10
|
+
decimals: number
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export class CoinGeckoSwapPrice extends ISwapPrice<{coinId: string, decimals: number}> {
|
|
16
|
+
|
|
17
|
+
url: string;
|
|
18
|
+
cache: {
|
|
19
|
+
[coinId: string]: {
|
|
20
|
+
price: BN,
|
|
21
|
+
expiry: number
|
|
22
|
+
}
|
|
23
|
+
} = {};
|
|
24
|
+
|
|
25
|
+
constructor(url: string, coins: CoinGeckoPriceData) {
|
|
26
|
+
const coinsMap = {};
|
|
27
|
+
for(let coinId in coins) {
|
|
28
|
+
const chains = coins[coinId];
|
|
29
|
+
for(let chainId in chains) {
|
|
30
|
+
const tokenData = chains[chainId];
|
|
31
|
+
if(coinsMap[chainId]==null) coinsMap[chainId] = {};
|
|
32
|
+
coinsMap[chainId][tokenData.address] = {
|
|
33
|
+
coinId,
|
|
34
|
+
decimals: tokenData.decimals
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
super(coinsMap);
|
|
39
|
+
this.url = url || "https://api.coingecko.com/api/v3";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Returns coin price in mSat
|
|
44
|
+
*
|
|
45
|
+
* @param coin
|
|
46
|
+
*/
|
|
47
|
+
async getPrice(coin: {coinId: string}): Promise<BN> {
|
|
48
|
+
const coinId = coin.coinId;
|
|
49
|
+
if(coinId.startsWith("$fixed-")) {
|
|
50
|
+
const amt: number = parseFloat(coinId.substring(7));
|
|
51
|
+
return new BN(Math.floor(amt*1000));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const cachedValue = this.cache[coinId];
|
|
55
|
+
if(cachedValue!=null && cachedValue.expiry>Date.now()) {
|
|
56
|
+
return cachedValue.price;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const response: Response = await fetch(this.url+"/simple/price?ids="+coinId+"&vs_currencies=sats&precision=3", {
|
|
60
|
+
method: "GET",
|
|
61
|
+
headers: {'Content-Type': 'application/json'}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if(response.status!==200) {
|
|
65
|
+
let resp: string;
|
|
66
|
+
try {
|
|
67
|
+
resp = await response.text();
|
|
68
|
+
} catch (e) {
|
|
69
|
+
throw new Error(response.statusText);
|
|
70
|
+
}
|
|
71
|
+
throw new Error(resp);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let jsonBody: any = await response.json();
|
|
75
|
+
|
|
76
|
+
const amt: number = jsonBody[coinId].sats;
|
|
77
|
+
|
|
78
|
+
const result = new BN(amt*1000);
|
|
79
|
+
|
|
80
|
+
this.cache[coinId] = {
|
|
81
|
+
price: result,
|
|
82
|
+
expiry: Date.now()+CACHE_DURATION
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {StorageObject} from "@atomiqlabs/base";
|
|
2
|
+
import * as BN from "bn.js";
|
|
3
|
+
|
|
4
|
+
export type StorageQueryParam = {
|
|
5
|
+
key: string,
|
|
6
|
+
value?: any,
|
|
7
|
+
values?: any[]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface IIntermediaryStorage<T extends StorageObject> {
|
|
11
|
+
|
|
12
|
+
init(): Promise<void>;
|
|
13
|
+
|
|
14
|
+
query(params: StorageQueryParam[]): Promise<T[]>;
|
|
15
|
+
|
|
16
|
+
getData(hash: string, sequence: BN | null): Promise<T>;
|
|
17
|
+
saveData(hash: string, sequence: BN | null, object: T): Promise<void>;
|
|
18
|
+
removeData(hash: string, sequence: BN | null): Promise<void>;
|
|
19
|
+
loadData(type: new(data: any) => T): Promise<void>;
|
|
20
|
+
|
|
21
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {StorageObject} from "@atomiqlabs/base";
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import {IIntermediaryStorage, StorageQueryParam} from "../storage/IIntermediaryStorage";
|
|
4
|
+
import * as BN from "bn.js";
|
|
5
|
+
|
|
6
|
+
export class IntermediaryStorageManager<T extends StorageObject> implements IIntermediaryStorage<T> {
|
|
7
|
+
|
|
8
|
+
private readonly directory: string;
|
|
9
|
+
private type: new(data: any) => T;
|
|
10
|
+
private data: {
|
|
11
|
+
[key: string]: T
|
|
12
|
+
} = {};
|
|
13
|
+
|
|
14
|
+
constructor(directory: string) {
|
|
15
|
+
this.directory = directory;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async init(): Promise<void> {
|
|
19
|
+
try {
|
|
20
|
+
await fs.mkdir(this.directory)
|
|
21
|
+
} catch (e) {}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
query(params: StorageQueryParam[]): Promise<T[]> {
|
|
25
|
+
return Promise.resolve(Object.keys(this.data).map((val) => this.data[val]).filter((val) => {
|
|
26
|
+
for(let param of params) {
|
|
27
|
+
if(param.value!=null) {
|
|
28
|
+
if(typeof param.value === "object") {
|
|
29
|
+
if(param.value.eq!=null && !param.value.eq(val[param.key])) return false;
|
|
30
|
+
if(param.value.equals!=null && !param.value.equals(val[param.key])) return false;
|
|
31
|
+
} else {
|
|
32
|
+
if(param.value!==val[param.key]) return false;
|
|
33
|
+
}
|
|
34
|
+
} else if(param.values!=null) {
|
|
35
|
+
let hasSome = false;
|
|
36
|
+
for(let expectedValue of param.values) {
|
|
37
|
+
if(typeof expectedValue === "object") {
|
|
38
|
+
if(expectedValue.eq!=null && !expectedValue.eq(val[param.key])) hasSome = true;
|
|
39
|
+
if(expectedValue.equals!=null && !expectedValue.equals(val[param.key])) hasSome = true;
|
|
40
|
+
} else {
|
|
41
|
+
if(expectedValue===val[param.key]) hasSome = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if(!hasSome) return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
getData(paymentHash: string, sequence: BN | null): Promise<T> {
|
|
52
|
+
return Promise.resolve(this.data[paymentHash+"_"+(sequence || new BN(0)).toString("hex", 8)]);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async saveData(hash: string, sequence: BN | null, object: T): Promise<void> {
|
|
56
|
+
|
|
57
|
+
const _sequence = (sequence || new BN(0)).toString("hex", 8);
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
await fs.mkdir(this.directory)
|
|
61
|
+
} catch (e) {}
|
|
62
|
+
|
|
63
|
+
this.data[hash+"_"+_sequence] = object;
|
|
64
|
+
|
|
65
|
+
const cpy = object.serialize();
|
|
66
|
+
|
|
67
|
+
await fs.writeFile(this.directory+"/"+hash+"_"+_sequence+".json", JSON.stringify(cpy));
|
|
68
|
+
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async removeData(hash: string, sequence: BN | null): Promise<void> {
|
|
72
|
+
const identifier = hash+"_"+(sequence || new BN(0)).toString("hex", 8);
|
|
73
|
+
try {
|
|
74
|
+
if(this.data[identifier]!=null) delete this.data[identifier];
|
|
75
|
+
await fs.rm(this.directory+"/"+identifier+".json");
|
|
76
|
+
} catch (e) {
|
|
77
|
+
console.error(e);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async loadData(type: new(data: any) => T): Promise<void> {
|
|
82
|
+
this.type = type;
|
|
83
|
+
|
|
84
|
+
let files: string[];
|
|
85
|
+
try {
|
|
86
|
+
files = await fs.readdir(this.directory);
|
|
87
|
+
} catch (e) {
|
|
88
|
+
console.error(e);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for(let file of files) {
|
|
93
|
+
const indentifier = file.split(".")[0];
|
|
94
|
+
const result = await fs.readFile(this.directory+"/"+file);
|
|
95
|
+
const obj = JSON.parse(result.toString());
|
|
96
|
+
const parsed = new type(obj);
|
|
97
|
+
this.data[indentifier] = parsed;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {StorageObject, IStorageManager} from "@atomiqlabs/base";
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
|
|
4
|
+
export class StorageManager<T extends StorageObject> implements IStorageManager<T> {
|
|
5
|
+
|
|
6
|
+
private readonly directory: string;
|
|
7
|
+
data: {
|
|
8
|
+
[key: string]: T
|
|
9
|
+
} = {};
|
|
10
|
+
|
|
11
|
+
constructor(directory: string) {
|
|
12
|
+
this.directory = directory;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async init(): Promise<void> {
|
|
16
|
+
try {
|
|
17
|
+
await fs.mkdir(this.directory)
|
|
18
|
+
} catch (e) {}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async saveData(hash: string, object: T): Promise<void> {
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
await fs.mkdir(this.directory)
|
|
25
|
+
} catch (e) {}
|
|
26
|
+
|
|
27
|
+
this.data[hash] = object;
|
|
28
|
+
|
|
29
|
+
const cpy = object.serialize();
|
|
30
|
+
|
|
31
|
+
await fs.writeFile(this.directory+"/"+hash+".json", JSON.stringify(cpy));
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async removeData(hash: string): Promise<void> {
|
|
36
|
+
const paymentHash = hash;
|
|
37
|
+
try {
|
|
38
|
+
if(this.data[paymentHash]!=null) delete this.data[paymentHash];
|
|
39
|
+
await fs.rm(this.directory+"/"+paymentHash+".json");
|
|
40
|
+
} catch (e) {
|
|
41
|
+
console.error(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async loadData(type: new(data: any) => T): Promise<T[]> {
|
|
46
|
+
let files;
|
|
47
|
+
try {
|
|
48
|
+
files = await fs.readdir(this.directory);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
console.error(e);
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const arr = [];
|
|
55
|
+
|
|
56
|
+
for(let file of files) {
|
|
57
|
+
const paymentHash = file.split(".")[0];
|
|
58
|
+
const result = await fs.readFile(this.directory+"/"+file);
|
|
59
|
+
const obj = JSON.parse(result.toString());
|
|
60
|
+
const parsed = new type(obj);
|
|
61
|
+
arr.push(parsed);
|
|
62
|
+
this.data[paymentHash] = parsed;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return arr;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
}
|