@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/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;