@xchainjs/xchain-thorchain-amm 0.1.0-alpha
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 +21 -0
- package/README.md +106 -0
- package/lib/index.esm.js +1732 -0
- package/lib/index.js +1746 -0
- package/lib/xchain-thorchain-amm/src/chain-defaults.d.ts +4 -0
- package/lib/xchain-thorchain-amm/src/crypto-amount.d.ts +37 -0
- package/lib/xchain-thorchain-amm/src/index.d.ts +6 -0
- package/lib/xchain-thorchain-amm/src/liquidity-pool-cache.d.ts +73 -0
- package/lib/xchain-thorchain-amm/src/liquidity-pool.d.ts +17 -0
- package/lib/xchain-thorchain-amm/src/thorchain-amm.d.ts +115 -0
- package/lib/xchain-thorchain-amm/src/types.d.ts +90 -0
- package/lib/xchain-thorchain-amm/src/utils/index.d.ts +3 -0
- package/lib/xchain-thorchain-amm/src/utils/liquidity.d.ts +33 -0
- package/lib/xchain-thorchain-amm/src/utils/midgard.d.ts +44 -0
- package/lib/xchain-thorchain-amm/src/utils/swap.d.ts +68 -0
- package/lib/xchain-thorchain-amm/src/utils/thornode.d.ts +57 -0
- package/lib/xchain-thorchain-amm/src/wallet.d.ts +63 -0
- package/package.json +102 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,1746 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var xchainBinance = require('@xchainjs/xchain-binance');
|
|
6
|
+
var xchainBitcoin = require('@xchainjs/xchain-bitcoin');
|
|
7
|
+
var xchainBitcoincash = require('@xchainjs/xchain-bitcoincash');
|
|
8
|
+
var xchainClient = require('@xchainjs/xchain-client');
|
|
9
|
+
var xchainCosmos = require('@xchainjs/xchain-cosmos');
|
|
10
|
+
var xchainDoge = require('@xchainjs/xchain-doge');
|
|
11
|
+
var xchainEthereum = require('@xchainjs/xchain-ethereum');
|
|
12
|
+
var xchainLitecoin = require('@xchainjs/xchain-litecoin');
|
|
13
|
+
var xchainTerra = require('@xchainjs/xchain-terra');
|
|
14
|
+
var xchainThorchain = require('@xchainjs/xchain-thorchain');
|
|
15
|
+
var xchainUtil = require('@xchainjs/xchain-util');
|
|
16
|
+
var ethers = require('ethers');
|
|
17
|
+
var xchainMidgard = require('@xchainjs/xchain-midgard');
|
|
18
|
+
var axios = require('axios');
|
|
19
|
+
var axiosRetry = require('axios-retry');
|
|
20
|
+
var BigNumber = require('bignumber.js');
|
|
21
|
+
var lib = require('@xchainjs/xchain-cosmos/lib');
|
|
22
|
+
var lib$1 = require('@xchainjs/xchain-terra/lib');
|
|
23
|
+
var lib$2 = require('@xchainjs/xchain-thorchain/lib');
|
|
24
|
+
|
|
25
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
26
|
+
|
|
27
|
+
var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
|
|
28
|
+
var axiosRetry__default = /*#__PURE__*/_interopDefaultLegacy(axiosRetry);
|
|
29
|
+
var BigNumber__default = /*#__PURE__*/_interopDefaultLegacy(BigNumber);
|
|
30
|
+
|
|
31
|
+
/*! *****************************************************************************
|
|
32
|
+
Copyright (c) Microsoft Corporation.
|
|
33
|
+
|
|
34
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
35
|
+
purpose with or without fee is hereby granted.
|
|
36
|
+
|
|
37
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
38
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
39
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
40
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
41
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
42
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
43
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
44
|
+
***************************************************************************** */
|
|
45
|
+
|
|
46
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
47
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
48
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
49
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
50
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
51
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
52
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
var routerABI = [
|
|
57
|
+
{
|
|
58
|
+
inputs: [
|
|
59
|
+
],
|
|
60
|
+
stateMutability: "nonpayable",
|
|
61
|
+
type: "constructor"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
anonymous: false,
|
|
65
|
+
inputs: [
|
|
66
|
+
{
|
|
67
|
+
indexed: true,
|
|
68
|
+
internalType: "address",
|
|
69
|
+
name: "to",
|
|
70
|
+
type: "address"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
indexed: true,
|
|
74
|
+
internalType: "address",
|
|
75
|
+
name: "asset",
|
|
76
|
+
type: "address"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
indexed: false,
|
|
80
|
+
internalType: "uint256",
|
|
81
|
+
name: "amount",
|
|
82
|
+
type: "uint256"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
indexed: false,
|
|
86
|
+
internalType: "string",
|
|
87
|
+
name: "memo",
|
|
88
|
+
type: "string"
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
name: "Deposit",
|
|
92
|
+
type: "event"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
anonymous: false,
|
|
96
|
+
inputs: [
|
|
97
|
+
{
|
|
98
|
+
indexed: true,
|
|
99
|
+
internalType: "address",
|
|
100
|
+
name: "oldVault",
|
|
101
|
+
type: "address"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
indexed: true,
|
|
105
|
+
internalType: "address",
|
|
106
|
+
name: "newVault",
|
|
107
|
+
type: "address"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
indexed: false,
|
|
111
|
+
internalType: "address",
|
|
112
|
+
name: "asset",
|
|
113
|
+
type: "address"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
indexed: false,
|
|
117
|
+
internalType: "uint256",
|
|
118
|
+
name: "amount",
|
|
119
|
+
type: "uint256"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
indexed: false,
|
|
123
|
+
internalType: "string",
|
|
124
|
+
name: "memo",
|
|
125
|
+
type: "string"
|
|
126
|
+
}
|
|
127
|
+
],
|
|
128
|
+
name: "TransferAllowance",
|
|
129
|
+
type: "event"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
anonymous: false,
|
|
133
|
+
inputs: [
|
|
134
|
+
{
|
|
135
|
+
indexed: true,
|
|
136
|
+
internalType: "address",
|
|
137
|
+
name: "vault",
|
|
138
|
+
type: "address"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
indexed: true,
|
|
142
|
+
internalType: "address",
|
|
143
|
+
name: "to",
|
|
144
|
+
type: "address"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
indexed: false,
|
|
148
|
+
internalType: "address",
|
|
149
|
+
name: "asset",
|
|
150
|
+
type: "address"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
indexed: false,
|
|
154
|
+
internalType: "uint256",
|
|
155
|
+
name: "amount",
|
|
156
|
+
type: "uint256"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
indexed: false,
|
|
160
|
+
internalType: "string",
|
|
161
|
+
name: "memo",
|
|
162
|
+
type: "string"
|
|
163
|
+
}
|
|
164
|
+
],
|
|
165
|
+
name: "TransferOut",
|
|
166
|
+
type: "event"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
anonymous: false,
|
|
170
|
+
inputs: [
|
|
171
|
+
{
|
|
172
|
+
indexed: true,
|
|
173
|
+
internalType: "address",
|
|
174
|
+
name: "oldVault",
|
|
175
|
+
type: "address"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
indexed: true,
|
|
179
|
+
internalType: "address",
|
|
180
|
+
name: "newVault",
|
|
181
|
+
type: "address"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
components: [
|
|
185
|
+
{
|
|
186
|
+
internalType: "address",
|
|
187
|
+
name: "asset",
|
|
188
|
+
type: "address"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
internalType: "uint256",
|
|
192
|
+
name: "amount",
|
|
193
|
+
type: "uint256"
|
|
194
|
+
}
|
|
195
|
+
],
|
|
196
|
+
indexed: false,
|
|
197
|
+
internalType: "struct Router.Coin[]",
|
|
198
|
+
name: "coins",
|
|
199
|
+
type: "tuple[]"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
indexed: false,
|
|
203
|
+
internalType: "string",
|
|
204
|
+
name: "memo",
|
|
205
|
+
type: "string"
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
name: "VaultTransfer",
|
|
209
|
+
type: "event"
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
inputs: [
|
|
213
|
+
],
|
|
214
|
+
name: "RUNE",
|
|
215
|
+
outputs: [
|
|
216
|
+
{
|
|
217
|
+
internalType: "address",
|
|
218
|
+
name: "",
|
|
219
|
+
type: "address"
|
|
220
|
+
}
|
|
221
|
+
],
|
|
222
|
+
stateMutability: "view",
|
|
223
|
+
type: "function"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
inputs: [
|
|
227
|
+
{
|
|
228
|
+
internalType: "address[]",
|
|
229
|
+
name: "recipients",
|
|
230
|
+
type: "address[]"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
components: [
|
|
234
|
+
{
|
|
235
|
+
internalType: "address",
|
|
236
|
+
name: "asset",
|
|
237
|
+
type: "address"
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
internalType: "uint256",
|
|
241
|
+
name: "amount",
|
|
242
|
+
type: "uint256"
|
|
243
|
+
}
|
|
244
|
+
],
|
|
245
|
+
internalType: "struct Router.Coin[]",
|
|
246
|
+
name: "coins",
|
|
247
|
+
type: "tuple[]"
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
internalType: "string[]",
|
|
251
|
+
name: "memos",
|
|
252
|
+
type: "string[]"
|
|
253
|
+
}
|
|
254
|
+
],
|
|
255
|
+
name: "batchTransferOut",
|
|
256
|
+
outputs: [
|
|
257
|
+
],
|
|
258
|
+
stateMutability: "payable",
|
|
259
|
+
type: "function"
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
inputs: [
|
|
263
|
+
{
|
|
264
|
+
internalType: "address payable",
|
|
265
|
+
name: "vault",
|
|
266
|
+
type: "address"
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
internalType: "address",
|
|
270
|
+
name: "asset",
|
|
271
|
+
type: "address"
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
internalType: "uint256",
|
|
275
|
+
name: "amount",
|
|
276
|
+
type: "uint256"
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
internalType: "string",
|
|
280
|
+
name: "memo",
|
|
281
|
+
type: "string"
|
|
282
|
+
}
|
|
283
|
+
],
|
|
284
|
+
name: "deposit",
|
|
285
|
+
outputs: [
|
|
286
|
+
],
|
|
287
|
+
stateMutability: "payable",
|
|
288
|
+
type: "function"
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
inputs: [
|
|
292
|
+
{
|
|
293
|
+
internalType: "address",
|
|
294
|
+
name: "router",
|
|
295
|
+
type: "address"
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
internalType: "address payable",
|
|
299
|
+
name: "asgard",
|
|
300
|
+
type: "address"
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
components: [
|
|
304
|
+
{
|
|
305
|
+
internalType: "address",
|
|
306
|
+
name: "asset",
|
|
307
|
+
type: "address"
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
internalType: "uint256",
|
|
311
|
+
name: "amount",
|
|
312
|
+
type: "uint256"
|
|
313
|
+
}
|
|
314
|
+
],
|
|
315
|
+
internalType: "struct Router.Coin[]",
|
|
316
|
+
name: "coins",
|
|
317
|
+
type: "tuple[]"
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
internalType: "string",
|
|
321
|
+
name: "memo",
|
|
322
|
+
type: "string"
|
|
323
|
+
}
|
|
324
|
+
],
|
|
325
|
+
name: "returnVaultAssets",
|
|
326
|
+
outputs: [
|
|
327
|
+
],
|
|
328
|
+
stateMutability: "payable",
|
|
329
|
+
type: "function"
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
inputs: [
|
|
333
|
+
{
|
|
334
|
+
internalType: "address",
|
|
335
|
+
name: "router",
|
|
336
|
+
type: "address"
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
internalType: "address",
|
|
340
|
+
name: "newVault",
|
|
341
|
+
type: "address"
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
internalType: "address",
|
|
345
|
+
name: "asset",
|
|
346
|
+
type: "address"
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
internalType: "uint256",
|
|
350
|
+
name: "amount",
|
|
351
|
+
type: "uint256"
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
internalType: "string",
|
|
355
|
+
name: "memo",
|
|
356
|
+
type: "string"
|
|
357
|
+
}
|
|
358
|
+
],
|
|
359
|
+
name: "transferAllowance",
|
|
360
|
+
outputs: [
|
|
361
|
+
],
|
|
362
|
+
stateMutability: "nonpayable",
|
|
363
|
+
type: "function"
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
inputs: [
|
|
367
|
+
{
|
|
368
|
+
internalType: "address payable",
|
|
369
|
+
name: "to",
|
|
370
|
+
type: "address"
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
internalType: "address",
|
|
374
|
+
name: "asset",
|
|
375
|
+
type: "address"
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
internalType: "uint256",
|
|
379
|
+
name: "amount",
|
|
380
|
+
type: "uint256"
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
internalType: "string",
|
|
384
|
+
name: "memo",
|
|
385
|
+
type: "string"
|
|
386
|
+
}
|
|
387
|
+
],
|
|
388
|
+
name: "transferOut",
|
|
389
|
+
outputs: [
|
|
390
|
+
],
|
|
391
|
+
stateMutability: "payable",
|
|
392
|
+
type: "function"
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
inputs: [
|
|
396
|
+
{
|
|
397
|
+
internalType: "address",
|
|
398
|
+
name: "",
|
|
399
|
+
type: "address"
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
internalType: "address",
|
|
403
|
+
name: "",
|
|
404
|
+
type: "address"
|
|
405
|
+
}
|
|
406
|
+
],
|
|
407
|
+
name: "vaultAllowance",
|
|
408
|
+
outputs: [
|
|
409
|
+
{
|
|
410
|
+
internalType: "uint256",
|
|
411
|
+
name: "",
|
|
412
|
+
type: "uint256"
|
|
413
|
+
}
|
|
414
|
+
],
|
|
415
|
+
stateMutability: "view",
|
|
416
|
+
type: "function"
|
|
417
|
+
}
|
|
418
|
+
];
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Utility Class to combine an amount (asset/base) with the Asset
|
|
422
|
+
*
|
|
423
|
+
*/
|
|
424
|
+
class CryptoAmount {
|
|
425
|
+
constructor(amount, asset) {
|
|
426
|
+
this.asset = asset;
|
|
427
|
+
this.baseAmount = amount;
|
|
428
|
+
}
|
|
429
|
+
plus(v) {
|
|
430
|
+
this.check(v);
|
|
431
|
+
const baseAmountResult = this.baseAmount.plus(v.baseAmount);
|
|
432
|
+
return new CryptoAmount(baseAmountResult, this.asset);
|
|
433
|
+
}
|
|
434
|
+
minus(v) {
|
|
435
|
+
this.check(v);
|
|
436
|
+
const baseAmountResult = this.baseAmount.minus(v.baseAmount);
|
|
437
|
+
return new CryptoAmount(baseAmountResult, this.asset);
|
|
438
|
+
}
|
|
439
|
+
times(v) {
|
|
440
|
+
this.check(v);
|
|
441
|
+
if (v instanceof CryptoAmount) {
|
|
442
|
+
const baseAmountResult = this.baseAmount.times(v.baseAmount);
|
|
443
|
+
return new CryptoAmount(baseAmountResult, this.asset);
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
const baseAmountResult = this.baseAmount.times(v);
|
|
447
|
+
return new CryptoAmount(baseAmountResult, this.asset);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
div(v) {
|
|
451
|
+
this.check(v);
|
|
452
|
+
if (v instanceof CryptoAmount) {
|
|
453
|
+
const baseAmountResult = this.baseAmount.div(v.baseAmount);
|
|
454
|
+
return new CryptoAmount(baseAmountResult, this.asset);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
const baseAmountResult = this.baseAmount.div(v);
|
|
458
|
+
return new CryptoAmount(baseAmountResult, this.asset);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
lt(v) {
|
|
462
|
+
this.check(v);
|
|
463
|
+
return this.baseAmount.lt(v.baseAmount);
|
|
464
|
+
}
|
|
465
|
+
lte(v) {
|
|
466
|
+
this.check(v);
|
|
467
|
+
return this.baseAmount.lte(v.baseAmount);
|
|
468
|
+
}
|
|
469
|
+
gt(v) {
|
|
470
|
+
this.check(v);
|
|
471
|
+
return this.baseAmount.gt(v.baseAmount);
|
|
472
|
+
}
|
|
473
|
+
gte(v) {
|
|
474
|
+
this.check(v);
|
|
475
|
+
return this.baseAmount.gte(v.baseAmount);
|
|
476
|
+
}
|
|
477
|
+
eq(v) {
|
|
478
|
+
this.check(v);
|
|
479
|
+
return this.baseAmount.eq(v.baseAmount);
|
|
480
|
+
}
|
|
481
|
+
formatedAssetString() {
|
|
482
|
+
return xchainUtil.formatAssetAmountCurrency({
|
|
483
|
+
amount: this.assetAmount,
|
|
484
|
+
asset: this.asset,
|
|
485
|
+
trimZeros: true,
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
assetAmountFixedString() {
|
|
489
|
+
return this.assetAmount.amount().toFixed();
|
|
490
|
+
}
|
|
491
|
+
get assetAmount() {
|
|
492
|
+
return xchainUtil.baseToAsset(this.baseAmount);
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* This guard protects against trying to perform math with different assets
|
|
496
|
+
*
|
|
497
|
+
* Example.
|
|
498
|
+
* const x = new CryptoAmount(baseAmount(1),AssetBTC)
|
|
499
|
+
* const y = new CryptoAmount(baseAmount(1),AssetETH)
|
|
500
|
+
*
|
|
501
|
+
* x.plus(y) <- will throw error "cannot perform math on 2 diff assets BTC.BTC ETH.ETH
|
|
502
|
+
*
|
|
503
|
+
* @param v - CryptoNumeric
|
|
504
|
+
*/
|
|
505
|
+
check(v) {
|
|
506
|
+
if (v instanceof CryptoAmount) {
|
|
507
|
+
if (!xchainUtil.eqAsset(this.asset, v.asset)) {
|
|
508
|
+
throw Error(`cannot perform math on 2 diff assets ${xchainUtil.assetToString(this.asset)} ${xchainUtil.assetToString(v.asset)}`);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const defaultMidgardConfig = {
|
|
515
|
+
mainnet: {
|
|
516
|
+
apiRetries: 3,
|
|
517
|
+
midgardBaseUrls: [
|
|
518
|
+
'https://midgard.thorchain.info/',
|
|
519
|
+
'https://midgard.thorswap.net/',
|
|
520
|
+
'https://midgard.ninerealms.com/',
|
|
521
|
+
],
|
|
522
|
+
},
|
|
523
|
+
stagenet: {
|
|
524
|
+
apiRetries: 3,
|
|
525
|
+
midgardBaseUrls: ['https://stagenet-midgard.ninerealms.com/'],
|
|
526
|
+
},
|
|
527
|
+
testnet: {
|
|
528
|
+
apiRetries: 3,
|
|
529
|
+
midgardBaseUrls: ['https://testnet.midgard.thorchain.info/'],
|
|
530
|
+
},
|
|
531
|
+
};
|
|
532
|
+
class Midgard {
|
|
533
|
+
constructor(network = xchainClient.Network.Mainnet, config) {
|
|
534
|
+
this.network = network;
|
|
535
|
+
this.config = config !== null && config !== void 0 ? config : defaultMidgardConfig[this.network];
|
|
536
|
+
axiosRetry__default['default'](axios__default['default'], { retries: this.config.apiRetries, retryDelay: axiosRetry__default['default'].exponentialDelay });
|
|
537
|
+
this.midgardApis = this.config.midgardBaseUrls.map((url) => new xchainMidgard.MidgardApi(new xchainMidgard.Configuration({ basePath: url })));
|
|
538
|
+
}
|
|
539
|
+
getMimirDetails() {
|
|
540
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
541
|
+
const path = '/v2/thorchain/mimir';
|
|
542
|
+
for (const baseUrl of this.config.midgardBaseUrls) {
|
|
543
|
+
try {
|
|
544
|
+
const { data } = yield axios__default['default'].get(`${baseUrl}${path}`);
|
|
545
|
+
return data;
|
|
546
|
+
}
|
|
547
|
+
catch (e) {
|
|
548
|
+
console.error(e);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
throw new Error('Midgard not responding');
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
*
|
|
556
|
+
* @returns an array of Pools
|
|
557
|
+
*/
|
|
558
|
+
getPools() {
|
|
559
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
560
|
+
for (const api of this.midgardApis) {
|
|
561
|
+
try {
|
|
562
|
+
return (yield api.getPools()).data;
|
|
563
|
+
}
|
|
564
|
+
catch (e) {
|
|
565
|
+
console.error(e);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
throw Error(`Midgard not responding`);
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
getAllInboundAddresses() {
|
|
572
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
573
|
+
for (const api of this.midgardApis) {
|
|
574
|
+
try {
|
|
575
|
+
return (yield api.getProxiedInboundAddresses()).data;
|
|
576
|
+
}
|
|
577
|
+
catch (e) {
|
|
578
|
+
console.error(e);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
throw Error(`Midgard not responding`);
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Gets the Inbound Details for a given array of Chains.
|
|
586
|
+
* Will check if chain is THOR.
|
|
587
|
+
* @param chains - external chains
|
|
588
|
+
* @returns inbound details of given chains
|
|
589
|
+
*/
|
|
590
|
+
getInboundDetails(chains) {
|
|
591
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
592
|
+
const [mimirDetails, allInboundDetails] = yield Promise.all([this.getMimirDetails(), this.getAllInboundAddresses()]);
|
|
593
|
+
const inboundDetails = [];
|
|
594
|
+
for (const chain of chains) {
|
|
595
|
+
if (chain != xchainUtil.Chain.THORChain) {
|
|
596
|
+
const inboundDetail = allInboundDetails === null || allInboundDetails === void 0 ? void 0 : allInboundDetails.find((item) => item.chain === chain);
|
|
597
|
+
if (inboundDetail) {
|
|
598
|
+
if (!inboundDetail.gas_rate)
|
|
599
|
+
throw new Error(`Could not get gas_rate for ${chain}`);
|
|
600
|
+
const details = {
|
|
601
|
+
vault: inboundDetail.address,
|
|
602
|
+
gas_rate: new BigNumber__default['default'](inboundDetail.gas_rate),
|
|
603
|
+
haltedChain: (inboundDetail === null || inboundDetail === void 0 ? void 0 : inboundDetail.halted) || !!mimirDetails[`HALT${chain}CHAIN`] || !!mimirDetails['HALTCHAINGLOBAL'],
|
|
604
|
+
haltedTrading: !!mimirDetails['HALTTRADING'] || !!mimirDetails[`HALT${chain}TRADING`],
|
|
605
|
+
haltedLP: !!mimirDetails['PAUSELP'] || !!mimirDetails[`PAUSELP${chain}`],
|
|
606
|
+
};
|
|
607
|
+
if (inboundDetail === null || inboundDetail === void 0 ? void 0 : inboundDetail.router)
|
|
608
|
+
details.router = inboundDetail.router;
|
|
609
|
+
inboundDetails.push(details);
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
throw new Error(`Could not get chain details for ${chain}`);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
const details = {
|
|
617
|
+
vault: '',
|
|
618
|
+
gas_rate: new BigNumber__default['default'](0),
|
|
619
|
+
haltedChain: false,
|
|
620
|
+
haltedTrading: !!mimirDetails['HALTTRADING'],
|
|
621
|
+
haltedLP: false, //
|
|
622
|
+
};
|
|
623
|
+
inboundDetails.push(details);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
return inboundDetails;
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
getConstantsDetails() {
|
|
630
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
631
|
+
const path = 'v2/thorchain/constants';
|
|
632
|
+
for (const baseUrl of this.config.midgardBaseUrls) {
|
|
633
|
+
try {
|
|
634
|
+
const { data } = yield axios__default['default'].get(`${baseUrl}${path}`);
|
|
635
|
+
return data;
|
|
636
|
+
}
|
|
637
|
+
catch (e) {
|
|
638
|
+
console.error(e);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
throw new Error('Midgard not responding');
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
*
|
|
646
|
+
* @returns the outbound Tx Value in RUNE (Basemount)
|
|
647
|
+
*/
|
|
648
|
+
getScheduledOutboundValue() {
|
|
649
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
650
|
+
const path = 'v2/thorchain/queue';
|
|
651
|
+
for (const baseUrl of this.config.midgardBaseUrls) {
|
|
652
|
+
try {
|
|
653
|
+
const { data } = yield axios__default['default'].get(`${baseUrl}${path}`);
|
|
654
|
+
const value = new CryptoAmount(xchainUtil.baseAmount(data['scheduled_outbound_value']), xchainUtil.AssetRuneNative);
|
|
655
|
+
return value;
|
|
656
|
+
}
|
|
657
|
+
catch (e) {
|
|
658
|
+
console.error(e);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
throw new Error('Midgard not responding');
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Function that wraps Mimir and Constants to return the value from a given constant name. Searchs Mimir first.
|
|
666
|
+
*
|
|
667
|
+
* @param networkValueName the network value to be used to search the contsants
|
|
668
|
+
* @returns the mimir or constants value
|
|
669
|
+
*/
|
|
670
|
+
getNetworkValueByNames(networkValueNames) {
|
|
671
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
672
|
+
const [mimirDetails, constantDetails] = yield Promise.all([this.getMimirDetails(), this.getConstantsDetails()]);
|
|
673
|
+
const retVal = {};
|
|
674
|
+
for (const networkValueName of networkValueNames) {
|
|
675
|
+
const mimirValue = mimirDetails[networkValueName.toUpperCase()];
|
|
676
|
+
const constantsValue = constantDetails['int_64_values'][networkValueName];
|
|
677
|
+
if (mimirValue != undefined) {
|
|
678
|
+
retVal[networkValueName] = mimirValue;
|
|
679
|
+
}
|
|
680
|
+
else if (constantDetails != undefined) {
|
|
681
|
+
retVal[networkValueName] = constantsValue;
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
throw Error(`Could not find network value name`);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
console.log(retVal);
|
|
688
|
+
return retVal;
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Gets the latest block using the Health endpoint within Midgard
|
|
693
|
+
*
|
|
694
|
+
* @returns
|
|
695
|
+
*/
|
|
696
|
+
getLatestBlockHeight() {
|
|
697
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
698
|
+
for (const api of this.midgardApis) {
|
|
699
|
+
try {
|
|
700
|
+
const data = (yield api.getHealth()).data;
|
|
701
|
+
return +data.scannerHeight;
|
|
702
|
+
}
|
|
703
|
+
catch (e) {
|
|
704
|
+
console.error(e);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
throw Error(`Midgard not responding`);
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
*
|
|
714
|
+
* @param inputAmount - amount to swap
|
|
715
|
+
* @param pool - Pool Data, RUNE and ASSET Depths
|
|
716
|
+
* @param toRune - Direction of Swap. True if swapping to RUNE.
|
|
717
|
+
* @returns
|
|
718
|
+
*/
|
|
719
|
+
const getSwapFee = (inputAmount, pool, toRune) => {
|
|
720
|
+
// formula: (x * x * Y) / (x + X) ^ 2
|
|
721
|
+
const x = inputAmount.amount();
|
|
722
|
+
const X = toRune ? pool.assetBalance.amount() : pool.runeBalance.amount(); // input is asset if toRune
|
|
723
|
+
const Y = toRune ? pool.runeBalance.amount() : pool.assetBalance.amount(); // output is rune if toRune
|
|
724
|
+
const numerator = x.times(x).multipliedBy(Y);
|
|
725
|
+
const denominator = x.plus(X).pow(2);
|
|
726
|
+
const result = numerator.div(denominator);
|
|
727
|
+
return xchainUtil.baseAmount(result);
|
|
728
|
+
};
|
|
729
|
+
/**
|
|
730
|
+
* Works out the swap slip for a given swap.
|
|
731
|
+
*
|
|
732
|
+
* @param inputAmount - amount to swap
|
|
733
|
+
* @param pool - Pool Data, RUNE and ASSET Depths
|
|
734
|
+
* @param toRune - Direction of Swap. True if swapping to RUNE.
|
|
735
|
+
* @returns The amount of slip. Needs to * 100 to get percentage.
|
|
736
|
+
*/
|
|
737
|
+
const getSwapSlip = (inputAmount, pool, toRune) => {
|
|
738
|
+
// formula: (x) / (x + X)
|
|
739
|
+
const x = inputAmount.amount();
|
|
740
|
+
const X = toRune ? pool.assetBalance.amount() : pool.runeBalance.amount(); // input is asset if toRune
|
|
741
|
+
const result = x.div(x.plus(X));
|
|
742
|
+
return new BigNumber.BigNumber(result);
|
|
743
|
+
};
|
|
744
|
+
/**
|
|
745
|
+
*
|
|
746
|
+
* @param inputAmount - amount to swap
|
|
747
|
+
* @param pool - Pool Data, RUNE and ASSET Depths
|
|
748
|
+
* @param toRune - Direction of Swap. True if swapping to RUNE.
|
|
749
|
+
* @returns The output amount
|
|
750
|
+
*/
|
|
751
|
+
const getSwapOutput = (inputAmount, pool, toRune) => {
|
|
752
|
+
// formula: (x * X * Y) / (x + X) ^ 2
|
|
753
|
+
const x = inputAmount.amount();
|
|
754
|
+
const X = toRune ? pool.assetBalance.amount() : pool.runeBalance.amount(); // input is asset if toRune
|
|
755
|
+
const Y = toRune ? pool.runeBalance.amount() : pool.assetBalance.amount(); // output is rune if toRune
|
|
756
|
+
const numerator = x.times(X).times(Y);
|
|
757
|
+
const denominator = x.plus(X).pow(2);
|
|
758
|
+
const result = numerator.div(denominator);
|
|
759
|
+
return xchainUtil.baseAmount(result);
|
|
760
|
+
};
|
|
761
|
+
const getDoubleSwapOutput = (inputAmount, pool1, pool2) => {
|
|
762
|
+
// formula: getSwapOutput(pool1) => getSwapOutput(pool2)
|
|
763
|
+
const r = getSwapOutput(inputAmount, pool1, true);
|
|
764
|
+
const output = getSwapOutput(r, pool2, false);
|
|
765
|
+
return output;
|
|
766
|
+
};
|
|
767
|
+
/**
|
|
768
|
+
*
|
|
769
|
+
* @param inputAmount - amount to swap
|
|
770
|
+
* @param pool - Pool Data, RUNE and ASSET Depths
|
|
771
|
+
* @returns swap output object - output - fee - slip
|
|
772
|
+
*/
|
|
773
|
+
const getSingleSwap = (inputAmount, pool, toRune) => {
|
|
774
|
+
const output = getSwapOutput(inputAmount, pool, toRune);
|
|
775
|
+
const fee = getSwapFee(inputAmount, pool, toRune);
|
|
776
|
+
const slip = getSwapSlip(inputAmount, pool, toRune);
|
|
777
|
+
const swapOutput = {
|
|
778
|
+
output: output,
|
|
779
|
+
swapFee: fee,
|
|
780
|
+
slip: slip,
|
|
781
|
+
};
|
|
782
|
+
return swapOutput;
|
|
783
|
+
};
|
|
784
|
+
const getDoubleSwapSlip = (inputAmount, pool1, pool2) => {
|
|
785
|
+
// formula: getSwapSlip1(input1) + getSwapSlip2(getSwapOutput1 => input2)
|
|
786
|
+
const swapOutput1 = getSingleSwap(inputAmount, pool1, true);
|
|
787
|
+
const swapOutput2 = getSingleSwap(swapOutput1.output, pool2, false);
|
|
788
|
+
const result = swapOutput2.slip.plus(swapOutput1.slip);
|
|
789
|
+
return result;
|
|
790
|
+
};
|
|
791
|
+
const getValueOfRuneInAsset = (inputRune, pool) => {
|
|
792
|
+
// formula: ((r * A) / R) => A per R ($perRune)
|
|
793
|
+
const r = inputRune.amount();
|
|
794
|
+
const R = pool.runeBalance.amount();
|
|
795
|
+
const A = pool.assetBalance.amount();
|
|
796
|
+
const result = r.times(A).div(R);
|
|
797
|
+
return xchainUtil.baseAmount(result);
|
|
798
|
+
};
|
|
799
|
+
const getDoubleSwapFee = (inputAmount, pool1, pool2) => {
|
|
800
|
+
// formula: getSwapFee1 + getSwapFee2
|
|
801
|
+
const fee1 = getSwapFee(inputAmount, pool1, true);
|
|
802
|
+
const r = getSwapOutput(inputAmount, pool1, true);
|
|
803
|
+
const fee2 = getSwapFee(r, pool2, false);
|
|
804
|
+
const fee1Asset = getValueOfRuneInAsset(fee1, pool2);
|
|
805
|
+
const result = fee2.amount().plus(fee1Asset.amount());
|
|
806
|
+
return xchainUtil.baseAmount(result);
|
|
807
|
+
};
|
|
808
|
+
/**
|
|
809
|
+
*
|
|
810
|
+
* @param inputAmount - amount to swap
|
|
811
|
+
* @param pool - Pool Data, RUNE and ASSET Depths
|
|
812
|
+
* @param toRune - Direction of Swap. True if swapping to RUNE.
|
|
813
|
+
* @returns swap output object - output - fee - slip
|
|
814
|
+
*/
|
|
815
|
+
const getDoubleSwap = (inputAmount, pool1, pool2) => {
|
|
816
|
+
const doubleOutput = getDoubleSwapOutput(inputAmount, pool1, pool2);
|
|
817
|
+
const doubleFee = getDoubleSwapFee(inputAmount, pool1, pool2);
|
|
818
|
+
const doubleSlip = getDoubleSwapSlip(inputAmount, pool1, pool2);
|
|
819
|
+
const SwapOutput = {
|
|
820
|
+
output: doubleOutput,
|
|
821
|
+
swapFee: doubleFee,
|
|
822
|
+
slip: doubleSlip,
|
|
823
|
+
};
|
|
824
|
+
return SwapOutput;
|
|
825
|
+
};
|
|
826
|
+
const getContractAddressFromAsset = (asset) => {
|
|
827
|
+
const assetAddress = asset.symbol.slice(asset.ticker.length + 1);
|
|
828
|
+
return xchainEthereum.strip0x(assetAddress);
|
|
829
|
+
};
|
|
830
|
+
/**
|
|
831
|
+
* Works out the required inbound or outbound fee based on the chain.
|
|
832
|
+
* Call getInboundDetails to get the current gasRate
|
|
833
|
+
*
|
|
834
|
+
* @param sourceAsset
|
|
835
|
+
* @param gasRate
|
|
836
|
+
* @see https://dev.thorchain.org/thorchain-dev/thorchain-and-fees#fee-calcuation-by-chain
|
|
837
|
+
* @returns
|
|
838
|
+
*/
|
|
839
|
+
const calcNetworkFee = (asset, gasRate) => {
|
|
840
|
+
if (asset.synth)
|
|
841
|
+
return new CryptoAmount(xchainUtil.baseAmount(2000000), xchainUtil.AssetRuneNative);
|
|
842
|
+
switch (asset.chain) {
|
|
843
|
+
case xchainUtil.Chain.Bitcoin:
|
|
844
|
+
return new CryptoAmount(xchainUtil.baseAmount(gasRate.multipliedBy(250)), xchainUtil.AssetBTC);
|
|
845
|
+
case xchainUtil.Chain.BitcoinCash:
|
|
846
|
+
return new CryptoAmount(xchainUtil.baseAmount(gasRate.multipliedBy(250)), xchainUtil.AssetBCH);
|
|
847
|
+
case xchainUtil.Chain.Litecoin:
|
|
848
|
+
return new CryptoAmount(xchainUtil.baseAmount(gasRate.multipliedBy(250)), xchainUtil.AssetLTC);
|
|
849
|
+
case xchainUtil.Chain.Doge:
|
|
850
|
+
// NOTE: UTXO chains estimate fees with a 250 byte size
|
|
851
|
+
return new CryptoAmount(xchainUtil.baseAmount(gasRate.multipliedBy(250)), xchainUtil.AssetDOGE);
|
|
852
|
+
case xchainUtil.Chain.Binance:
|
|
853
|
+
//flat fee
|
|
854
|
+
return new CryptoAmount(xchainUtil.baseAmount(gasRate), xchainUtil.AssetBNB);
|
|
855
|
+
case xchainUtil.Chain.Ethereum:
|
|
856
|
+
if (xchainUtil.eqAsset(asset, xchainUtil.AssetETH)) {
|
|
857
|
+
return new CryptoAmount(xchainUtil.baseAmount(gasRate.multipliedBy(35000)), xchainUtil.AssetETH);
|
|
858
|
+
}
|
|
859
|
+
else {
|
|
860
|
+
return new CryptoAmount(xchainUtil.baseAmount(gasRate.multipliedBy(70000)), xchainUtil.AssetETH);
|
|
861
|
+
}
|
|
862
|
+
case xchainUtil.Chain.Terra:
|
|
863
|
+
return new CryptoAmount(xchainUtil.baseAmount(gasRate), lib$1.AssetLUNA);
|
|
864
|
+
case xchainUtil.Chain.Cosmos:
|
|
865
|
+
return new CryptoAmount(xchainUtil.baseAmount(gasRate), lib.AssetAtom);
|
|
866
|
+
case xchainUtil.Chain.THORChain:
|
|
867
|
+
return new CryptoAmount(xchainUtil.baseAmount(2000000), xchainUtil.AssetRuneNative);
|
|
868
|
+
}
|
|
869
|
+
throw new Error(`could not calculate inbound fee for ${asset.chain}`);
|
|
870
|
+
};
|
|
871
|
+
/**
|
|
872
|
+
* Return the chain for a given Asset This method should live somewhere else.
|
|
873
|
+
* @param chain
|
|
874
|
+
* @returns the gas asset type for the given chain
|
|
875
|
+
*/
|
|
876
|
+
const getChainAsset = (chain) => {
|
|
877
|
+
switch (chain) {
|
|
878
|
+
case xchainUtil.BNBChain:
|
|
879
|
+
return xchainUtil.AssetBNB;
|
|
880
|
+
case xchainUtil.BTCChain:
|
|
881
|
+
return xchainUtil.AssetBTC;
|
|
882
|
+
case xchainUtil.ETHChain:
|
|
883
|
+
return xchainUtil.AssetETH;
|
|
884
|
+
case xchainUtil.THORChain:
|
|
885
|
+
return xchainUtil.AssetRuneNative;
|
|
886
|
+
case xchainUtil.CosmosChain:
|
|
887
|
+
return lib.AssetAtom;
|
|
888
|
+
case xchainUtil.BCHChain:
|
|
889
|
+
return xchainUtil.AssetBCH;
|
|
890
|
+
case xchainUtil.LTCChain:
|
|
891
|
+
return xchainUtil.AssetLTC;
|
|
892
|
+
case xchainUtil.DOGEChain:
|
|
893
|
+
return xchainUtil.AssetDOGE;
|
|
894
|
+
case xchainUtil.TerraChain:
|
|
895
|
+
return lib$1.AssetLUNA;
|
|
896
|
+
case xchainUtil.PolkadotChain:
|
|
897
|
+
throw Error('Polkadot is not supported yet');
|
|
898
|
+
default:
|
|
899
|
+
throw Error('Unknown chain');
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
const chainIds = {
|
|
904
|
+
[xchainClient.Network.Mainnet]: 'thorchain-mainnet-v1',
|
|
905
|
+
[xchainClient.Network.Stagenet]: 'chain-id-stagenet',
|
|
906
|
+
[xchainClient.Network.Testnet]: 'thorchain-testnet-v2',
|
|
907
|
+
};
|
|
908
|
+
const APPROVE_GASLIMIT_FALLBACK = '200000';
|
|
909
|
+
const ONE_HOUR = 60 * 60 * 1000;
|
|
910
|
+
/**
|
|
911
|
+
* Wallet Class for managing all xchain-* wallets with a mnemonic seed.
|
|
912
|
+
*/
|
|
913
|
+
class Wallet {
|
|
914
|
+
/**
|
|
915
|
+
* Contructor to create a Wallet
|
|
916
|
+
*
|
|
917
|
+
* @param network - stagenet,testnet,mainnet
|
|
918
|
+
* @param phrase - mnemonic phrase
|
|
919
|
+
* @returns Wallet
|
|
920
|
+
*/
|
|
921
|
+
constructor(network, phrase) {
|
|
922
|
+
this.asgardAssets = undefined;
|
|
923
|
+
this.network = network;
|
|
924
|
+
const settings = { network, phrase };
|
|
925
|
+
this.midgard = new Midgard(this.network);
|
|
926
|
+
this.clients = {
|
|
927
|
+
BCH: new xchainBitcoincash.Client(settings),
|
|
928
|
+
BTC: new xchainBitcoin.Client(settings),
|
|
929
|
+
DOGE: new xchainDoge.Client(settings),
|
|
930
|
+
TERRA: new xchainTerra.Client(settings),
|
|
931
|
+
ETH: new xchainEthereum.Client(settings),
|
|
932
|
+
THOR: new xchainThorchain.Client(Object.assign(Object.assign({}, settings), { chainIds })),
|
|
933
|
+
LTC: new xchainLitecoin.Client(settings),
|
|
934
|
+
BNB: new xchainBinance.Client(settings),
|
|
935
|
+
GAIA: new xchainCosmos.Client(settings),
|
|
936
|
+
};
|
|
937
|
+
this.updateAsgardAddresses(ONE_HOUR);
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Fetch balances for all wallets
|
|
941
|
+
*
|
|
942
|
+
* @returns AllBalances[]
|
|
943
|
+
*/
|
|
944
|
+
getAllBalances() {
|
|
945
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
946
|
+
const clientArray = Object.entries(this.clients);
|
|
947
|
+
const allBalances = yield Promise.all(clientArray.map((entry) => __awaiter(this, void 0, void 0, function* () {
|
|
948
|
+
const chain = entry[0];
|
|
949
|
+
const address = entry[1].getAddress(0);
|
|
950
|
+
try {
|
|
951
|
+
const balances = yield entry[1].getBalance(address);
|
|
952
|
+
return { chain, address, balances };
|
|
953
|
+
}
|
|
954
|
+
catch (err) {
|
|
955
|
+
return { chain, address, balances: err.message };
|
|
956
|
+
}
|
|
957
|
+
})));
|
|
958
|
+
return allBalances;
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* Executes a Swap from THORChainAMM.doSwap()
|
|
963
|
+
*
|
|
964
|
+
* @param swap object with all the required details for a swap.
|
|
965
|
+
* @returns transaction details and explorer url
|
|
966
|
+
* @see ThorchainAMM.doSwap()
|
|
967
|
+
*/
|
|
968
|
+
executeSwap(swap) {
|
|
969
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
970
|
+
this.validateSwap(swap);
|
|
971
|
+
if (swap.sourceAsset.chain === xchainUtil.Chain.THORChain || swap.sourceAsset.synth) {
|
|
972
|
+
return yield this.swapRuneTo(swap);
|
|
973
|
+
}
|
|
974
|
+
else {
|
|
975
|
+
return yield this.swapNonRune(swap);
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
constructSwapMemo(swap) {
|
|
980
|
+
const limstring = swap.limit.amount().toFixed();
|
|
981
|
+
// create LIM with interface ID
|
|
982
|
+
const lim = limstring.substring(0, limstring.length - 3).concat(swap.interfaceID.toString());
|
|
983
|
+
// create the full memo
|
|
984
|
+
let memo = `=:${xchainUtil.assetToString(swap.destinationAsset)}`;
|
|
985
|
+
// needs to be tested
|
|
986
|
+
if (swap.affiliateAddress != '' || swap.affiliateFee == undefined) {
|
|
987
|
+
memo = memo.concat(`:${swap.destinationAddress}:${lim}:${swap.affiliateAddress}:${swap.affiliateFee.amount().toFixed()}`);
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
990
|
+
memo = memo.concat(`:${swap.destinationAddress}:${lim}`);
|
|
991
|
+
}
|
|
992
|
+
// If memo length is too long for BTC, trim it
|
|
993
|
+
if (xchainUtil.eqAsset(swap.sourceAsset, xchainUtil.AssetBTC) && memo.length > 80) {
|
|
994
|
+
memo = `=:${xchainUtil.assetToString(swap.destinationAsset)}:${swap.destinationAddress}`;
|
|
995
|
+
}
|
|
996
|
+
return memo;
|
|
997
|
+
}
|
|
998
|
+
validateSwap(swap) {
|
|
999
|
+
const errors = [];
|
|
1000
|
+
const isThorchainDestinationAsset = swap.destinationAsset.synth || swap.destinationAsset.chain === xchainUtil.Chain.THORChain;
|
|
1001
|
+
const chain = isThorchainDestinationAsset ? xchainUtil.Chain.THORChain : swap.destinationAsset.chain;
|
|
1002
|
+
if (!this.clients[chain].validateAddress(swap.destinationAddress)) {
|
|
1003
|
+
errors.push(`destinationAddress ${swap.destinationAddress} is not a valid address`);
|
|
1004
|
+
}
|
|
1005
|
+
if (swap.affiliateAddress && !this.clients[xchainUtil.Chain.THORChain].validateAddress(swap.affiliateAddress))
|
|
1006
|
+
errors.push(`affiliateAddress ${swap.affiliateAddress} is not a valid address`);
|
|
1007
|
+
if (errors.length > 0)
|
|
1008
|
+
throw Error(errors.join('\n'));
|
|
1009
|
+
}
|
|
1010
|
+
swapRuneTo(swap) {
|
|
1011
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1012
|
+
const thorClient = this.clients.THOR;
|
|
1013
|
+
const waitTimeSeconds = swap.waitTimeSeconds;
|
|
1014
|
+
const hash = yield thorClient.deposit({
|
|
1015
|
+
amount: swap.fromBaseAmount,
|
|
1016
|
+
asset: swap.sourceAsset,
|
|
1017
|
+
memo: this.constructSwapMemo(swap),
|
|
1018
|
+
});
|
|
1019
|
+
return { hash, url: this.clients.THOR.getExplorerTxUrl(hash), waitTimeSeconds };
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
swapNonRune(swap) {
|
|
1023
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1024
|
+
const client = this.clients[swap.sourceAsset.chain];
|
|
1025
|
+
const waitTimeSeconds = swap.waitTimeSeconds;
|
|
1026
|
+
const inboundAsgard = (yield this.getAsgardAssets()).find((item) => {
|
|
1027
|
+
return item.chain === swap.sourceAsset.chain;
|
|
1028
|
+
});
|
|
1029
|
+
if (swap.sourceAsset.chain === xchainUtil.Chain.Ethereum) {
|
|
1030
|
+
const params = {
|
|
1031
|
+
walletIndex: 0,
|
|
1032
|
+
asset: swap.sourceAsset,
|
|
1033
|
+
amount: swap.fromBaseAmount,
|
|
1034
|
+
feeOption: swap.feeOption || xchainClient.FeeOption.Fast,
|
|
1035
|
+
memo: this.constructSwapMemo(swap),
|
|
1036
|
+
};
|
|
1037
|
+
const hash = yield this.sendETHDeposit(params);
|
|
1038
|
+
return { hash, url: client.getExplorerTxUrl(hash), waitTimeSeconds };
|
|
1039
|
+
}
|
|
1040
|
+
else {
|
|
1041
|
+
const params = {
|
|
1042
|
+
walletIndex: 0,
|
|
1043
|
+
asset: swap.sourceAsset,
|
|
1044
|
+
amount: swap.fromBaseAmount,
|
|
1045
|
+
recipient: (inboundAsgard === null || inboundAsgard === void 0 ? void 0 : inboundAsgard.address) || '',
|
|
1046
|
+
memo: this.constructSwapMemo(swap),
|
|
1047
|
+
};
|
|
1048
|
+
const hash = yield client.transfer(params);
|
|
1049
|
+
return { hash, url: client.getExplorerTxUrl(hash), waitTimeSeconds };
|
|
1050
|
+
}
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
updateAsgardAddresses(checkTimeMs) {
|
|
1054
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1055
|
+
try {
|
|
1056
|
+
this.asgardAssets = yield this.midgard.getAllInboundAddresses();
|
|
1057
|
+
}
|
|
1058
|
+
catch (error) {
|
|
1059
|
+
console.error(error);
|
|
1060
|
+
}
|
|
1061
|
+
setTimeout(this.updateAsgardAddresses.bind(this), checkTimeMs);
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Transaction to THORChain inbound address.
|
|
1066
|
+
*
|
|
1067
|
+
* @param {DepositParams} params The transaction options.
|
|
1068
|
+
* @returns {TxHash} The transaction hash.
|
|
1069
|
+
*
|
|
1070
|
+
* @throws {"halted chain"} Thrown if chain is halted.
|
|
1071
|
+
* @throws {"halted trading"} Thrown if trading is halted.
|
|
1072
|
+
* @throws {"amount is not approved"} Thrown if the amount is not allowed to spend
|
|
1073
|
+
* @throws {"router address is not defined"} Thrown if router address is not defined
|
|
1074
|
+
*/
|
|
1075
|
+
sendETHDeposit(params) {
|
|
1076
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1077
|
+
const ethClient = this.clients.ETH;
|
|
1078
|
+
const inboundAsgard = (yield this.getAsgardAssets()).find((item) => {
|
|
1079
|
+
return item.chain === params.asset.chain;
|
|
1080
|
+
});
|
|
1081
|
+
if (!(inboundAsgard === null || inboundAsgard === void 0 ? void 0 : inboundAsgard.router)) {
|
|
1082
|
+
throw new Error('router address is not defined');
|
|
1083
|
+
}
|
|
1084
|
+
const address = this.clients.ETH.getAddress(params.walletIndex);
|
|
1085
|
+
const gasPrice = yield ethClient.estimateGasPrices();
|
|
1086
|
+
if (xchainUtil.eqAsset(params.asset, xchainUtil.AssetETH)) {
|
|
1087
|
+
//ETH is a simple transfer
|
|
1088
|
+
return yield this.clients.ETH.transfer({
|
|
1089
|
+
walletIndex: params.walletIndex || 0,
|
|
1090
|
+
asset: params.asset,
|
|
1091
|
+
amount: params.amount,
|
|
1092
|
+
recipient: inboundAsgard.address,
|
|
1093
|
+
memo: params.memo,
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
else {
|
|
1097
|
+
//erc-20 must be depsited to the router
|
|
1098
|
+
const isApprovedResult = yield this.isTCRouterApprovedToSpend(params.asset, params.amount, params.walletIndex);
|
|
1099
|
+
if (!isApprovedResult) {
|
|
1100
|
+
throw new Error('The amount is not allowed to spend');
|
|
1101
|
+
}
|
|
1102
|
+
const contractAddress = getContractAddressFromAsset(params.asset);
|
|
1103
|
+
const checkSummedContractAddress = ethers.ethers.utils.getAddress(contractAddress);
|
|
1104
|
+
const depositParams = [
|
|
1105
|
+
inboundAsgard.address,
|
|
1106
|
+
checkSummedContractAddress,
|
|
1107
|
+
params.amount.amount().toFixed(),
|
|
1108
|
+
params.memo,
|
|
1109
|
+
];
|
|
1110
|
+
const routerContract = new ethers.ethers.Contract(inboundAsgard.router, routerABI);
|
|
1111
|
+
const gasPriceInWei = gasPrice[params.feeOption];
|
|
1112
|
+
const gasPriceInGwei = gasPriceInWei.div(Math.pow(10, 9)).amount();
|
|
1113
|
+
// TODO should we change the calcInboundFee() to use gasRate in BaseAmount instead of BIgNumber?
|
|
1114
|
+
// currently its hardto know the units to use, GWEI/WEI, etc
|
|
1115
|
+
const gasLimitInWei = calcNetworkFee(params.asset, gasPriceInGwei);
|
|
1116
|
+
const gasLimitInGWei = gasLimitInWei
|
|
1117
|
+
.div(Math.pow(10, 9))
|
|
1118
|
+
.baseAmount.amount()
|
|
1119
|
+
.toFixed();
|
|
1120
|
+
const unsignedTx = yield routerContract.populateTransaction.deposit(...depositParams, {
|
|
1121
|
+
from: address,
|
|
1122
|
+
value: 0,
|
|
1123
|
+
gasPrice: gasPrice.fast.amount().toFixed(),
|
|
1124
|
+
gasLimit: gasLimitInGWei,
|
|
1125
|
+
});
|
|
1126
|
+
const { hash } = yield ethClient.getWallet(params.walletIndex).sendTransaction(unsignedTx);
|
|
1127
|
+
return hash;
|
|
1128
|
+
}
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
isTCRouterApprovedToSpend(asset, amount, walletIndex = 0) {
|
|
1132
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1133
|
+
const ethClient = this.clients.ETH;
|
|
1134
|
+
const router = yield this.getRouterAddressForChain(asset.chain);
|
|
1135
|
+
const contractAddress = getContractAddressFromAsset(asset);
|
|
1136
|
+
return yield ethClient.isApproved({
|
|
1137
|
+
amount: amount,
|
|
1138
|
+
spenderAddress: router,
|
|
1139
|
+
contractAddress,
|
|
1140
|
+
walletIndex: walletIndex,
|
|
1141
|
+
});
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
approveTCRouterToSpend(asset, amount = xchainEthereum.MAX_APPROVAL, walletIndex = 0) {
|
|
1145
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1146
|
+
const ethClient = this.clients.ETH;
|
|
1147
|
+
const contractAddress = getContractAddressFromAsset(asset);
|
|
1148
|
+
const router = yield this.getRouterAddressForChain(asset.chain);
|
|
1149
|
+
// const gasPrice = await ethClient.estimateGasPrices()
|
|
1150
|
+
// const gasLimit = calcInboundFee(asset, gasPrice.fast.amount())
|
|
1151
|
+
const approveParams = {
|
|
1152
|
+
contractAddress,
|
|
1153
|
+
spenderAddress: router,
|
|
1154
|
+
amount: xchainUtil.baseAmount(amount.toString(), xchainEthereum.ETH_DECIMAL),
|
|
1155
|
+
walletIndex,
|
|
1156
|
+
gasLimitFallback: APPROVE_GASLIMIT_FALLBACK,
|
|
1157
|
+
};
|
|
1158
|
+
return yield ethClient.approve(approveParams);
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
getRouterAddressForChain(chain) {
|
|
1162
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1163
|
+
const inboundAsgard = (yield this.getAsgardAssets()).find((item) => {
|
|
1164
|
+
return item.chain === chain;
|
|
1165
|
+
});
|
|
1166
|
+
if (!(inboundAsgard === null || inboundAsgard === void 0 ? void 0 : inboundAsgard.router)) {
|
|
1167
|
+
throw new Error('router address is not defined');
|
|
1168
|
+
}
|
|
1169
|
+
return inboundAsgard === null || inboundAsgard === void 0 ? void 0 : inboundAsgard.router;
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
getAsgardAssets() {
|
|
1173
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1174
|
+
if (!this.asgardAssets) {
|
|
1175
|
+
this.asgardAssets = yield this.midgard.getAllInboundAddresses();
|
|
1176
|
+
}
|
|
1177
|
+
return this.asgardAssets;
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
const DefaultChainAttributes = {
|
|
1183
|
+
BCH: {
|
|
1184
|
+
blockReward: 6.25,
|
|
1185
|
+
avgBlockTimeInSecs: 600,
|
|
1186
|
+
},
|
|
1187
|
+
BTC: {
|
|
1188
|
+
blockReward: 6.25,
|
|
1189
|
+
avgBlockTimeInSecs: 600,
|
|
1190
|
+
},
|
|
1191
|
+
ETH: {
|
|
1192
|
+
blockReward: 2,
|
|
1193
|
+
avgBlockTimeInSecs: 13,
|
|
1194
|
+
},
|
|
1195
|
+
LTC: {
|
|
1196
|
+
blockReward: 12.5,
|
|
1197
|
+
avgBlockTimeInSecs: 150,
|
|
1198
|
+
},
|
|
1199
|
+
DOGE: {
|
|
1200
|
+
blockReward: 10000,
|
|
1201
|
+
avgBlockTimeInSecs: 60,
|
|
1202
|
+
},
|
|
1203
|
+
GAIA: {
|
|
1204
|
+
blockReward: 0,
|
|
1205
|
+
avgBlockTimeInSecs: 6,
|
|
1206
|
+
},
|
|
1207
|
+
TERRA: {
|
|
1208
|
+
blockReward: 0,
|
|
1209
|
+
avgBlockTimeInSecs: 0,
|
|
1210
|
+
},
|
|
1211
|
+
BNB: {
|
|
1212
|
+
blockReward: 0,
|
|
1213
|
+
avgBlockTimeInSecs: 6,
|
|
1214
|
+
},
|
|
1215
|
+
THOR: {
|
|
1216
|
+
blockReward: 0,
|
|
1217
|
+
avgBlockTimeInSecs: 6,
|
|
1218
|
+
},
|
|
1219
|
+
POLKA: {
|
|
1220
|
+
blockReward: 0,
|
|
1221
|
+
avgBlockTimeInSecs: 0,
|
|
1222
|
+
},
|
|
1223
|
+
};
|
|
1224
|
+
|
|
1225
|
+
/**
|
|
1226
|
+
* Represent a Liquidity Pool in Thorchain
|
|
1227
|
+
*/
|
|
1228
|
+
class LiquidityPool {
|
|
1229
|
+
constructor(pool) {
|
|
1230
|
+
this.pool = pool;
|
|
1231
|
+
const asset = xchainUtil.assetFromString(this.pool.asset);
|
|
1232
|
+
if (!asset)
|
|
1233
|
+
throw new Error(`could not parse ${this.pool.asset}`);
|
|
1234
|
+
this.asset = asset;
|
|
1235
|
+
this.assetString = this.pool.asset;
|
|
1236
|
+
this.assetBalance = xchainUtil.baseAmount(this.pool.assetDepth);
|
|
1237
|
+
this.runeBalance = xchainUtil.baseAmount(this.pool.runeDepth); //Rune is always 8 decimals
|
|
1238
|
+
this.runeToAssetRatio = this.runeBalance.amount().div(this.assetBalance.amount());
|
|
1239
|
+
this.assetToRuneRatio = this.assetBalance.amount().div(this.runeBalance.amount());
|
|
1240
|
+
}
|
|
1241
|
+
isAvailable() {
|
|
1242
|
+
return this.pool.status.toLowerCase() === 'available';
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
const SAME_ASSET_EXCHANGE_RATE = new BigNumber.BigNumber(1);
|
|
1247
|
+
/**
|
|
1248
|
+
* This class manages retrieving information from up to date Thorchain Liquidity Pools
|
|
1249
|
+
*/
|
|
1250
|
+
class LiquidityPoolCache {
|
|
1251
|
+
/**
|
|
1252
|
+
* Contrustor to create a LiquidityPoolCache
|
|
1253
|
+
*
|
|
1254
|
+
* @param midgard - an instance of the midgard API (could be pointing to stagenet,testnet,mainnet)
|
|
1255
|
+
* @param expirePoolCacheMillis - how long should the pools be cached before expiry
|
|
1256
|
+
* @returns LiquidityPoolCache
|
|
1257
|
+
*/
|
|
1258
|
+
constructor(midgard, expirePoolCacheMillis = 6000) {
|
|
1259
|
+
this.midgard = midgard;
|
|
1260
|
+
this.expirePoolCacheMillis = expirePoolCacheMillis;
|
|
1261
|
+
//initialize the cache
|
|
1262
|
+
this.refereshPoolCache();
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Gets the exchange rate of the from asset in terms on the to asset
|
|
1266
|
+
*
|
|
1267
|
+
* @param asset - cannot be RUNE.
|
|
1268
|
+
* @returns Promise<BigNumber>
|
|
1269
|
+
*/
|
|
1270
|
+
getExchangeRate(from, to) {
|
|
1271
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1272
|
+
let exchangeRate;
|
|
1273
|
+
if (xchainUtil.eqAsset(from, to)) {
|
|
1274
|
+
exchangeRate = SAME_ASSET_EXCHANGE_RATE;
|
|
1275
|
+
}
|
|
1276
|
+
else if (lib$2.isAssetRuneNative(from)) {
|
|
1277
|
+
// Runes per Asset
|
|
1278
|
+
const lpTo = yield this.getPoolForAsset(to);
|
|
1279
|
+
exchangeRate = lpTo.assetToRuneRatio;
|
|
1280
|
+
}
|
|
1281
|
+
else if (lib$2.isAssetRuneNative(to)) {
|
|
1282
|
+
// Asset per rune
|
|
1283
|
+
const lpFrom = yield this.getPoolForAsset(from);
|
|
1284
|
+
exchangeRate = lpFrom.runeToAssetRatio;
|
|
1285
|
+
}
|
|
1286
|
+
else {
|
|
1287
|
+
// AssetA per AssetB
|
|
1288
|
+
const lpFrom = yield this.getPoolForAsset(from);
|
|
1289
|
+
const lpTo = yield this.getPoolForAsset(to);
|
|
1290
|
+
// from/R * R/to = from/to
|
|
1291
|
+
exchangeRate = lpFrom.runeToAssetRatio.times(lpTo.assetToRuneRatio);
|
|
1292
|
+
}
|
|
1293
|
+
return exchangeRate;
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* Gets the Liquidity Pool for a given Asset
|
|
1298
|
+
*
|
|
1299
|
+
* @param asset - cannot be RUNE, since Rune is the other side of each pool.
|
|
1300
|
+
* @returns Promise<LiquidityPool>
|
|
1301
|
+
*/
|
|
1302
|
+
getPoolForAsset(asset) {
|
|
1303
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1304
|
+
if (lib$2.isAssetRuneNative(asset))
|
|
1305
|
+
throw Error(`AssetRuneNative doesn't have a pool`);
|
|
1306
|
+
const pools = yield this.getPools();
|
|
1307
|
+
// Not: we use ticker, not asset string to get the same pool for both assets and synths
|
|
1308
|
+
const pool = pools[asset.ticker];
|
|
1309
|
+
if (pool) {
|
|
1310
|
+
return pool;
|
|
1311
|
+
}
|
|
1312
|
+
throw Error(`Pool for ${xchainUtil.assetToString(asset)} not found`);
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Get all the Liquidity Pools currently cached.
|
|
1317
|
+
* if the cache is expired, the pools wioll be re-fetched from midgard
|
|
1318
|
+
*
|
|
1319
|
+
* @returns Promise<Record<string, LiquidityPool>>
|
|
1320
|
+
*/
|
|
1321
|
+
getPools() {
|
|
1322
|
+
var _a, _b;
|
|
1323
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1324
|
+
const millisSinceLastRefeshed = Date.now() - (((_a = this.poolCache) === null || _a === void 0 ? void 0 : _a.lastRefreshed) || 0);
|
|
1325
|
+
if (millisSinceLastRefeshed > this.expirePoolCacheMillis) {
|
|
1326
|
+
try {
|
|
1327
|
+
yield this.refereshPoolCache();
|
|
1328
|
+
console.log('updated pool cache');
|
|
1329
|
+
}
|
|
1330
|
+
catch (e) {
|
|
1331
|
+
console.error(e);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
if (this.poolCache) {
|
|
1335
|
+
return (_b = this.poolCache) === null || _b === void 0 ? void 0 : _b.pools;
|
|
1336
|
+
}
|
|
1337
|
+
else {
|
|
1338
|
+
throw Error(`Could not refresh Pools `);
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
* Refreshes the Pool Cache
|
|
1344
|
+
*
|
|
1345
|
+
* NOTE: do not call refereshPoolCache() directly, call getPools() instead
|
|
1346
|
+
* which will refresh the cache if it's expired
|
|
1347
|
+
*/
|
|
1348
|
+
refereshPoolCache() {
|
|
1349
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1350
|
+
const pools = yield this.midgard.getPools();
|
|
1351
|
+
const poolMap = {};
|
|
1352
|
+
if (pools) {
|
|
1353
|
+
for (const pool of pools) {
|
|
1354
|
+
const lp = new LiquidityPool(pool);
|
|
1355
|
+
poolMap[lp.asset.ticker] = lp;
|
|
1356
|
+
}
|
|
1357
|
+
this.poolCache = {
|
|
1358
|
+
lastRefreshed: Date.now(),
|
|
1359
|
+
pools: poolMap,
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
*
|
|
1366
|
+
* Calcuate the expected slip, output & swapFee given the current pool depths
|
|
1367
|
+
*
|
|
1368
|
+
* swapFee - the amount of asset lost according to slip calculations
|
|
1369
|
+
* slip - the percent (0-1) of original amount lost to slipfees
|
|
1370
|
+
* output - the amount of asset expected from the swap *
|
|
1371
|
+
*
|
|
1372
|
+
* @param inputAmount - CryptoAmount amount to swap from
|
|
1373
|
+
* @param destinationAsset - destimation Asset to swap to
|
|
1374
|
+
* @returns SwapOutput - swap output object - output - fee - slip
|
|
1375
|
+
*/
|
|
1376
|
+
getExpectedSwapOutput(inputAmount, destinationAsset) {
|
|
1377
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1378
|
+
if (lib$2.isAssetRuneNative(inputAmount.asset)) {
|
|
1379
|
+
//singleswap from rune -> asset
|
|
1380
|
+
const pool = yield this.getPoolForAsset(destinationAsset);
|
|
1381
|
+
return getSingleSwap(inputAmount.baseAmount, pool, false);
|
|
1382
|
+
}
|
|
1383
|
+
else if (lib$2.isAssetRuneNative(destinationAsset)) {
|
|
1384
|
+
//singleswap from asset -> rune
|
|
1385
|
+
const pool = yield this.getPoolForAsset(inputAmount.asset);
|
|
1386
|
+
return getSingleSwap(inputAmount.baseAmount, pool, true);
|
|
1387
|
+
}
|
|
1388
|
+
else {
|
|
1389
|
+
//doubleswap asset-> asset
|
|
1390
|
+
const inPpool = yield this.getPoolForAsset(inputAmount.asset);
|
|
1391
|
+
const destPool = yield this.getPoolForAsset(destinationAsset);
|
|
1392
|
+
return getDoubleSwap(inputAmount.baseAmount, inPpool, destPool);
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Returns the exchange of a CryptoAmount to a different Asset
|
|
1398
|
+
*
|
|
1399
|
+
* Ex. convert(input:100 BUSD, outAsset: BTC) -> 0.0001234 BTC
|
|
1400
|
+
*
|
|
1401
|
+
* @param input - amount/asset to convert to outAsset
|
|
1402
|
+
* @param ouAsset - the Asset you want to convert to
|
|
1403
|
+
* @returns CryptoAmount of input
|
|
1404
|
+
*/
|
|
1405
|
+
convert(input, outAsset) {
|
|
1406
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1407
|
+
const exchangeRate = yield this.getExchangeRate(input.asset, outAsset);
|
|
1408
|
+
const amt = input.assetAmount.times(exchangeRate);
|
|
1409
|
+
const result = new CryptoAmount(xchainUtil.assetToBase(amt), outAsset);
|
|
1410
|
+
return result;
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
const BN_1 = new BigNumber.BigNumber(1);
|
|
1416
|
+
/**
|
|
1417
|
+
* THORChain Class for interacting with THORChain.
|
|
1418
|
+
* Recommended main class to use for swapping with THORChain
|
|
1419
|
+
* Has access to Midgard and THORNode data
|
|
1420
|
+
*/
|
|
1421
|
+
class ThorchainAMM {
|
|
1422
|
+
/**
|
|
1423
|
+
* Contructor to create a ThorchainAMM
|
|
1424
|
+
*
|
|
1425
|
+
* @param midgard - an instance of the midgard API (could be pointing to stagenet,testnet,mainnet)
|
|
1426
|
+
* @param expirePoolCacheMillis - how long should the pools be cached before expiry
|
|
1427
|
+
* @param chainAttributes - atrributes used to calculate waitTime & conf counting
|
|
1428
|
+
* @returns ThorchainAMM
|
|
1429
|
+
*/
|
|
1430
|
+
constructor(midgard, expirePoolCacheMillis = 6000, chainAttributes = DefaultChainAttributes) {
|
|
1431
|
+
this.midgard = midgard;
|
|
1432
|
+
this.allPools = new LiquidityPoolCache(midgard, expirePoolCacheMillis);
|
|
1433
|
+
this.chainAttributes = chainAttributes;
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Provides a swap estimate for the given swap detail. Will check the params for errors before trying to get the estimate.
|
|
1437
|
+
* Uses current pool data, works out inbound and outboud fee, affiliate fees and works out the expected wait time for the swap (in and out)
|
|
1438
|
+
*
|
|
1439
|
+
* @param params - amount to swap
|
|
1440
|
+
|
|
1441
|
+
* @returns The SwapEstimate
|
|
1442
|
+
*/
|
|
1443
|
+
estimateSwap(params) {
|
|
1444
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1445
|
+
this.isValidSwap(params);
|
|
1446
|
+
const [sourceInboundDetails, destinationInboundDetails] = yield this.midgard.getInboundDetails([
|
|
1447
|
+
params.input.asset.chain,
|
|
1448
|
+
params.destinationAsset.chain,
|
|
1449
|
+
]);
|
|
1450
|
+
const swapEstimate = yield this.calcSwapEstimate(params, sourceInboundDetails, destinationInboundDetails);
|
|
1451
|
+
const errors = yield this.getSwapEstimateErrors(params, swapEstimate, sourceInboundDetails, destinationInboundDetails);
|
|
1452
|
+
if (errors.length > 0) {
|
|
1453
|
+
swapEstimate.canSwap = false;
|
|
1454
|
+
swapEstimate.errors = errors;
|
|
1455
|
+
}
|
|
1456
|
+
else {
|
|
1457
|
+
swapEstimate.canSwap = true;
|
|
1458
|
+
if (params.destinationAsset.chain !== xchainUtil.Chain.THORChain && !params.destinationAsset.synth) {
|
|
1459
|
+
//---------------- Work out total Wait Time for Swap ---------------------- /
|
|
1460
|
+
const inboundDelay = yield this.confCounting(params.input);
|
|
1461
|
+
const outboundDelay = yield this.outboundDelay(swapEstimate.netOutput);
|
|
1462
|
+
swapEstimate.waitTimeSeconds = inboundDelay + outboundDelay;
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
return swapEstimate;
|
|
1466
|
+
});
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* Convinience method to convert TotalFees to a different CryptoAmount
|
|
1470
|
+
*
|
|
1471
|
+
* TotalFees are always calculated and returned in RUNE, this method can
|
|
1472
|
+
* be used to show the equivalent fees in another Asset Type
|
|
1473
|
+
*
|
|
1474
|
+
* @param fees: TotalFees - the fees you want to convert
|
|
1475
|
+
* @param asset: Asset - the asset you want the fees converted to
|
|
1476
|
+
* @returns TotalFees in asset
|
|
1477
|
+
*/
|
|
1478
|
+
getFeesIn(fees, asset) {
|
|
1479
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1480
|
+
return {
|
|
1481
|
+
inboundFee: yield this.convert(fees.inboundFee, asset),
|
|
1482
|
+
swapFee: yield this.convert(fees.swapFee, asset),
|
|
1483
|
+
outboundFee: yield this.convert(fees.outboundFee, asset),
|
|
1484
|
+
affiliateFee: yield this.convert(fees.affiliateFee, asset),
|
|
1485
|
+
};
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1488
|
+
/**
|
|
1489
|
+
* Returns the exchange of a CryptoAmount to a different Asset
|
|
1490
|
+
*
|
|
1491
|
+
* Ex. convert(input:100 BUSD, outAsset: BTC) -> 0.0001234 BTC
|
|
1492
|
+
*
|
|
1493
|
+
* @param input - amount/asset to convert to outAsset
|
|
1494
|
+
* @param ouAsset - the Asset you want to convert to
|
|
1495
|
+
* @returns CryptoAmount of input
|
|
1496
|
+
*/
|
|
1497
|
+
convert(input, outAsset) {
|
|
1498
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1499
|
+
return yield this.allPools.convert(input, outAsset);
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
/**
|
|
1503
|
+
* Conducts a swap with the given inputs. Should be called after estimateSwap() to ensure the swap is valid
|
|
1504
|
+
*
|
|
1505
|
+
* @param wallet - wallet to use
|
|
1506
|
+
* @param params - swap paraps
|
|
1507
|
+
* @param destinationAddress - were to send the output of the swap
|
|
1508
|
+
* @param affiliateAddress - were to send the affilate Address, should be a THOR address (optional)
|
|
1509
|
+
* @param interfaceID - id if the calling interface (optional)
|
|
1510
|
+
* @returns {SwapSubmitted} - Tx Hash, URL of BlockExplorer and expected wait time.
|
|
1511
|
+
*/
|
|
1512
|
+
doSwap(wallet, params, destinationAddress, affiliateAddress = '', interfaceID = 999) {
|
|
1513
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1514
|
+
// TODO validate all input fields
|
|
1515
|
+
this.isValidSwap(params);
|
|
1516
|
+
// remove any affiliateFee. netInput * affiliateFee (%age) of the destination asset type
|
|
1517
|
+
const affiliateFee = params.input.baseAmount.times(params.affiliateFeePercent || 0);
|
|
1518
|
+
// Work out LIM from the slip percentage
|
|
1519
|
+
let limPercentage = BN_1;
|
|
1520
|
+
if (params.slipLimit) {
|
|
1521
|
+
limPercentage = BN_1.minus(params.slipLimit || 1);
|
|
1522
|
+
} // else allowed slip is 100%
|
|
1523
|
+
// out min outbound asset based on limPercentage
|
|
1524
|
+
const limAssetAmount = yield this.allPools.convert(params.input.times(limPercentage), params.destinationAsset);
|
|
1525
|
+
let waitTimeSeconds = yield this.confCounting(params.input);
|
|
1526
|
+
const outboundDelay = yield this.outboundDelay(limAssetAmount);
|
|
1527
|
+
waitTimeSeconds = outboundDelay + waitTimeSeconds;
|
|
1528
|
+
return yield wallet.executeSwap({
|
|
1529
|
+
fromBaseAmount: params.input.baseAmount,
|
|
1530
|
+
sourceAsset: params.input.asset,
|
|
1531
|
+
destinationAsset: params.destinationAsset,
|
|
1532
|
+
limit: limAssetAmount.baseAmount,
|
|
1533
|
+
destinationAddress,
|
|
1534
|
+
affiliateAddress,
|
|
1535
|
+
affiliateFee,
|
|
1536
|
+
interfaceID,
|
|
1537
|
+
waitTimeSeconds,
|
|
1538
|
+
});
|
|
1539
|
+
});
|
|
1540
|
+
}
|
|
1541
|
+
/**
|
|
1542
|
+
* Basic Checks for swap information
|
|
1543
|
+
* @param params
|
|
1544
|
+
*/
|
|
1545
|
+
isValidSwap(params) {
|
|
1546
|
+
// TODO validate all input fields
|
|
1547
|
+
if (xchainUtil.eqAsset(params.input.asset, params.destinationAsset))
|
|
1548
|
+
throw Error(`sourceAsset and destinationAsset cannot be the same`);
|
|
1549
|
+
if (params.input.baseAmount.lte(0))
|
|
1550
|
+
throw Error('inputAmount must be greater than 0');
|
|
1551
|
+
if (params.affiliateFeePercent && (params.affiliateFeePercent < 0 || params.affiliateFeePercent > 0.1))
|
|
1552
|
+
throw Error(`affiliateFee must be between 0 and 1000`);
|
|
1553
|
+
}
|
|
1554
|
+
/**
|
|
1555
|
+
* Does the calculations for the swap.
|
|
1556
|
+
* Used by estimateSwap
|
|
1557
|
+
*
|
|
1558
|
+
* @param params
|
|
1559
|
+
* @param sourceInboundDetails
|
|
1560
|
+
* @param destinationInboundDetails
|
|
1561
|
+
* @param sourcePool
|
|
1562
|
+
* @param destinationPool
|
|
1563
|
+
* @returns
|
|
1564
|
+
*/
|
|
1565
|
+
calcSwapEstimate(params, sourceInboundDetails, destinationInboundDetails) {
|
|
1566
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1567
|
+
const input = params.input;
|
|
1568
|
+
const inputInRune = yield this.allPools.convert(input, xchainUtil.AssetRuneNative);
|
|
1569
|
+
const inboundFeeInAsset = calcNetworkFee(input.asset, sourceInboundDetails.gas_rate);
|
|
1570
|
+
let outboundFeeInAsset = calcNetworkFee(params.destinationAsset, destinationInboundDetails.gas_rate);
|
|
1571
|
+
outboundFeeInAsset = outboundFeeInAsset.times(3);
|
|
1572
|
+
const inboundFeeInRune = yield this.allPools.convert(inboundFeeInAsset, xchainUtil.AssetRuneNative);
|
|
1573
|
+
const outboundFeeInRune = yield this.allPools.convert(outboundFeeInAsset, xchainUtil.AssetRuneNative);
|
|
1574
|
+
// ---------- Remove Fees from inbound before doing the swap -----------
|
|
1575
|
+
// TODO confirm with chris about this change
|
|
1576
|
+
// const inputMinusInboundFeeInRune = inputInRune.minus(inboundFeeInRune)
|
|
1577
|
+
const inputMinusInboundFeeInRune = inputInRune;
|
|
1578
|
+
// remove any affiliateFee. netInput * affiliateFee (%age) of the destination asset type
|
|
1579
|
+
const affiliateFeeInRune = inputMinusInboundFeeInRune.times(params.affiliateFeePercent || 0);
|
|
1580
|
+
// remove the affiliate fee from the input.
|
|
1581
|
+
const inputNetAmountInRune = inputMinusInboundFeeInRune.minus(affiliateFeeInRune);
|
|
1582
|
+
// convert back to input asset
|
|
1583
|
+
const inputNetInAsset = yield this.allPools.convert(inputNetAmountInRune, input.asset);
|
|
1584
|
+
// now calculate swapfee based on inputNetAmount
|
|
1585
|
+
const swapOutput = yield this.allPools.getExpectedSwapOutput(inputNetInAsset, params.destinationAsset);
|
|
1586
|
+
const swapFeeInAsset = new CryptoAmount(swapOutput.swapFee, xchainUtil.AssetRuneNative);
|
|
1587
|
+
const swapFeeInRune = yield this.allPools.convert(swapFeeInAsset, xchainUtil.AssetRuneNative);
|
|
1588
|
+
const outputInAsset = new CryptoAmount(swapOutput.output, params.destinationAsset);
|
|
1589
|
+
const outputInRune = yield this.allPools.convert(outputInAsset, xchainUtil.AssetRuneNative);
|
|
1590
|
+
// ---------------- Remove Outbound Fee ---------------------- /
|
|
1591
|
+
const netOutputInRune = outputInRune.minus(outboundFeeInRune);
|
|
1592
|
+
const netOutputInAsset = yield this.allPools.convert(netOutputInRune, params.destinationAsset);
|
|
1593
|
+
const totalFees = {
|
|
1594
|
+
inboundFee: inboundFeeInRune,
|
|
1595
|
+
swapFee: swapFeeInRune,
|
|
1596
|
+
outboundFee: outboundFeeInRune,
|
|
1597
|
+
affiliateFee: affiliateFeeInRune,
|
|
1598
|
+
};
|
|
1599
|
+
const swapEstimate = {
|
|
1600
|
+
totalFees: totalFees,
|
|
1601
|
+
slipPercentage: swapOutput.slip,
|
|
1602
|
+
netOutput: netOutputInAsset,
|
|
1603
|
+
waitTimeSeconds: 0,
|
|
1604
|
+
canSwap: false, // assume false for now, the getSwapEstimateErrors() step will flip this flag if required
|
|
1605
|
+
};
|
|
1606
|
+
return swapEstimate;
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* Looks for errors or issues within swap prams before doing the swap.
|
|
1611
|
+
*
|
|
1612
|
+
*
|
|
1613
|
+
* @param params
|
|
1614
|
+
* @param estimate
|
|
1615
|
+
* @param sourcePool
|
|
1616
|
+
* @param sourceInboundDetails
|
|
1617
|
+
* @param destinationPool
|
|
1618
|
+
* @param destinationInboundDetails
|
|
1619
|
+
* @returns
|
|
1620
|
+
*/
|
|
1621
|
+
getSwapEstimateErrors(params, estimate, sourceInboundDetails, destinationInboundDetails) {
|
|
1622
|
+
var _a;
|
|
1623
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1624
|
+
const errors = [];
|
|
1625
|
+
const sourceAsset = params.input.asset;
|
|
1626
|
+
const destAsset = params.destinationAsset;
|
|
1627
|
+
if (!lib$2.isAssetRuneNative(sourceAsset)) {
|
|
1628
|
+
const sourcePool = yield this.allPools.getPoolForAsset(sourceAsset);
|
|
1629
|
+
if (!sourcePool.isAvailable())
|
|
1630
|
+
errors.push(`sourceAsset ${sourceAsset.ticker} does not have a valid liquidity pool`);
|
|
1631
|
+
}
|
|
1632
|
+
if (!lib$2.isAssetRuneNative(destAsset)) {
|
|
1633
|
+
const destPool = yield this.allPools.getPoolForAsset(destAsset);
|
|
1634
|
+
if (!destPool.isAvailable())
|
|
1635
|
+
errors.push(`destinationAsset ${destAsset.ticker} does not have a valid liquidity pool`);
|
|
1636
|
+
}
|
|
1637
|
+
if (sourceInboundDetails.haltedChain)
|
|
1638
|
+
errors.push(`source chain is halted`);
|
|
1639
|
+
if (sourceInboundDetails.haltedTrading)
|
|
1640
|
+
errors.push(`source pool is halted trading`);
|
|
1641
|
+
if (destinationInboundDetails.haltedChain)
|
|
1642
|
+
errors.push(`destination chain is halted`);
|
|
1643
|
+
if (destinationInboundDetails.haltedTrading)
|
|
1644
|
+
errors.push(`destination pool is halted trading`);
|
|
1645
|
+
if (estimate.slipPercentage.gte(params.slipLimit || 1))
|
|
1646
|
+
errors.push(`expected slip: ${estimate.slipPercentage.toFixed()} is greater than your slip limit:${(_a = params.slipLimit) === null || _a === void 0 ? void 0 : _a.toFixed()} `);
|
|
1647
|
+
// only proceed to check fees if there are no errors so far
|
|
1648
|
+
if (errors.length > 0)
|
|
1649
|
+
return errors;
|
|
1650
|
+
// Check if the inputAmount value is enough to cover all the fees.
|
|
1651
|
+
const canCoverFeesError = yield this.checkCoverFees(params, estimate);
|
|
1652
|
+
if (canCoverFeesError)
|
|
1653
|
+
errors.push(canCoverFeesError);
|
|
1654
|
+
return errors;
|
|
1655
|
+
});
|
|
1656
|
+
}
|
|
1657
|
+
checkCoverFees(params, estimate) {
|
|
1658
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1659
|
+
let result = undefined;
|
|
1660
|
+
const input = yield this.allPools.convert(params.input, xchainUtil.AssetRuneNative);
|
|
1661
|
+
const fees = yield this.getFeesIn(estimate.totalFees, xchainUtil.AssetRuneNative);
|
|
1662
|
+
const totalSwapFeesInRune = fees.inboundFee.plus(fees.outboundFee).plus(fees.swapFee).plus(fees.affiliateFee);
|
|
1663
|
+
if (totalSwapFeesInRune.gte(input))
|
|
1664
|
+
result = `Input amount ${input.formatedAssetString()} is less than or equal to total swap fees`;
|
|
1665
|
+
return result;
|
|
1666
|
+
});
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Works out how long an outbound Tx will be held by THORChain before sending.
|
|
1670
|
+
*
|
|
1671
|
+
* @param outboundAmount: CryptoAmount being sent.
|
|
1672
|
+
* @returns required delay in seconds
|
|
1673
|
+
* @see https://gitlab.com/thorchain/thornode/-/blob/develop/x/thorchain/manager_txout_current.go#L548
|
|
1674
|
+
*/
|
|
1675
|
+
outboundDelay(outboundAmount) {
|
|
1676
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1677
|
+
const networkValues = yield this.midgard.getNetworkValueByNames([
|
|
1678
|
+
'MinTxOutVolumeThreshold',
|
|
1679
|
+
'MaxTxOutOffset',
|
|
1680
|
+
'TXOUTDELAYRATE',
|
|
1681
|
+
]);
|
|
1682
|
+
const minTxOutVolumeThreshold = new CryptoAmount(xchainUtil.baseAmount(networkValues['MinTxOutVolumeThreshold']), xchainUtil.AssetRuneNative);
|
|
1683
|
+
const maxTxOutOffset = Number.parseInt(networkValues['MaxTxOutOffset']);
|
|
1684
|
+
let txOutDelayRate = new CryptoAmount(xchainUtil.baseAmount(networkValues['TXOUTDELAYRATE']), xchainUtil.AssetRuneNative);
|
|
1685
|
+
const getScheduledOutboundValue = yield this.midgard.getScheduledOutboundValue();
|
|
1686
|
+
const thorChainblocktime = this.chainAttributes[xchainUtil.THORChain].avgBlockTimeInSecs; // blocks required to confirm tx
|
|
1687
|
+
// If asset is equal to Rune set runeValue as outbound amount else set it to the asset's value in rune
|
|
1688
|
+
const runeValue = yield this.allPools.convert(outboundAmount, xchainUtil.AssetRuneNative);
|
|
1689
|
+
// Check rune value amount
|
|
1690
|
+
if (runeValue.lt(minTxOutVolumeThreshold)) {
|
|
1691
|
+
return thorChainblocktime;
|
|
1692
|
+
}
|
|
1693
|
+
// Rune value in the outbound queue
|
|
1694
|
+
if (getScheduledOutboundValue == undefined) {
|
|
1695
|
+
throw new Error(`Could not return Scheduled Outbound Value`);
|
|
1696
|
+
}
|
|
1697
|
+
// Add OutboundAmount in rune to the oubound queue
|
|
1698
|
+
const outboundAmountTotal = runeValue.plus(getScheduledOutboundValue);
|
|
1699
|
+
// calculate the if outboundAmountTotal is over the volume threshold
|
|
1700
|
+
const volumeThreshold = outboundAmountTotal.div(minTxOutVolumeThreshold);
|
|
1701
|
+
// check delay rate
|
|
1702
|
+
txOutDelayRate = txOutDelayRate.minus(volumeThreshold).baseAmount.amount().lt(1)
|
|
1703
|
+
? new CryptoAmount(xchainUtil.baseAmount(1), xchainUtil.AssetRuneNative)
|
|
1704
|
+
: txOutDelayRate;
|
|
1705
|
+
// calculate the minimum number of blocks in the future the txn has to be
|
|
1706
|
+
let minBlocks = runeValue.div(txOutDelayRate).baseAmount.amount().toNumber();
|
|
1707
|
+
minBlocks = minBlocks > maxTxOutOffset ? maxTxOutOffset : minBlocks;
|
|
1708
|
+
return minBlocks * thorChainblocktime;
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Finds the required confCount required for an inbound or outbound Tx to THORChain. Estimate based on Midgard data only.
|
|
1713
|
+
*
|
|
1714
|
+
* Finds the gas asset of the given asset (e.g. BUSD is on BNB), finds the value of asset in Gas Asset then finds the required confirmation count.
|
|
1715
|
+
* ConfCount is then times by 6 seconds.
|
|
1716
|
+
*
|
|
1717
|
+
* @param inbound: CryptoAmount - amount/asset of the outbound amount.
|
|
1718
|
+
* @returns time in seconds before a Tx is confirmed by THORChain
|
|
1719
|
+
* @see https://docs.thorchain.org/chain-clients/overview
|
|
1720
|
+
*/
|
|
1721
|
+
confCounting(inbound) {
|
|
1722
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1723
|
+
// RUNE, BNB and Synths have near instant finality, so no conf counting required.
|
|
1724
|
+
if (lib$2.isAssetRuneNative(inbound.asset) || xchainUtil.eqAsset(xchainUtil.AssetBNB, inbound.asset) || inbound.asset.synth) {
|
|
1725
|
+
return this.chainAttributes[xchainUtil.Chain.THORChain].avgBlockTimeInSecs;
|
|
1726
|
+
}
|
|
1727
|
+
// Get the gas asset for the inbound.asset.chain
|
|
1728
|
+
const chainGasAsset = getChainAsset(inbound.asset.chain);
|
|
1729
|
+
// check for chain asset, else need to convert asset value to chain asset.
|
|
1730
|
+
const amountInGasAsset = yield this.allPools.convert(inbound, chainGasAsset);
|
|
1731
|
+
// Convert to Asset Amount
|
|
1732
|
+
const amountInGasAssetInAsset = amountInGasAsset.assetAmount;
|
|
1733
|
+
const confConfig = this.chainAttributes[inbound.asset.chain];
|
|
1734
|
+
// find the requried confs
|
|
1735
|
+
const requiredConfs = Math.ceil(amountInGasAssetInAsset.amount().div(confConfig.blockReward).toNumber());
|
|
1736
|
+
// convert that into seconds
|
|
1737
|
+
return requiredConfs * confConfig.avgBlockTimeInSecs;
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
exports.CryptoAmount = CryptoAmount;
|
|
1743
|
+
exports.LiquidityPool = LiquidityPool;
|
|
1744
|
+
exports.Midgard = Midgard;
|
|
1745
|
+
exports.ThorchainAMM = ThorchainAMM;
|
|
1746
|
+
exports.Wallet = Wallet;
|