@xchainjs/xchain-thorchain-query 0.2.3 → 0.2.5

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.esm.js CHANGED
@@ -1,18 +1,9 @@
1
- import { BNBChain, AssetBNB } from '@xchainjs/xchain-binance';
2
- import { BTCChain, AssetBTC } from '@xchainjs/xchain-bitcoin';
3
- import { GAIAChain, AssetATOM } from '@xchainjs/xchain-cosmos';
4
- import { AssetRuneNative, THORChain, isAssetRuneNative } from '@xchainjs/xchain-thorchain';
5
1
  import { assetToBase, formatAssetAmountCurrency, baseToAsset, eqAsset, assetToString, assetFromString, baseAmount, assetFromStringEx, assetAmount, getContractAddressFromAsset } from '@xchainjs/xchain-util';
6
2
  import { BigNumber } from 'bignumber.js';
7
3
  import { Network } from '@xchainjs/xchain-client';
8
4
  import { MidgardApi, Configuration } from '@xchainjs/xchain-midgard';
9
5
  import axios from 'axios';
10
6
  import axiosRetry from 'axios-retry';
11
- import { AVAXChain, AssetAVAX } from '@xchainjs/xchain-avax';
12
- import { BCHChain, AssetBCH } from '@xchainjs/xchain-bitcoincash';
13
- import { DOGEChain, AssetDOGE } from '@xchainjs/xchain-doge';
14
- import { ETHChain, AssetETH } from '@xchainjs/xchain-ethereum';
15
- import { LTCChain, AssetLTC } from '@xchainjs/xchain-litecoin';
16
7
  import { TransactionsApi, Configuration as Configuration$1, QueueApi, NetworkApi, PoolsApi, LiquidityProvidersApi, SaversApi, QuoteApi, MimirApi } from '@xchainjs/xchain-thornode';
17
8
 
18
9
  /*! *****************************************************************************
@@ -77,6 +68,14 @@ const DefaultChainAttributes = {
77
68
  blockReward: 0,
78
69
  avgBlockTimeInSecs: 6,
79
70
  },
71
+ BSC: {
72
+ blockReward: 0,
73
+ avgBlockTimeInSecs: 3,
74
+ },
75
+ MAYA: {
76
+ blockReward: 0,
77
+ avgBlockTimeInSecs: 6,
78
+ },
80
79
  };
81
80
 
82
81
  /**
@@ -196,148 +195,29 @@ class LiquidityPool {
196
195
  }
197
196
  }
198
197
 
199
- const defaultMidgardConfig = {
200
- mainnet: {
201
- apiRetries: 3,
202
- midgardBaseUrls: ['https://midgard.ninerealms.com'],
203
- },
204
- stagenet: {
205
- apiRetries: 3,
206
- midgardBaseUrls: ['https://stagenet-midgard.ninerealms.com'],
207
- },
208
- testnet: {
209
- apiRetries: 3,
210
- midgardBaseUrls: ['https://testnet.midgard.thorchain.info'],
211
- },
212
- };
213
- class Midgard {
214
- constructor(network = Network.Mainnet, config) {
215
- this.network = network;
216
- this.config = config !== null && config !== void 0 ? config : defaultMidgardConfig[this.network];
217
- axiosRetry(axios, { retries: this.config.apiRetries, retryDelay: axiosRetry.exponentialDelay });
218
- this.midgardApis = this.config.midgardBaseUrls.map((url) => new MidgardApi(new Configuration({ basePath: url })));
219
- }
220
- /**
221
- *
222
- * @returns an array of Pools
223
- */
224
- getPools() {
225
- return __awaiter(this, void 0, void 0, function* () {
226
- for (const api of this.midgardApis) {
227
- try {
228
- return (yield api.getPools()).data;
229
- }
230
- catch (e) {
231
- //console.error(e)
232
- }
233
- }
234
- throw new Error(`Midgard not responding`);
235
- });
236
- }
237
- /**
238
- * Gets the latest block using the Health endpoint within Midgard
239
- *
240
- * @returns
241
- */
242
- getLatestBlockHeight() {
243
- return __awaiter(this, void 0, void 0, function* () {
244
- for (const api of this.midgardApis) {
245
- try {
246
- const data = (yield api.getHealth()).data;
247
- return +data.scannerHeight;
248
- }
249
- catch (e) {
250
- //console.error(e)
251
- }
252
- }
253
- throw Error(`Midgard not responding`);
254
- });
255
- }
256
- /**
257
- * Gets actions object for any of the parameters
258
- * @param txHash transaction id
259
- * @returns Type Action array of objects
260
- */
261
- getActions(address, txid, asset, type, affiliate, limit, offset) {
262
- return __awaiter(this, void 0, void 0, function* () {
263
- for (const api of this.midgardApis) {
264
- try {
265
- const actions = (yield api.getActions(address, txid, asset, type, affiliate, limit, offset)).data.actions;
266
- return actions;
267
- }
268
- catch (e) {
269
- //console.error(e)
270
- }
271
- }
272
- throw Error(`Midgard not responding`);
273
- });
274
- }
275
- /**
276
- * Function to return member details based on valid liquidity position
277
- * @param address - needed to query for Lp details
278
- * @returns - object type of Member Detail
279
- */
280
- getMember(address) {
281
- return __awaiter(this, void 0, void 0, function* () {
282
- for (const api of this.midgardApis) {
283
- try {
284
- const memberDetail = (yield api.getMemberDetail(address)).data;
285
- return memberDetail;
286
- }
287
- catch (e) {
288
- //console.error(e)
289
- }
290
- }
291
- throw Error(`Midgard not responding`);
292
- });
293
- }
294
- /**
295
- * Function to return pool statistics for a particular asset
296
- * @param asset - asset string to query its pool stats
297
- * @returns - type object poolstatsDetail
298
- */
299
- getPoolStats(asset) {
300
- return __awaiter(this, void 0, void 0, function* () {
301
- for (const api of this.midgardApis) {
302
- try {
303
- const poolDetail = (yield api.getPoolStats(asset)).data;
304
- return poolDetail;
305
- }
306
- catch (e) {
307
- //console.error(e)
308
- }
309
- }
310
- throw Error(`Midgard not responding`);
311
- });
312
- }
313
- /**
314
- * Function to return THORNameDetails for a particular name
315
- * @param name - thorname string to query
316
- * @returns - type object THORNameDetails
317
- */
318
- getTHORNameDetails(name) {
319
- return __awaiter(this, void 0, void 0, function* () {
320
- for (const api of this.midgardApis) {
321
- try {
322
- const resp = yield api.getTHORNameDetail(name);
323
- if (resp.status == 404) {
324
- return undefined;
325
- }
326
- else if (resp.status == 200) {
327
- return resp.data;
328
- }
329
- }
330
- catch (e) {
331
- // if (resp.status == 404) {
332
- // return undefined
333
- // }
334
- //console.error(e)
335
- }
336
- }
337
- throw Error(`Midgard not responding`);
338
- });
339
- }
340
- }
198
+ const AssetBNB = assetFromStringEx('BNB.BNB');
199
+ const AssetAVAX = assetFromStringEx('AVAX.AVAX');
200
+ const AssetBTC = assetFromStringEx('BTC.BTC');
201
+ const AssetBCH = assetFromStringEx('BCH.BHC');
202
+ const AssetETH = assetFromStringEx('ETH.ETH');
203
+ const AssetDOGE = assetFromStringEx('DOGE.DOGE');
204
+ const AssetLTC = assetFromStringEx('LTC.LTC');
205
+ const AssetATOM = assetFromStringEx('GAIA.ATOM');
206
+ const AssetMAYA = assetFromStringEx('MAYA.CACAO');
207
+ const AssetBSC = assetFromStringEx('BSC.BNB');
208
+ const AssetRuneNative = assetFromStringEx('THOR.RUNE');
209
+ const BNBChain = 'BNB';
210
+ const BTCChain = 'BTC';
211
+ const BCHChain = 'BCH';
212
+ const ETHChain = 'ETH';
213
+ const GAIAChain = 'GAIA';
214
+ const DOGEChain = 'DOGE';
215
+ const LTCChain = 'LTC';
216
+ const AVAXChain = 'AVAX';
217
+ const MAYAChain = 'MAYA';
218
+ const BSCChain = 'BSC';
219
+ const THORChain = 'THOR';
220
+ const isAssetRuneNative = (asset) => assetToString(asset) === assetToString(AssetRuneNative);
341
221
 
342
222
  const getBaseAmountWithDiffDecimals = (inputAmount, outDecimals) => {
343
223
  const inDecimals = inputAmount.baseAmount.decimal;
@@ -491,6 +371,10 @@ const getChainAsset = (chain) => {
491
371
  return AssetDOGE;
492
372
  case AVAXChain:
493
373
  return AssetAVAX;
374
+ case BSCChain:
375
+ return AssetBSC;
376
+ case MAYAChain:
377
+ return AssetMAYA;
494
378
  default:
495
379
  throw Error('Unknown chain');
496
380
  }
@@ -540,58 +424,300 @@ const calcNetworkFee = (asset, inbound) => {
540
424
  else {
541
425
  return new CryptoAmount(gasRateinETHWei.times(70000), AssetETH);
542
426
  }
543
- case AVAXChain:
544
- const gasRateinAVAXGwei = inbound.gasRate;
545
- const gasRateinAVAXWei = baseAmount(gasRateinAVAXGwei.multipliedBy(Math.pow(10, 9)), 18);
546
- if (eqAsset(asset, AssetAVAX)) {
547
- return new CryptoAmount(gasRateinAVAXWei.times(21000), AssetAVAX);
427
+ case AVAXChain:
428
+ const gasRateinAVAXGwei = inbound.gasRate;
429
+ const gasRateinAVAXWei = baseAmount(gasRateinAVAXGwei.multipliedBy(Math.pow(10, 9)), 18);
430
+ if (eqAsset(asset, AssetAVAX)) {
431
+ return new CryptoAmount(gasRateinAVAXWei.times(21000), AssetAVAX);
432
+ }
433
+ else {
434
+ return new CryptoAmount(gasRateinAVAXWei.times(70000), AssetAVAX);
435
+ }
436
+ case GAIAChain:
437
+ return new CryptoAmount(baseAmount(inbound.gasRate), AssetATOM);
438
+ case THORChain:
439
+ return new CryptoAmount(baseAmount(2000000), AssetRuneNative);
440
+ case BSCChain:
441
+ return new CryptoAmount(baseAmount(inbound.gasRate), AssetBSC);
442
+ case MAYAChain:
443
+ return new CryptoAmount(baseAmount(inbound.gasRate), AssetMAYA);
444
+ }
445
+ throw new Error(`could not calculate inbound fee for ${asset.chain}`);
446
+ };
447
+ /**
448
+ * Works out the required outbound fee based on the chain.
449
+ * Call getInboundDetails to get the current outbound fee
450
+ *
451
+ * @param sourceAsset
452
+ * @param inbound detail
453
+ * @see https://dev.thorchain.org/thorchain-dev/thorchain-and-fees#fee-calcuation-by-chain
454
+ * @returns
455
+ */
456
+ const calcOutboundFee = (asset, inbound) => {
457
+ if (asset.synth)
458
+ return new CryptoAmount(baseAmount(2000000), AssetRuneNative);
459
+ switch (asset.chain) {
460
+ case BTCChain:
461
+ return new CryptoAmount(baseAmount(inbound.outboundFee), AssetBTC);
462
+ case BCHChain:
463
+ return new CryptoAmount(baseAmount(inbound.outboundFee), AssetBCH);
464
+ case LTCChain:
465
+ return new CryptoAmount(baseAmount(inbound.outboundFee), AssetLTC);
466
+ case DOGEChain:
467
+ // NOTE: UTXO chains estimate fees with a 250 byte size
468
+ return new CryptoAmount(baseAmount(inbound.outboundFee), AssetDOGE);
469
+ case BNBChain:
470
+ //flat fee
471
+ return new CryptoAmount(baseAmount(inbound.outboundFee), AssetBNB);
472
+ case ETHChain:
473
+ return new CryptoAmount(baseAmount(inbound.outboundFee.multipliedBy(Math.pow(10, 9)), 18), AssetETH);
474
+ case AVAXChain:
475
+ return new CryptoAmount(baseAmount(inbound.outboundFee.multipliedBy(Math.pow(10, 9)), 18), AssetAVAX);
476
+ case GAIAChain:
477
+ return new CryptoAmount(baseAmount(inbound.outboundFee), AssetATOM);
478
+ case BSCChain:
479
+ return new CryptoAmount(baseAmount(inbound.outboundFee), AssetBSC);
480
+ case THORChain:
481
+ return new CryptoAmount(baseAmount(2000000), AssetRuneNative);
482
+ case MAYAChain:
483
+ return new CryptoAmount(baseAmount(2000000), AssetMAYA);
484
+ }
485
+ throw new Error(`could not calculate outbound fee for ${asset.chain}`);
486
+ };
487
+
488
+ /**
489
+ * https://dev.thorchain.org/thorchain-dev/interface-guide/math#lp-units-add
490
+ * @param liquidity - asset amount added
491
+ * @param pool - pool depths
492
+ * @returns liquidity units - ownership of pool
493
+ */
494
+ const getLiquidityUnits = (liquidity, pool) => {
495
+ const baseAmount8decimals = getBaseAmountWithDiffDecimals(liquidity.asset, 8);
496
+ const P = new BigNumber(pool.pool.liquidityUnits);
497
+ const r = liquidity.rune.baseAmount.amount();
498
+ const a = baseAmount8decimals;
499
+ const R = pool.runeBalance.amount();
500
+ const A = pool.assetBalance.amount();
501
+ const part1 = R.times(a);
502
+ const part2 = r.times(A);
503
+ const numerator = P.times(part1.plus(part2));
504
+ const denominator = R.times(A).times(2);
505
+ const result = numerator.div(denominator);
506
+ return result;
507
+ };
508
+ /**
509
+ *
510
+ * @param unitData - units for both asset and rune
511
+ * @param pool - pool that the asset is bound to
512
+ * @returns - pool share of both asset and rune in percentage
513
+ */
514
+ const getPoolShare = (unitData, pool) => {
515
+ // formula: (rune * part) / total; (asset * part) / total
516
+ const units = unitData.liquidityUnits;
517
+ const total = unitData.totalUnits;
518
+ const R = pool.runeBalance.amount();
519
+ const T = pool.assetBalance.amount();
520
+ const asset = T.times(units).div(total);
521
+ const rune = R.times(units).div(total);
522
+ const poolShareDetail = {
523
+ assetShare: new CryptoAmount(baseAmount(asset), pool.asset),
524
+ runeShare: new CryptoAmount(baseAmount(rune), AssetRuneNative),
525
+ };
526
+ return poolShareDetail;
527
+ };
528
+ /**
529
+ *
530
+ * @param poolShare - the share of asset and rune added to the pool
531
+ * @param pool - Pool that the asset is attached to
532
+ * @returns - returns bignumber representing a slip percentage
533
+ */
534
+ const getSlipOnLiquidity = (stake, pool) => {
535
+ const baseAmount8decimals = getBaseAmountWithDiffDecimals(stake.asset, 8);
536
+ // formula: (t * R - T * r)/ (T*r + R*T)
537
+ const r = stake.rune.baseAmount.amount();
538
+ const t = baseAmount8decimals;
539
+ const R = pool.runeBalance.amount();
540
+ const T = pool.assetBalance.amount();
541
+ const numerator = t.times(R).minus(T.times(r));
542
+ const denominator = T.times(r).plus(R.times(T));
543
+ const result = numerator.div(denominator).abs();
544
+ return result;
545
+ };
546
+ /**
547
+ * https://docs.thorchain.org/thorchain-finance/continuous-liquidity-pools#impermanent-loss-protection
548
+ * @param poolShare - the share of asset and rune added to the pool
549
+ * @param pool - Pool that the asset is attached to
550
+ * @param block - blockl object with current, last added and the constant blocksforlossProtection
551
+ * @returns
552
+ */
553
+ // Blocks for full protection 1440000 // 100 days
554
+ const getLiquidityProtectionData = (depositValue, poolShare, block) => {
555
+ //Coverage formula coverage=((A0∗P1)+R0)−((A1∗P1)+R1)=>((A0∗R1/A1)+R0)−(R1+R1)
556
+ //formula: protectionProgress (currentHeight-heightLastAdded)/blocksforfullprotection
557
+ const R0 = depositValue.rune.amount(); // rune deposit value
558
+ const A0 = depositValue.asset.amount(); // asset deposit value
559
+ const R1 = poolShare.runeShare.baseAmount.amount(); // rune amount to redeem
560
+ const A1 = poolShare.assetShare.baseAmount.amount(); // asset amount to redeem
561
+ const P1 = R1.div(A1); // Pool ratio at withdrawal
562
+ const part1 = A0.times(P1).plus(R0).minus(A1.times(P1).plus(R1)); // start position minus end position
563
+ const part2 = A0.times(R1.div(A1)).plus(R0).minus(R1.plus(R1)); // different way to check position
564
+ const coverage = part1 >= part2 ? part1 : part2; // Coverage represents how much ILP a LP is entitled to
565
+ const currentHeight = block.current;
566
+ const heightLastAdded = block.lastAdded || 0; //default to zero if undefined
567
+ const blocksforfullprotection = block.fullProtection;
568
+ const fractionOfFullILPProtection = (currentHeight - heightLastAdded) / blocksforfullprotection;
569
+ const protectionProgress = Math.min(fractionOfFullILPProtection, 1); // percentage of entitlement, max 100%
570
+ const result = coverage.times(protectionProgress); // impermanent loss protection result
571
+ const maxILP = result.lt(0) ? new BigNumber(0) : result; // max negative ILP to 0
572
+ const ILProtection = {
573
+ ILProtection: new CryptoAmount(baseAmount(maxILP), AssetRuneNative),
574
+ totalDays: (fractionOfFullILPProtection * 100).toFixed(2),
575
+ };
576
+ return ILProtection;
577
+ };
578
+
579
+ const defaultMidgardConfig = {
580
+ mainnet: {
581
+ apiRetries: 3,
582
+ midgardBaseUrls: ['https://midgard.ninerealms.com'],
583
+ },
584
+ stagenet: {
585
+ apiRetries: 3,
586
+ midgardBaseUrls: ['https://stagenet-midgard.ninerealms.com'],
587
+ },
588
+ testnet: {
589
+ apiRetries: 3,
590
+ midgardBaseUrls: ['https://testnet.midgard.thorchain.info'],
591
+ },
592
+ };
593
+ class Midgard {
594
+ constructor(network = Network.Mainnet, config) {
595
+ this.network = network;
596
+ this.config = config !== null && config !== void 0 ? config : defaultMidgardConfig[this.network];
597
+ axiosRetry(axios, { retries: this.config.apiRetries, retryDelay: axiosRetry.exponentialDelay });
598
+ this.midgardApis = this.config.midgardBaseUrls.map((url) => new MidgardApi(new Configuration({ basePath: url })));
599
+ }
600
+ /**
601
+ *
602
+ * @returns an array of Pools
603
+ */
604
+ getPools() {
605
+ return __awaiter(this, void 0, void 0, function* () {
606
+ for (const api of this.midgardApis) {
607
+ try {
608
+ return (yield api.getPools()).data;
609
+ }
610
+ catch (e) {
611
+ //console.error(e)
612
+ }
613
+ }
614
+ throw new Error(`Midgard not responding`);
615
+ });
616
+ }
617
+ /**
618
+ * Gets the latest block using the Health endpoint within Midgard
619
+ *
620
+ * @returns
621
+ */
622
+ getLatestBlockHeight() {
623
+ return __awaiter(this, void 0, void 0, function* () {
624
+ for (const api of this.midgardApis) {
625
+ try {
626
+ const data = (yield api.getHealth()).data;
627
+ return +data.scannerHeight;
628
+ }
629
+ catch (e) {
630
+ //console.error(e)
631
+ }
632
+ }
633
+ throw Error(`Midgard not responding`);
634
+ });
635
+ }
636
+ /**
637
+ * Gets actions object for any of the parameters
638
+ * @param txHash transaction id
639
+ * @returns Type Action array of objects
640
+ */
641
+ getActions(address, txid, asset, type, affiliate, limit, offset) {
642
+ return __awaiter(this, void 0, void 0, function* () {
643
+ for (const api of this.midgardApis) {
644
+ try {
645
+ const actions = (yield api.getActions(address, txid, asset, type, affiliate, limit, offset)).data.actions;
646
+ return actions;
647
+ }
648
+ catch (e) {
649
+ //console.error(e)
650
+ }
651
+ }
652
+ throw Error(`Midgard not responding`);
653
+ });
654
+ }
655
+ /**
656
+ * Function to return member details based on valid liquidity position
657
+ * @param address - needed to query for Lp details
658
+ * @returns - object type of Member Detail
659
+ */
660
+ getMember(address) {
661
+ return __awaiter(this, void 0, void 0, function* () {
662
+ for (const api of this.midgardApis) {
663
+ try {
664
+ const memberDetail = (yield api.getMemberDetail(address)).data;
665
+ return memberDetail;
666
+ }
667
+ catch (e) {
668
+ //console.error(e)
669
+ }
548
670
  }
549
- else {
550
- return new CryptoAmount(gasRateinAVAXWei.times(70000), AssetAVAX);
671
+ throw Error(`Midgard not responding`);
672
+ });
673
+ }
674
+ /**
675
+ * Function to return pool statistics for a particular asset
676
+ * @param asset - asset string to query its pool stats
677
+ * @returns - type object poolstatsDetail
678
+ */
679
+ getPoolStats(asset) {
680
+ return __awaiter(this, void 0, void 0, function* () {
681
+ for (const api of this.midgardApis) {
682
+ try {
683
+ const poolDetail = (yield api.getPoolStats(asset)).data;
684
+ return poolDetail;
685
+ }
686
+ catch (e) {
687
+ //console.error(e)
688
+ }
551
689
  }
552
- case GAIAChain:
553
- return new CryptoAmount(baseAmount(inbound.gasRate), AssetATOM);
554
- case THORChain:
555
- return new CryptoAmount(baseAmount(2000000), AssetRuneNative);
690
+ throw Error(`Midgard not responding`);
691
+ });
556
692
  }
557
- throw new Error(`could not calculate inbound fee for ${asset.chain}`);
558
- };
559
- /**
560
- * Works out the required outbound fee based on the chain.
561
- * Call getInboundDetails to get the current outbound fee
562
- *
563
- * @param sourceAsset
564
- * @param inbound detail
565
- * @see https://dev.thorchain.org/thorchain-dev/thorchain-and-fees#fee-calcuation-by-chain
566
- * @returns
567
- */
568
- const calcOutboundFee = (asset, inbound) => {
569
- if (asset.synth)
570
- return new CryptoAmount(baseAmount(2000000), AssetRuneNative);
571
- switch (asset.chain) {
572
- case BTCChain:
573
- return new CryptoAmount(baseAmount(inbound.outboundFee), AssetBTC);
574
- case BCHChain:
575
- return new CryptoAmount(baseAmount(inbound.outboundFee), AssetBCH);
576
- case LTCChain:
577
- return new CryptoAmount(baseAmount(inbound.outboundFee), AssetLTC);
578
- case DOGEChain:
579
- // NOTE: UTXO chains estimate fees with a 250 byte size
580
- return new CryptoAmount(baseAmount(inbound.outboundFee), AssetDOGE);
581
- case BNBChain:
582
- //flat fee
583
- return new CryptoAmount(baseAmount(inbound.outboundFee), AssetBNB);
584
- case ETHChain:
585
- return new CryptoAmount(baseAmount(inbound.outboundFee.multipliedBy(Math.pow(10, 9)), 18), AssetETH);
586
- case AVAXChain:
587
- return new CryptoAmount(baseAmount(inbound.outboundFee.multipliedBy(Math.pow(10, 9)), 18), AssetAVAX);
588
- case GAIAChain:
589
- return new CryptoAmount(baseAmount(inbound.outboundFee), AssetATOM);
590
- case THORChain:
591
- return new CryptoAmount(baseAmount(2000000), AssetRuneNative);
693
+ /**
694
+ * Function to return THORNameDetails for a particular name
695
+ * @param name - thorname string to query
696
+ * @returns - type object THORNameDetails
697
+ */
698
+ getTHORNameDetails(name) {
699
+ return __awaiter(this, void 0, void 0, function* () {
700
+ for (const api of this.midgardApis) {
701
+ try {
702
+ const resp = yield api.getTHORNameDetail(name);
703
+ if (resp.status == 404) {
704
+ return undefined;
705
+ }
706
+ else if (resp.status == 200) {
707
+ return resp.data;
708
+ }
709
+ }
710
+ catch (e) {
711
+ // if (resp.status == 404) {
712
+ // return undefined
713
+ // }
714
+ //console.error(e)
715
+ }
716
+ }
717
+ throw Error(`Midgard not responding`);
718
+ });
592
719
  }
593
- throw new Error(`could not calculate outbound fee for ${asset.chain}`);
594
- };
720
+ }
595
721
 
596
722
  const defaultThornodeConfig = {
597
723
  mainnet: {
@@ -1356,97 +1482,6 @@ class ThorchainCache {
1356
1482
  }
1357
1483
  }
1358
1484
 
1359
- /**
1360
- * https://dev.thorchain.org/thorchain-dev/interface-guide/math#lp-units-add
1361
- * @param liquidity - asset amount added
1362
- * @param pool - pool depths
1363
- * @returns liquidity units - ownership of pool
1364
- */
1365
- const getLiquidityUnits = (liquidity, pool) => {
1366
- const baseAmount8decimals = getBaseAmountWithDiffDecimals(liquidity.asset, 8);
1367
- const P = new BigNumber(pool.pool.liquidityUnits);
1368
- const r = liquidity.rune.baseAmount.amount();
1369
- const a = baseAmount8decimals;
1370
- const R = pool.runeBalance.amount();
1371
- const A = pool.assetBalance.amount();
1372
- const part1 = R.times(a);
1373
- const part2 = r.times(A);
1374
- const numerator = P.times(part1.plus(part2));
1375
- const denominator = R.times(A).times(2);
1376
- const result = numerator.div(denominator);
1377
- return result;
1378
- };
1379
- /**
1380
- *
1381
- * @param unitData - units for both asset and rune
1382
- * @param pool - pool that the asset is bound to
1383
- * @returns - pool share of both asset and rune in percentage
1384
- */
1385
- const getPoolShare = (unitData, pool) => {
1386
- // formula: (rune * part) / total; (asset * part) / total
1387
- const units = unitData.liquidityUnits;
1388
- const total = unitData.totalUnits;
1389
- const R = pool.runeBalance.amount();
1390
- const T = pool.assetBalance.amount();
1391
- const asset = T.times(units).div(total);
1392
- const rune = R.times(units).div(total);
1393
- const poolShareDetail = {
1394
- assetShare: new CryptoAmount(baseAmount(asset), pool.asset),
1395
- runeShare: new CryptoAmount(baseAmount(rune), AssetRuneNative),
1396
- };
1397
- return poolShareDetail;
1398
- };
1399
- /**
1400
- *
1401
- * @param poolShare - the share of asset and rune added to the pool
1402
- * @param pool - Pool that the asset is attached to
1403
- * @returns - returns bignumber representing a slip percentage
1404
- */
1405
- const getSlipOnLiquidity = (stake, pool) => {
1406
- const baseAmount8decimals = getBaseAmountWithDiffDecimals(stake.asset, 8);
1407
- // formula: (t * R - T * r)/ (T*r + R*T)
1408
- const r = stake.rune.baseAmount.amount();
1409
- const t = baseAmount8decimals;
1410
- const R = pool.runeBalance.amount();
1411
- const T = pool.assetBalance.amount();
1412
- const numerator = t.times(R).minus(T.times(r));
1413
- const denominator = T.times(r).plus(R.times(T));
1414
- const result = numerator.div(denominator).abs();
1415
- return result;
1416
- };
1417
- /**
1418
- * https://docs.thorchain.org/thorchain-finance/continuous-liquidity-pools#impermanent-loss-protection
1419
- * @param poolShare - the share of asset and rune added to the pool
1420
- * @param pool - Pool that the asset is attached to
1421
- * @param block - blockl object with current, last added and the constant blocksforlossProtection
1422
- * @returns
1423
- */
1424
- // Blocks for full protection 1440000 // 100 days
1425
- const getLiquidityProtectionData = (depositValue, poolShare, block) => {
1426
- //Coverage formula coverage=((A0∗P1)+R0)−((A1∗P1)+R1)=>((A0∗R1/A1)+R0)−(R1+R1)
1427
- //formula: protectionProgress (currentHeight-heightLastAdded)/blocksforfullprotection
1428
- const R0 = depositValue.rune.amount(); // rune deposit value
1429
- const A0 = depositValue.asset.amount(); // asset deposit value
1430
- const R1 = poolShare.runeShare.baseAmount.amount(); // rune amount to redeem
1431
- const A1 = poolShare.assetShare.baseAmount.amount(); // asset amount to redeem
1432
- const P1 = R1.div(A1); // Pool ratio at withdrawal
1433
- const part1 = A0.times(P1).plus(R0).minus(A1.times(P1).plus(R1)); // start position minus end position
1434
- const part2 = A0.times(R1.div(A1)).plus(R0).minus(R1.plus(R1)); // different way to check position
1435
- const coverage = part1 >= part2 ? part1 : part2; // Coverage represents how much ILP a LP is entitled to
1436
- const currentHeight = block.current;
1437
- const heightLastAdded = block.lastAdded || 0; //default to zero if undefined
1438
- const blocksforfullprotection = block.fullProtection;
1439
- const fractionOfFullILPProtection = (currentHeight - heightLastAdded) / blocksforfullprotection;
1440
- const protectionProgress = Math.min(fractionOfFullILPProtection, 1); // percentage of entitlement, max 100%
1441
- const result = coverage.times(protectionProgress); // impermanent loss protection result
1442
- const maxILP = result.lt(0) ? new BigNumber(0) : result; // max negative ILP to 0
1443
- const ILProtection = {
1444
- ILProtection: new CryptoAmount(baseAmount(maxILP), AssetRuneNative),
1445
- totalDays: (fractionOfFullILPProtection * 100).toFixed(2),
1446
- };
1447
- return ILProtection;
1448
- };
1449
-
1450
1485
  const BN_1 = new BigNumber(1);
1451
1486
  const defaultCache = new ThorchainCache();
1452
1487
  /**
@@ -2149,6 +2184,20 @@ class ThorchainQuery {
2149
2184
  rune: new CryptoAmount(assetToBase(assetAmount(0)), AssetRuneNative),
2150
2185
  };
2151
2186
  return dustValues;
2187
+ case 'BSC':
2188
+ // 0 BSC
2189
+ dustValues = {
2190
+ asset: new CryptoAmount(assetToBase(assetAmount(0)), asset),
2191
+ rune: new CryptoAmount(assetToBase(assetAmount(0)), AssetRuneNative),
2192
+ };
2193
+ return dustValues;
2194
+ case 'MAYA':
2195
+ // 0 MAYA
2196
+ dustValues = {
2197
+ asset: new CryptoAmount(assetToBase(assetAmount(0)), asset),
2198
+ rune: new CryptoAmount(assetToBase(assetAmount(0)), AssetRuneNative),
2199
+ };
2200
+ return dustValues;
2152
2201
  default:
2153
2202
  throw Error('Unknown chain');
2154
2203
  }
@@ -2159,7 +2208,17 @@ class ThorchainQuery {
2159
2208
  estimateAddSaver(addAmount) {
2160
2209
  return __awaiter(this, void 0, void 0, function* () {
2161
2210
  let errors = [];
2211
+ // check for errors before sending quote
2162
2212
  errors = yield this.getAddSaversEstimateErrors(addAmount);
2213
+ // request param amount should always be in 1e8 which is why we pass in adjusted decimals if chain decimals != 8
2214
+ const newAddAmount = addAmount.baseAmount.decimal != 8 ? getBaseAmountWithDiffDecimals(addAmount, 8) : addAmount.baseAmount.amount();
2215
+ // Fetch quote
2216
+ const depositQuote = yield this.thorchainCache.thornode.getSaversDepositQuote(assetToString(addAmount.asset), newAddAmount.toNumber());
2217
+ // error handling
2218
+ const response = JSON.parse(JSON.stringify(depositQuote));
2219
+ if (response.error)
2220
+ errors.push(`Thornode request quote failed: ${response.error}`);
2221
+ // Return errors if there is any
2163
2222
  if (errors.length > 0) {
2164
2223
  return {
2165
2224
  assetAmount: addAmount,
@@ -2179,29 +2238,27 @@ class ThorchainQuery {
2179
2238
  errors,
2180
2239
  };
2181
2240
  }
2182
- // request param amount should always be in 1e8 which is why we pass in adjusted decimals if chain decimals != 8
2183
- const newAddAmount = addAmount.baseAmount.decimal != 8 ? getBaseAmountWithDiffDecimals(addAmount, 8) : addAmount.baseAmount.amount();
2184
- const depositQuote = yield this.thorchainCache.thornode.getSaversDepositQuote(assetToString(addAmount.asset), newAddAmount.toNumber());
2185
2241
  // Calculate transaction expiry time of the vault address
2186
2242
  const currentDatetime = new Date();
2187
2243
  const minutesToAdd = 15;
2188
2244
  const expiryDatetime = new Date(currentDatetime.getTime() + minutesToAdd * 60000);
2245
+ // Calculate seconds
2189
2246
  const estimatedWait = depositQuote.inbound_confirmation_seconds
2190
2247
  ? depositQuote.inbound_confirmation_seconds
2191
2248
  : yield this.confCounting(addAmount);
2192
2249
  const pool = (yield this.thorchainCache.getPoolForAsset(addAmount.asset)).pool;
2193
- if (addAmount.baseAmount.lte(depositQuote.expected_amount_out))
2194
- errors.push(`Amount being added to savers can't pay for fees`);
2250
+ // Organise fees
2195
2251
  const saverFees = {
2196
2252
  affiliate: new CryptoAmount(baseAmount(depositQuote.fees.affiliate), addAmount.asset),
2197
2253
  asset: assetFromStringEx(depositQuote.fees.asset),
2198
2254
  outbound: new CryptoAmount(baseAmount(depositQuote.fees.outbound), addAmount.asset),
2199
2255
  };
2256
+ // define savers cap
2200
2257
  const saverCap = 0.3 * +pool.assetDepth;
2201
2258
  const saverCapFilledPercent = (+pool.saversDepth / saverCap) * 100;
2202
2259
  const estimateAddSaver = {
2203
2260
  assetAmount: new CryptoAmount(baseAmount(depositQuote.expected_amount_out), addAmount.asset),
2204
- estimatedDepositValue: new CryptoAmount(baseAmount(depositQuote.expected_amount_out), saverFees.asset),
2261
+ estimatedDepositValue: new CryptoAmount(baseAmount(depositQuote.expected_amount_deposit), addAmount.asset),
2205
2262
  fee: saverFees,
2206
2263
  expiry: expiryDatetime,
2207
2264
  toAddress: depositQuote.inbound_address,
@@ -2222,12 +2279,56 @@ class ThorchainQuery {
2222
2279
  */
2223
2280
  estimateWithdrawSaver(withdrawParams) {
2224
2281
  return __awaiter(this, void 0, void 0, function* () {
2282
+ const errors = [];
2283
+ // return error if Asset in is incorrect
2225
2284
  if (isAssetRuneNative(withdrawParams.asset) || withdrawParams.asset.synth)
2226
- throw Error(`Native Rune and synth assets are not supported only L1's`);
2285
+ errors.push(`Native Rune and synth assets are not supported only L1's`);
2286
+ const inboundDetails = yield this.thorchainCache.getInboundDetails();
2287
+ // Check to see if there is a position before calling withdraw quote
2288
+ const checkPositon = yield this.getSaverPosition(withdrawParams);
2289
+ if (checkPositon.errors.length > 0) {
2290
+ for (let i = 0; i < checkPositon.errors.length; i++) {
2291
+ errors.push(checkPositon.errors[i]);
2292
+ }
2293
+ return {
2294
+ expectedAssetAmount: new CryptoAmount(assetToBase(assetAmount(checkPositon.redeemableValue.assetAmount.amount())), withdrawParams.asset),
2295
+ fee: {
2296
+ affiliate: new CryptoAmount(assetToBase(assetAmount(0)), withdrawParams.asset),
2297
+ asset: withdrawParams.asset,
2298
+ outbound: new CryptoAmount(assetToBase(assetAmount(calcOutboundFee(withdrawParams.asset, inboundDetails[withdrawParams.asset.chain]).assetAmount.amount())), withdrawParams.asset),
2299
+ },
2300
+ expiry: new Date(0),
2301
+ toAddress: '',
2302
+ memo: '',
2303
+ estimatedWaitTime: -1,
2304
+ slipBasisPoints: -1,
2305
+ dustAmount: new CryptoAmount(baseAmount(0), withdrawParams.asset),
2306
+ errors,
2307
+ };
2308
+ }
2309
+ // Request withdraw quote
2227
2310
  const withdrawQuote = yield this.thorchainCache.thornode.getSaversWithdrawQuote(withdrawParams);
2228
- if (!withdrawQuote.expected_amount_out)
2229
- throw Error(`Could not quote withdrawal ${JSON.stringify(withdrawQuote)}`);
2230
- // const pool = (await this.thorchainCache.getPoolForAsset(withdrawParams.asset)).pool
2311
+ // error handling
2312
+ const response = JSON.parse(JSON.stringify(withdrawQuote));
2313
+ if (response.error)
2314
+ errors.push(`Thornode request quote failed: ${response.error}`);
2315
+ if (errors.length > 0) {
2316
+ return {
2317
+ expectedAssetAmount: new CryptoAmount(assetToBase(assetAmount(0)), withdrawParams.asset),
2318
+ fee: {
2319
+ affiliate: new CryptoAmount(assetToBase(assetAmount(0)), withdrawParams.asset),
2320
+ asset: withdrawParams.asset,
2321
+ outbound: new CryptoAmount(assetToBase(assetAmount(0)), withdrawParams.asset),
2322
+ },
2323
+ expiry: new Date(0),
2324
+ toAddress: '',
2325
+ memo: '',
2326
+ estimatedWaitTime: -1,
2327
+ slipBasisPoints: -1,
2328
+ dustAmount: new CryptoAmount(baseAmount(0), withdrawParams.asset),
2329
+ errors,
2330
+ };
2331
+ }
2231
2332
  // Calculate transaction expiry time of the vault address
2232
2333
  const currentDatetime = new Date();
2233
2334
  const minutesToAdd = 15;
@@ -2247,6 +2348,7 @@ class ThorchainQuery {
2247
2348
  estimatedWaitTime: estimatedWait,
2248
2349
  slipBasisPoints: withdrawQuote.slippage_bps,
2249
2350
  dustAmount: new CryptoAmount(baseAmount(withdrawQuote.dust_amount), withdrawParams.asset),
2351
+ errors,
2250
2352
  };
2251
2353
  return estimateWithdrawSaver;
2252
2354
  });
@@ -2258,31 +2360,37 @@ class ThorchainQuery {
2258
2360
  */
2259
2361
  getSaverPosition(params) {
2260
2362
  return __awaiter(this, void 0, void 0, function* () {
2363
+ const errors = [];
2364
+ const inboundDetails = yield this.thorchainCache.getInboundDetails();
2261
2365
  const blockData = (yield this.thorchainCache.thornode.getLastBlock()).find((item) => item.chain === params.asset.chain);
2262
2366
  const savers = (yield this.thorchainCache.thornode.getSavers(`${params.asset.chain}.${params.asset.ticker}`)).find((item) => item.asset_address === params.address);
2263
2367
  const pool = (yield this.thorchainCache.getPoolForAsset(params.asset)).pool;
2264
2368
  if (!savers)
2265
- throw Error(`Could not find position for ${params.address}`);
2266
- if (!savers.last_add_height)
2267
- throw Error(`Could not find position for ${params.address}`);
2369
+ errors.push(`Could not find position for ${params.address}`);
2370
+ if (!(savers === null || savers === void 0 ? void 0 : savers.last_add_height))
2371
+ errors.push(`Could not find position for ${params.address}`);
2268
2372
  if (!(blockData === null || blockData === void 0 ? void 0 : blockData.thorchain))
2269
- throw Error(`Could not get thorchain block height`);
2270
- const ownerUnits = Number(savers.units);
2271
- const lastAdded = Number(savers.last_add_height);
2373
+ errors.push(`Could not get thorchain block height`);
2374
+ const outboundFee = yield calcOutboundFee(params.asset, inboundDetails[params.asset.chain]);
2375
+ if (Number(savers === null || savers === void 0 ? void 0 : savers.asset_redeem_value) < outboundFee.baseAmount.amount().toNumber())
2376
+ errors.push(`Unlikely to withdraw balance as outbound fee is greater than redeemable amount`);
2377
+ const ownerUnits = Number(savers === null || savers === void 0 ? void 0 : savers.units);
2378
+ const lastAdded = Number(savers === null || savers === void 0 ? void 0 : savers.last_add_height);
2272
2379
  const saverUnits = Number(pool.saversUnits);
2273
2380
  const assetDepth = Number(pool.saversDepth);
2274
2381
  const redeemableValue = (ownerUnits / saverUnits) * assetDepth;
2275
- const depositAmount = new CryptoAmount(baseAmount(savers.asset_deposit_value), params.asset);
2382
+ const depositAmount = new CryptoAmount(baseAmount(savers === null || savers === void 0 ? void 0 : savers.asset_deposit_value), params.asset);
2276
2383
  const redeemableAssetAmount = new CryptoAmount(baseAmount(redeemableValue), params.asset);
2277
- const saversAge = ((blockData === null || blockData === void 0 ? void 0 : blockData.thorchain) - lastAdded) / ((365 * 86400) / 6);
2384
+ const saversAge = (Number(blockData === null || blockData === void 0 ? void 0 : blockData.thorchain) - lastAdded) / ((365 * 86400) / 6);
2278
2385
  const saverGrowth = redeemableAssetAmount.minus(depositAmount).div(depositAmount).times(100);
2279
2386
  const saversPos = {
2280
2387
  depositValue: depositAmount,
2281
2388
  redeemableValue: redeemableAssetAmount,
2282
- lastAddHeight: savers.last_add_height,
2389
+ lastAddHeight: Number(savers === null || savers === void 0 ? void 0 : savers.last_add_height),
2283
2390
  percentageGrowth: saverGrowth.assetAmount.amount().toNumber(),
2284
2391
  ageInYears: saversAge,
2285
2392
  ageInDays: saversAge * 365,
2393
+ errors,
2286
2394
  };
2287
2395
  return saversPos;
2288
2396
  });
@@ -2301,6 +2409,9 @@ class ThorchainQuery {
2301
2409
  const pool = (yield this.thorchainCache.getPoolForAsset(addAmount.asset)).pool;
2302
2410
  if (pool.status.toLowerCase() !== 'available')
2303
2411
  errors.push(`Pool is not available for this asset ${assetToString(addAmount.asset)}`);
2412
+ const inboundFee = calcNetworkFee(addAmount.asset, inboundDetails[addAmount.asset.chain]);
2413
+ if (addAmount.lte(inboundFee))
2414
+ errors.push(`Add amount does not cover fees`);
2304
2415
  return errors;
2305
2416
  });
2306
2417
  }
@@ -2740,4 +2851,4 @@ class TransactionStage {
2740
2851
  }
2741
2852
  }
2742
2853
 
2743
- export { AddLpStatus, AddSaverStatus, CryptoAmount, InboundStatus, LiquidityPool, Midgard, RefundStatus, SwapStatus, ThorchainCache, ThorchainQuery, Thornode, TransactionStage, TxType, WithdrawStatus, calcNetworkFee, getDoubleSwap, getLiquidityProtectionData, getLiquidityUnits, getPoolShare, getSingleSwap, getSlipOnLiquidity };
2854
+ export { AVAXChain, AddLpStatus, AddSaverStatus, AssetATOM, AssetAVAX, AssetBCH, AssetBNB, AssetBSC, AssetBTC, AssetDOGE, AssetETH, AssetLTC, AssetMAYA, AssetRuneNative, BCHChain, BNBChain, BSCChain, BTCChain, CryptoAmount, DOGEChain, ETHChain, GAIAChain, InboundStatus, LTCChain, LiquidityPool, MAYAChain, Midgard, RefundStatus, SwapStatus, THORChain, ThorchainCache, ThorchainQuery, Thornode, TransactionStage, TxType, WithdrawStatus, calcNetworkFee, getDoubleSwap, getLiquidityProtectionData, getLiquidityUnits, getPoolShare, getSingleSwap, getSlipOnLiquidity, isAssetRuneNative };