@xchainjs/xchain-thorchain-query 0.1.4 → 0.1.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/README.md +3 -1
- package/lib/index.esm.js +103 -103
- package/lib/index.js +103 -103
- package/lib/thorchain-cache.d.ts +1 -16
- package/lib/thorchain-query.d.ts +2 -1
- package/lib/types.d.ts +5 -9
- package/lib/utils/swap.d.ts +10 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,11 +43,13 @@ Estimation example from a swap of 2 BTC to RUNE
|
|
|
43
43
|
|
|
44
44
|
## Documentation
|
|
45
45
|
|
|
46
|
+
[`Overview `](https://dev.thorchain.org/thorchain-dev/xchainjs-integration-guide/query-package)
|
|
47
|
+
|
|
46
48
|
For bash exmples, see example folder at the base of this repository xchainjs/xchainjs-lib.
|
|
47
49
|
|
|
48
50
|
### [`xchain-thorchain-query`](http://docs.xchainjs.org/xchain-thorchain-query/)
|
|
49
51
|
|
|
50
|
-
[`How xchain-thorchain-query works`](http://docs.xchainjs.org/xchain-thorchain-query/how-it-works.html)
|
|
52
|
+
[`How xchain-thorchain-query works`](http://docs.xchainjs.org/xchain-thorchain-query/how-it-works.html)
|
|
51
53
|
[`How to use xchain-thorchain-query`](http://docs.xchainjs.org/xchain-thorchain-query/how-to-use.html)
|
|
52
54
|
|
|
53
55
|
For Live examples
|
package/lib/index.esm.js
CHANGED
|
@@ -427,6 +427,7 @@ const getBaseAmountWithDiffDecimals = (inputAmount, outDecimals) => {
|
|
|
427
427
|
const getSwapFee = (inputAmount, pool, toRune) => {
|
|
428
428
|
// formula: (x * x * Y) / (x + X) ^ 2
|
|
429
429
|
// const isInputRune = isAssetRuneNative(inputAmount.asset)
|
|
430
|
+
const decimalsOut = pool.pool.nativeDecimal !== '-1' ? Number(pool.pool.nativeDecimal) : inputAmount.baseAmount.decimal;
|
|
430
431
|
const x = getBaseAmountWithDiffDecimals(inputAmount, 8);
|
|
431
432
|
const X = toRune ? pool.assetBalance.amount() : pool.runeBalance.amount(); // input is asset if toRune
|
|
432
433
|
const Y = toRune ? pool.runeBalance.amount() : pool.assetBalance.amount(); // output is rune if toRune
|
|
@@ -435,7 +436,7 @@ const getSwapFee = (inputAmount, pool, toRune) => {
|
|
|
435
436
|
const denominator = x.plus(X).pow(2);
|
|
436
437
|
const result = numerator.div(denominator);
|
|
437
438
|
const eightDecimalResult = new CryptoAmount(baseAmount(result), units);
|
|
438
|
-
const decimals = toRune ? 8 :
|
|
439
|
+
const decimals = toRune ? 8 : decimalsOut;
|
|
439
440
|
const baseOut = getBaseAmountWithDiffDecimals(eightDecimalResult, decimals);
|
|
440
441
|
const swapFee = new CryptoAmount(baseAmount(baseOut, decimals), units);
|
|
441
442
|
//console.log(` swapFee ${swapFee.assetAmountFixedString()} `)
|
|
@@ -465,6 +466,7 @@ const getSwapSlip = (inputAmount, pool, toRune) => {
|
|
|
465
466
|
*/
|
|
466
467
|
const getSwapOutput = (inputAmount, pool, toRune) => {
|
|
467
468
|
// formula: (x * X * Y) / (x + X) ^ 2
|
|
469
|
+
const decimalsOut = pool.pool.nativeDecimal !== '-1' ? Number(pool.pool.nativeDecimal) : inputAmount.baseAmount.decimal;
|
|
468
470
|
const x = getBaseAmountWithDiffDecimals(inputAmount, 8);
|
|
469
471
|
const X = toRune ? pool.assetBalance.amount() : pool.runeBalance.amount(); // input is asset if toRune
|
|
470
472
|
const Y = toRune ? pool.runeBalance.amount() : pool.assetBalance.amount(); // output is rune if toRune
|
|
@@ -474,7 +476,7 @@ const getSwapOutput = (inputAmount, pool, toRune) => {
|
|
|
474
476
|
const denominator = x.plus(X).pow(2);
|
|
475
477
|
const result = numerator.div(denominator);
|
|
476
478
|
const eightDecimalResult = new CryptoAmount(baseAmount(result), units);
|
|
477
|
-
const decimals = toRune ? 8 :
|
|
479
|
+
const decimals = toRune ? 8 : decimalsOut;
|
|
478
480
|
const baseOut = getBaseAmountWithDiffDecimals(eightDecimalResult, decimals);
|
|
479
481
|
return new CryptoAmount(baseAmount(baseOut, decimals), units);
|
|
480
482
|
};
|
|
@@ -585,6 +587,42 @@ const calcNetworkFee = (asset, inbound) => {
|
|
|
585
587
|
}
|
|
586
588
|
throw new Error(`could not calculate inbound fee for ${asset.chain}`);
|
|
587
589
|
};
|
|
590
|
+
/**
|
|
591
|
+
* Works out the required outbound fee based on the chain.
|
|
592
|
+
* Call getInboundDetails to get the current outbound fee
|
|
593
|
+
*
|
|
594
|
+
* @param sourceAsset
|
|
595
|
+
* @param inbound detail
|
|
596
|
+
* @see https://dev.thorchain.org/thorchain-dev/thorchain-and-fees#fee-calcuation-by-chain
|
|
597
|
+
* @returns
|
|
598
|
+
*/
|
|
599
|
+
const calcOutboundFee = (asset, inbound) => {
|
|
600
|
+
if (asset.synth)
|
|
601
|
+
return new CryptoAmount(baseAmount(2000000), AssetRuneNative);
|
|
602
|
+
switch (asset.chain) {
|
|
603
|
+
case Chain.Bitcoin:
|
|
604
|
+
return new CryptoAmount(baseAmount(inbound.outboundFee), AssetBTC);
|
|
605
|
+
case Chain.BitcoinCash:
|
|
606
|
+
return new CryptoAmount(baseAmount(inbound.outboundFee), AssetBCH);
|
|
607
|
+
case Chain.Litecoin:
|
|
608
|
+
return new CryptoAmount(baseAmount(inbound.outboundFee), AssetLTC);
|
|
609
|
+
case Chain.Doge:
|
|
610
|
+
// NOTE: UTXO chains estimate fees with a 250 byte size
|
|
611
|
+
return new CryptoAmount(baseAmount(inbound.outboundFee), AssetDOGE);
|
|
612
|
+
case Chain.Binance:
|
|
613
|
+
//flat fee
|
|
614
|
+
return new CryptoAmount(baseAmount(inbound.outboundFee), AssetBNB);
|
|
615
|
+
case Chain.Ethereum:
|
|
616
|
+
return new CryptoAmount(baseAmount(inbound.outboundFee.multipliedBy(Math.pow(10, 9)), 18), AssetETH);
|
|
617
|
+
case Chain.Avalanche:
|
|
618
|
+
return new CryptoAmount(baseAmount(inbound.outboundFee.multipliedBy(Math.pow(10, 9)), 18), AssetAVAX);
|
|
619
|
+
case Chain.Cosmos:
|
|
620
|
+
return new CryptoAmount(baseAmount(inbound.outboundFee), AssetAtom);
|
|
621
|
+
case Chain.THORChain:
|
|
622
|
+
return new CryptoAmount(baseAmount(2000000), AssetRuneNative);
|
|
623
|
+
}
|
|
624
|
+
throw new Error(`could not calculate outbound fee for ${asset.chain}`);
|
|
625
|
+
};
|
|
588
626
|
/**
|
|
589
627
|
* Return the chain for a given Asset This method should live somewhere else.
|
|
590
628
|
* @param chain
|
|
@@ -829,14 +867,12 @@ class ThorchainCache {
|
|
|
829
867
|
* @param expireNetworkValuesCacheMillis - how long should the Mimir/Constants be cached before expiry
|
|
830
868
|
* @returns ThorchainCache
|
|
831
869
|
*/
|
|
832
|
-
constructor(midgard = defaultMidgard, thornode = defaultThornode, expirePoolCacheMillis = 6000,
|
|
833
|
-
this.asgardAssetsCache = undefined;
|
|
870
|
+
constructor(midgard = defaultMidgard, thornode = defaultThornode, expirePoolCacheMillis = 6000, expireInboundDetailsCacheMillis = 6000, expireNetworkValuesCacheMillis = TEN_MINUTES) {
|
|
834
871
|
this.inboundDetailCache = undefined;
|
|
835
872
|
this.networkValuesCache = undefined;
|
|
836
873
|
this.midgard = midgard;
|
|
837
874
|
this.thornode = thornode;
|
|
838
875
|
this.expirePoolCacheMillis = expirePoolCacheMillis;
|
|
839
|
-
this.expireAsgardCacheMillis = expireAsgardCacheMillis;
|
|
840
876
|
this.expireInboundDetailsCacheMillis = expireInboundDetailsCacheMillis;
|
|
841
877
|
this.expireNetworkValuesCacheMillis = expireNetworkValuesCacheMillis;
|
|
842
878
|
//initialize the cache
|
|
@@ -946,29 +982,6 @@ class ThorchainCache {
|
|
|
946
982
|
}
|
|
947
983
|
});
|
|
948
984
|
}
|
|
949
|
-
/**
|
|
950
|
-
* Refreshes the asgardAssetsCache Cache
|
|
951
|
-
*
|
|
952
|
-
* NOTE: do not call refereshAsgardCache() directly, call getAsgardAssets() instead
|
|
953
|
-
* which will refresh the cache if it's expired
|
|
954
|
-
*/
|
|
955
|
-
refereshAsgardCache() {
|
|
956
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
957
|
-
const inboundAddresses = yield this.thornode.getInboundAddresses();
|
|
958
|
-
const map = {};
|
|
959
|
-
if (inboundAddresses) {
|
|
960
|
-
for (const inboundAddress of inboundAddresses) {
|
|
961
|
-
if (!inboundAddress.chain)
|
|
962
|
-
throw Error('chain needed');
|
|
963
|
-
map[inboundAddress.chain] = inboundAddress;
|
|
964
|
-
}
|
|
965
|
-
this.asgardAssetsCache = {
|
|
966
|
-
lastRefreshed: Date.now(),
|
|
967
|
-
inboundAddresses: map,
|
|
968
|
-
};
|
|
969
|
-
}
|
|
970
|
-
});
|
|
971
|
-
}
|
|
972
985
|
/**
|
|
973
986
|
* Refreshes the InboundDetailCache Cache
|
|
974
987
|
*
|
|
@@ -1055,22 +1068,26 @@ class ThorchainCache {
|
|
|
1055
1068
|
*/
|
|
1056
1069
|
getExpectedSwapOutput(inputAmount, destinationAsset) {
|
|
1057
1070
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1071
|
+
let swapOutput;
|
|
1058
1072
|
if (isAssetRuneNative(inputAmount.asset)) {
|
|
1059
1073
|
//singleswap from rune -> asset
|
|
1060
1074
|
const pool = yield this.getPoolForAsset(destinationAsset);
|
|
1061
|
-
|
|
1075
|
+
swapOutput = getSingleSwap(inputAmount, pool, false);
|
|
1062
1076
|
}
|
|
1063
1077
|
else if (isAssetRuneNative(destinationAsset)) {
|
|
1064
1078
|
//singleswap from asset -> rune
|
|
1065
1079
|
const pool = yield this.getPoolForAsset(inputAmount.asset);
|
|
1066
|
-
|
|
1080
|
+
swapOutput = getSingleSwap(inputAmount, pool, true);
|
|
1067
1081
|
}
|
|
1068
1082
|
else {
|
|
1069
1083
|
//doubleswap asset-> asset
|
|
1070
1084
|
const inPool = yield this.getPoolForAsset(inputAmount.asset);
|
|
1071
1085
|
const destPool = yield this.getPoolForAsset(destinationAsset);
|
|
1072
|
-
|
|
1086
|
+
swapOutput = yield getDoubleSwap(inputAmount, inPool, destPool, this);
|
|
1073
1087
|
}
|
|
1088
|
+
//Note this is needed to return a synth vs. a native asset on swap out
|
|
1089
|
+
swapOutput.output = new CryptoAmount(swapOutput.output.baseAmount, destinationAsset);
|
|
1090
|
+
return swapOutput;
|
|
1074
1091
|
});
|
|
1075
1092
|
}
|
|
1076
1093
|
/**
|
|
@@ -1113,37 +1130,13 @@ class ThorchainCache {
|
|
|
1113
1130
|
}
|
|
1114
1131
|
getRouterAddressForChain(chain) {
|
|
1115
1132
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1116
|
-
const inboundAsgard = (yield this.
|
|
1133
|
+
const inboundAsgard = (yield this.getInboundDetails())[chain];
|
|
1117
1134
|
if (!(inboundAsgard === null || inboundAsgard === void 0 ? void 0 : inboundAsgard.router)) {
|
|
1118
1135
|
throw new Error('router address is not defined');
|
|
1119
1136
|
}
|
|
1120
1137
|
return inboundAsgard === null || inboundAsgard === void 0 ? void 0 : inboundAsgard.router;
|
|
1121
1138
|
});
|
|
1122
1139
|
}
|
|
1123
|
-
/**
|
|
1124
|
-
*
|
|
1125
|
-
* @returns - inbound adresses item
|
|
1126
|
-
*/
|
|
1127
|
-
getInboundAddresses() {
|
|
1128
|
-
var _a;
|
|
1129
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1130
|
-
const millisSinceLastRefeshed = Date.now() - (((_a = this.asgardAssetsCache) === null || _a === void 0 ? void 0 : _a.lastRefreshed) || 0);
|
|
1131
|
-
if (millisSinceLastRefeshed > this.expireAsgardCacheMillis) {
|
|
1132
|
-
try {
|
|
1133
|
-
yield this.refereshAsgardCache();
|
|
1134
|
-
}
|
|
1135
|
-
catch (e) {
|
|
1136
|
-
console.error(e);
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
if (this.asgardAssetsCache) {
|
|
1140
|
-
return this.asgardAssetsCache.inboundAddresses;
|
|
1141
|
-
}
|
|
1142
|
-
else {
|
|
1143
|
-
throw Error(`Could not refresh refereshAsgardCache `);
|
|
1144
|
-
}
|
|
1145
|
-
});
|
|
1146
|
-
}
|
|
1147
1140
|
/**
|
|
1148
1141
|
*
|
|
1149
1142
|
* @returns - inbound details
|
|
@@ -1327,7 +1320,8 @@ class ThorchainQuery {
|
|
|
1327
1320
|
|
|
1328
1321
|
* @returns The SwapEstimate
|
|
1329
1322
|
*/
|
|
1330
|
-
estimateSwap({ input, destinationAsset, destinationAddress, slipLimit
|
|
1323
|
+
estimateSwap({ input, destinationAsset, destinationAddress, slipLimit = new BigNumber('0.03'), //default to 3%
|
|
1324
|
+
interfaceID = '555', affiliateAddress = '', affiliateFeeBasisPoints = 0, }) {
|
|
1331
1325
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1332
1326
|
yield this.isValidSwap({
|
|
1333
1327
|
input,
|
|
@@ -1335,7 +1329,7 @@ class ThorchainQuery {
|
|
|
1335
1329
|
destinationAddress,
|
|
1336
1330
|
slipLimit,
|
|
1337
1331
|
affiliateAddress,
|
|
1338
|
-
|
|
1332
|
+
affiliateFeeBasisPoints,
|
|
1339
1333
|
interfaceID,
|
|
1340
1334
|
});
|
|
1341
1335
|
const inboundDetails = yield this.thorchainCache.getInboundDetails();
|
|
@@ -1348,7 +1342,7 @@ class ThorchainQuery {
|
|
|
1348
1342
|
destinationAddress,
|
|
1349
1343
|
slipLimit,
|
|
1350
1344
|
affiliateAddress,
|
|
1351
|
-
|
|
1345
|
+
affiliateFeeBasisPoints,
|
|
1352
1346
|
interfaceID,
|
|
1353
1347
|
}, sourceInboundDetails, destinationInboundDetails);
|
|
1354
1348
|
// Calculate transaction expiry time
|
|
@@ -1362,7 +1356,7 @@ class ThorchainQuery {
|
|
|
1362
1356
|
destinationAddress,
|
|
1363
1357
|
slipLimit,
|
|
1364
1358
|
affiliateAddress,
|
|
1365
|
-
|
|
1359
|
+
affiliateFeeBasisPoints,
|
|
1366
1360
|
interfaceID,
|
|
1367
1361
|
}, swapEstimate, sourceInboundDetails, destinationInboundDetails);
|
|
1368
1362
|
const txDetails = {
|
|
@@ -1377,7 +1371,7 @@ class ThorchainQuery {
|
|
|
1377
1371
|
}
|
|
1378
1372
|
else {
|
|
1379
1373
|
txDetails.txEstimate.canSwap = true;
|
|
1380
|
-
const inboundAsgard = (yield this.thorchainCache.
|
|
1374
|
+
const inboundAsgard = (yield this.thorchainCache.getInboundDetails())[input.asset.chain];
|
|
1381
1375
|
txDetails.toAddress = (inboundAsgard === null || inboundAsgard === void 0 ? void 0 : inboundAsgard.address) || '';
|
|
1382
1376
|
// Work out LIM from the slip percentage
|
|
1383
1377
|
let limPercentage = BN_1;
|
|
@@ -1397,7 +1391,7 @@ class ThorchainQuery {
|
|
|
1397
1391
|
limit: baseAmount(limAssetAmount8Decimals),
|
|
1398
1392
|
destinationAddress: destinationAddress,
|
|
1399
1393
|
affiliateAddress: affiliateAddress,
|
|
1400
|
-
|
|
1394
|
+
affiliateFeeBasisPoints: affiliateFeeBasisPoints,
|
|
1401
1395
|
interfaceID: interfaceID,
|
|
1402
1396
|
});
|
|
1403
1397
|
}
|
|
@@ -1409,6 +1403,7 @@ class ThorchainQuery {
|
|
|
1409
1403
|
* @param params
|
|
1410
1404
|
*/
|
|
1411
1405
|
isValidSwap(params) {
|
|
1406
|
+
var _a, _b;
|
|
1412
1407
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1413
1408
|
if (isAssetRuneNative(params.input.asset)) {
|
|
1414
1409
|
if (params.input.baseAmount.decimal !== 8)
|
|
@@ -1428,8 +1423,12 @@ class ThorchainQuery {
|
|
|
1428
1423
|
if (params.input.baseAmount.lte(0))
|
|
1429
1424
|
throw Error('inputAmount must be greater than 0');
|
|
1430
1425
|
// Affiliate fee % can't exceed 10% because this is set by TC.
|
|
1431
|
-
if (params.
|
|
1432
|
-
throw Error(`
|
|
1426
|
+
if (params.affiliateFeeBasisPoints && (params.affiliateFeeBasisPoints < 0 || params.affiliateFeeBasisPoints > 1000))
|
|
1427
|
+
throw Error(`affiliateFeeBasisPoints must be between 0 and 1000 basis points`);
|
|
1428
|
+
if (params.affiliateFeeBasisPoints && !Number.isInteger(params.affiliateFeeBasisPoints))
|
|
1429
|
+
throw Error(`affiliateFeeBasisPoints must be an integer`);
|
|
1430
|
+
if (((_a = params.slipLimit) === null || _a === void 0 ? void 0 : _a.lte(0)) || ((_b = params.slipLimit) === null || _b === void 0 ? void 0 : _b.gt(1)))
|
|
1431
|
+
throw Error(`slipLimit must be between 0 and 1`);
|
|
1433
1432
|
});
|
|
1434
1433
|
}
|
|
1435
1434
|
/**
|
|
@@ -1450,51 +1449,48 @@ class ThorchainQuery {
|
|
|
1450
1449
|
const input = params.input.baseAmount.decimal === DEFAULT_THORCHAIN_DECIMALS
|
|
1451
1450
|
? params.input
|
|
1452
1451
|
: yield this.thorchainCache.convert(params.input, params.input.asset);
|
|
1453
|
-
|
|
1454
|
-
const
|
|
1455
|
-
const inboundFeeInAsset = calcNetworkFee(input.asset, sourceInboundDetails);
|
|
1456
|
-
// Retrieve outbound fee from inboundAddressDetails.
|
|
1457
|
-
const outboundFeeInAsset = calcNetworkFee(params.destinationAsset, destinationInboundDetails).times(3);
|
|
1458
|
-
// convert fees to rune
|
|
1459
|
-
const inboundFeeInRune = yield this.thorchainCache.convert(inboundFeeInAsset, AssetRuneNative);
|
|
1460
|
-
let outboundFeeInRune = yield this.thorchainCache.convert(outboundFeeInAsset, AssetRuneNative);
|
|
1452
|
+
const inboundFeeInInboundGasAsset = calcNetworkFee(input.asset, sourceInboundDetails);
|
|
1453
|
+
const outboundFeeInOutboundGasAsset = calcOutboundFee(params.destinationAsset, destinationInboundDetails);
|
|
1461
1454
|
// ----------- Remove Fees from inbound before doing the swap -----------
|
|
1462
|
-
const
|
|
1455
|
+
const inboundFeeInInboundAsset = yield this.thorchainCache.convert(inboundFeeInInboundGasAsset, params.input.asset);
|
|
1456
|
+
const inputMinusInboundFeeInAsset = input.minus(inboundFeeInInboundAsset);
|
|
1457
|
+
// console.log('y', inputMinusInboundFeeInAsset.formatedAssetString())
|
|
1463
1458
|
// remove any affiliateFee. netInput * affiliateFee (percentage) of the destination asset type
|
|
1464
|
-
const
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
const checkOutboundFee = (yield this.convert(outboundFeeInRune, usdAsset)).gte(usdMinFee);
|
|
1477
|
-
if (!checkOutboundFee) {
|
|
1478
|
-
const newFee = usdMinFee;
|
|
1479
|
-
outboundFeeInRune = yield this.convert(newFee, AssetRuneNative);
|
|
1480
|
-
}
|
|
1459
|
+
const affiliateFeePercent = params.affiliateFeeBasisPoints ? params.affiliateFeeBasisPoints / 10000 : 0;
|
|
1460
|
+
const affiliateFeeInAsset = inputMinusInboundFeeInAsset.times(affiliateFeePercent);
|
|
1461
|
+
let affiliateFeeSwapOutputInRune;
|
|
1462
|
+
if (isAssetRuneNative(affiliateFeeInAsset.asset)) {
|
|
1463
|
+
affiliateFeeSwapOutputInRune = {
|
|
1464
|
+
output: affiliateFeeInAsset,
|
|
1465
|
+
swapFee: new CryptoAmount(baseAmount(0), AssetRuneNative),
|
|
1466
|
+
slip: new BigNumber(0),
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
else {
|
|
1470
|
+
affiliateFeeSwapOutputInRune = yield this.thorchainCache.getExpectedSwapOutput(affiliateFeeInAsset, AssetRuneNative);
|
|
1481
1471
|
}
|
|
1472
|
+
// remove the affiliate fee from the input.
|
|
1473
|
+
const inputNetInAsset = inputMinusInboundFeeInAsset.minus(affiliateFeeInAsset);
|
|
1482
1474
|
// Now calculate swap output based on inputNetAmount
|
|
1483
|
-
const
|
|
1484
|
-
const swapFeeInRune = yield this.thorchainCache.convert(swapOutput.swapFee, AssetRuneNative);
|
|
1485
|
-
const outputInRune = yield this.thorchainCache.convert(swapOutput.output, AssetRuneNative);
|
|
1475
|
+
const swapOutputInDestinationAsset = yield this.thorchainCache.getExpectedSwapOutput(inputNetInAsset, params.destinationAsset);
|
|
1486
1476
|
// ---------------- Remove Outbound Fee ---------------------- /
|
|
1487
|
-
const
|
|
1488
|
-
|
|
1477
|
+
const outboundFeeInDestinationAsset = yield this.thorchainCache.convert(outboundFeeInOutboundGasAsset, params.destinationAsset);
|
|
1478
|
+
// console.log('a', assetToString(swapOutputInDestinationAsset.output.asset))
|
|
1479
|
+
// console.log('b', assetToString(outboundFeeInDestinationAsset.asset))
|
|
1480
|
+
// // console.log('x', swapOutputInDestinationAsset.output.formatedAssetString())
|
|
1481
|
+
// // console.log('y', outboundFeeInDestinationAsset.formatedAssetString())
|
|
1482
|
+
const netOutputInAsset = swapOutputInDestinationAsset.output.minus(outboundFeeInDestinationAsset);
|
|
1489
1483
|
const totalFees = {
|
|
1490
|
-
inboundFee:
|
|
1491
|
-
swapFee:
|
|
1492
|
-
outboundFee:
|
|
1493
|
-
affiliateFee:
|
|
1484
|
+
inboundFee: inboundFeeInInboundGasAsset,
|
|
1485
|
+
swapFee: swapOutputInDestinationAsset.swapFee,
|
|
1486
|
+
outboundFee: outboundFeeInOutboundGasAsset,
|
|
1487
|
+
affiliateFee: affiliateFeeSwapOutputInRune.output,
|
|
1488
|
+
// totalFees: ,
|
|
1494
1489
|
};
|
|
1490
|
+
//const totalFeesInUsd = await this.getFeesIn(totalFees, usdAsset)
|
|
1495
1491
|
const swapEstimate = {
|
|
1496
1492
|
totalFees: totalFees,
|
|
1497
|
-
slipPercentage:
|
|
1493
|
+
slipPercentage: swapOutputInDestinationAsset.slip,
|
|
1498
1494
|
netOutput: netOutputInAsset,
|
|
1499
1495
|
waitTimeSeconds: 0,
|
|
1500
1496
|
canSwap: false,
|
|
@@ -1509,15 +1505,16 @@ class ThorchainQuery {
|
|
|
1509
1505
|
* @returns - constructed memo string
|
|
1510
1506
|
*/
|
|
1511
1507
|
constructSwapMemo(params) {
|
|
1508
|
+
var _a;
|
|
1512
1509
|
const limstring = params.limit.amount().toFixed();
|
|
1513
1510
|
// create LIM with interface ID
|
|
1514
|
-
const lim = limstring.substring(0, limstring.length - 3).concat(params.interfaceID
|
|
1511
|
+
const lim = limstring.substring(0, limstring.length - 3).concat(params.interfaceID);
|
|
1515
1512
|
// create the full memo
|
|
1516
1513
|
let memo = `=:${assetToString(params.destinationAsset)}`;
|
|
1517
1514
|
// NOTE: we should validate affiliate address is EITHER: a thorname or valid thorchain address, currently we cannot do this without importing xchain-thorchain
|
|
1518
|
-
if (params.affiliateAddress
|
|
1515
|
+
if (((_a = params.affiliateAddress) === null || _a === void 0 ? void 0 : _a.length) > 0) {
|
|
1519
1516
|
// NOTE: we should validate destinationAddress address is valid destination address for the asset type requested
|
|
1520
|
-
memo = memo.concat(`:${params.destinationAddress}:${lim}:${params.affiliateAddress}:${params.
|
|
1517
|
+
memo = memo.concat(`:${params.destinationAddress}:${lim}:${params.affiliateAddress}:${params.affiliateFeeBasisPoints}`);
|
|
1521
1518
|
}
|
|
1522
1519
|
else {
|
|
1523
1520
|
memo = memo.concat(`:${params.destinationAddress}:${lim}`);
|
|
@@ -1632,6 +1629,9 @@ class ThorchainQuery {
|
|
|
1632
1629
|
};
|
|
1633
1630
|
});
|
|
1634
1631
|
}
|
|
1632
|
+
// async getTotalFees(inbound: CryptoAmount, swapFee: CryptoAmount, outbound: CryptoAmount, affiliate: CryptoAmount): Promise<TotalFees> {
|
|
1633
|
+
// return {}
|
|
1634
|
+
// }
|
|
1635
1635
|
/**
|
|
1636
1636
|
* Returns the exchange of a CryptoAmount to a different Asset
|
|
1637
1637
|
*
|
package/lib/index.js
CHANGED
|
@@ -436,6 +436,7 @@ const getBaseAmountWithDiffDecimals = (inputAmount, outDecimals) => {
|
|
|
436
436
|
const getSwapFee = (inputAmount, pool, toRune) => {
|
|
437
437
|
// formula: (x * x * Y) / (x + X) ^ 2
|
|
438
438
|
// const isInputRune = isAssetRuneNative(inputAmount.asset)
|
|
439
|
+
const decimalsOut = pool.pool.nativeDecimal !== '-1' ? Number(pool.pool.nativeDecimal) : inputAmount.baseAmount.decimal;
|
|
439
440
|
const x = getBaseAmountWithDiffDecimals(inputAmount, 8);
|
|
440
441
|
const X = toRune ? pool.assetBalance.amount() : pool.runeBalance.amount(); // input is asset if toRune
|
|
441
442
|
const Y = toRune ? pool.runeBalance.amount() : pool.assetBalance.amount(); // output is rune if toRune
|
|
@@ -444,7 +445,7 @@ const getSwapFee = (inputAmount, pool, toRune) => {
|
|
|
444
445
|
const denominator = x.plus(X).pow(2);
|
|
445
446
|
const result = numerator.div(denominator);
|
|
446
447
|
const eightDecimalResult = new CryptoAmount(xchainUtil.baseAmount(result), units);
|
|
447
|
-
const decimals = toRune ? 8 :
|
|
448
|
+
const decimals = toRune ? 8 : decimalsOut;
|
|
448
449
|
const baseOut = getBaseAmountWithDiffDecimals(eightDecimalResult, decimals);
|
|
449
450
|
const swapFee = new CryptoAmount(xchainUtil.baseAmount(baseOut, decimals), units);
|
|
450
451
|
//console.log(` swapFee ${swapFee.assetAmountFixedString()} `)
|
|
@@ -474,6 +475,7 @@ const getSwapSlip = (inputAmount, pool, toRune) => {
|
|
|
474
475
|
*/
|
|
475
476
|
const getSwapOutput = (inputAmount, pool, toRune) => {
|
|
476
477
|
// formula: (x * X * Y) / (x + X) ^ 2
|
|
478
|
+
const decimalsOut = pool.pool.nativeDecimal !== '-1' ? Number(pool.pool.nativeDecimal) : inputAmount.baseAmount.decimal;
|
|
477
479
|
const x = getBaseAmountWithDiffDecimals(inputAmount, 8);
|
|
478
480
|
const X = toRune ? pool.assetBalance.amount() : pool.runeBalance.amount(); // input is asset if toRune
|
|
479
481
|
const Y = toRune ? pool.runeBalance.amount() : pool.assetBalance.amount(); // output is rune if toRune
|
|
@@ -483,7 +485,7 @@ const getSwapOutput = (inputAmount, pool, toRune) => {
|
|
|
483
485
|
const denominator = x.plus(X).pow(2);
|
|
484
486
|
const result = numerator.div(denominator);
|
|
485
487
|
const eightDecimalResult = new CryptoAmount(xchainUtil.baseAmount(result), units);
|
|
486
|
-
const decimals = toRune ? 8 :
|
|
488
|
+
const decimals = toRune ? 8 : decimalsOut;
|
|
487
489
|
const baseOut = getBaseAmountWithDiffDecimals(eightDecimalResult, decimals);
|
|
488
490
|
return new CryptoAmount(xchainUtil.baseAmount(baseOut, decimals), units);
|
|
489
491
|
};
|
|
@@ -594,6 +596,42 @@ const calcNetworkFee = (asset, inbound) => {
|
|
|
594
596
|
}
|
|
595
597
|
throw new Error(`could not calculate inbound fee for ${asset.chain}`);
|
|
596
598
|
};
|
|
599
|
+
/**
|
|
600
|
+
* Works out the required outbound fee based on the chain.
|
|
601
|
+
* Call getInboundDetails to get the current outbound fee
|
|
602
|
+
*
|
|
603
|
+
* @param sourceAsset
|
|
604
|
+
* @param inbound detail
|
|
605
|
+
* @see https://dev.thorchain.org/thorchain-dev/thorchain-and-fees#fee-calcuation-by-chain
|
|
606
|
+
* @returns
|
|
607
|
+
*/
|
|
608
|
+
const calcOutboundFee = (asset, inbound) => {
|
|
609
|
+
if (asset.synth)
|
|
610
|
+
return new CryptoAmount(xchainUtil.baseAmount(2000000), xchainUtil.AssetRuneNative);
|
|
611
|
+
switch (asset.chain) {
|
|
612
|
+
case xchainUtil.Chain.Bitcoin:
|
|
613
|
+
return new CryptoAmount(xchainUtil.baseAmount(inbound.outboundFee), xchainUtil.AssetBTC);
|
|
614
|
+
case xchainUtil.Chain.BitcoinCash:
|
|
615
|
+
return new CryptoAmount(xchainUtil.baseAmount(inbound.outboundFee), xchainUtil.AssetBCH);
|
|
616
|
+
case xchainUtil.Chain.Litecoin:
|
|
617
|
+
return new CryptoAmount(xchainUtil.baseAmount(inbound.outboundFee), xchainUtil.AssetLTC);
|
|
618
|
+
case xchainUtil.Chain.Doge:
|
|
619
|
+
// NOTE: UTXO chains estimate fees with a 250 byte size
|
|
620
|
+
return new CryptoAmount(xchainUtil.baseAmount(inbound.outboundFee), xchainUtil.AssetDOGE);
|
|
621
|
+
case xchainUtil.Chain.Binance:
|
|
622
|
+
//flat fee
|
|
623
|
+
return new CryptoAmount(xchainUtil.baseAmount(inbound.outboundFee), xchainUtil.AssetBNB);
|
|
624
|
+
case xchainUtil.Chain.Ethereum:
|
|
625
|
+
return new CryptoAmount(xchainUtil.baseAmount(inbound.outboundFee.multipliedBy(Math.pow(10, 9)), 18), xchainUtil.AssetETH);
|
|
626
|
+
case xchainUtil.Chain.Avalanche:
|
|
627
|
+
return new CryptoAmount(xchainUtil.baseAmount(inbound.outboundFee.multipliedBy(Math.pow(10, 9)), 18), xchainUtil.AssetAVAX);
|
|
628
|
+
case xchainUtil.Chain.Cosmos:
|
|
629
|
+
return new CryptoAmount(xchainUtil.baseAmount(inbound.outboundFee), xchainUtil.AssetAtom);
|
|
630
|
+
case xchainUtil.Chain.THORChain:
|
|
631
|
+
return new CryptoAmount(xchainUtil.baseAmount(2000000), xchainUtil.AssetRuneNative);
|
|
632
|
+
}
|
|
633
|
+
throw new Error(`could not calculate outbound fee for ${asset.chain}`);
|
|
634
|
+
};
|
|
597
635
|
/**
|
|
598
636
|
* Return the chain for a given Asset This method should live somewhere else.
|
|
599
637
|
* @param chain
|
|
@@ -838,14 +876,12 @@ class ThorchainCache {
|
|
|
838
876
|
* @param expireNetworkValuesCacheMillis - how long should the Mimir/Constants be cached before expiry
|
|
839
877
|
* @returns ThorchainCache
|
|
840
878
|
*/
|
|
841
|
-
constructor(midgard = defaultMidgard, thornode = defaultThornode, expirePoolCacheMillis = 6000,
|
|
842
|
-
this.asgardAssetsCache = undefined;
|
|
879
|
+
constructor(midgard = defaultMidgard, thornode = defaultThornode, expirePoolCacheMillis = 6000, expireInboundDetailsCacheMillis = 6000, expireNetworkValuesCacheMillis = TEN_MINUTES) {
|
|
843
880
|
this.inboundDetailCache = undefined;
|
|
844
881
|
this.networkValuesCache = undefined;
|
|
845
882
|
this.midgard = midgard;
|
|
846
883
|
this.thornode = thornode;
|
|
847
884
|
this.expirePoolCacheMillis = expirePoolCacheMillis;
|
|
848
|
-
this.expireAsgardCacheMillis = expireAsgardCacheMillis;
|
|
849
885
|
this.expireInboundDetailsCacheMillis = expireInboundDetailsCacheMillis;
|
|
850
886
|
this.expireNetworkValuesCacheMillis = expireNetworkValuesCacheMillis;
|
|
851
887
|
//initialize the cache
|
|
@@ -955,29 +991,6 @@ class ThorchainCache {
|
|
|
955
991
|
}
|
|
956
992
|
});
|
|
957
993
|
}
|
|
958
|
-
/**
|
|
959
|
-
* Refreshes the asgardAssetsCache Cache
|
|
960
|
-
*
|
|
961
|
-
* NOTE: do not call refereshAsgardCache() directly, call getAsgardAssets() instead
|
|
962
|
-
* which will refresh the cache if it's expired
|
|
963
|
-
*/
|
|
964
|
-
refereshAsgardCache() {
|
|
965
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
966
|
-
const inboundAddresses = yield this.thornode.getInboundAddresses();
|
|
967
|
-
const map = {};
|
|
968
|
-
if (inboundAddresses) {
|
|
969
|
-
for (const inboundAddress of inboundAddresses) {
|
|
970
|
-
if (!inboundAddress.chain)
|
|
971
|
-
throw Error('chain needed');
|
|
972
|
-
map[inboundAddress.chain] = inboundAddress;
|
|
973
|
-
}
|
|
974
|
-
this.asgardAssetsCache = {
|
|
975
|
-
lastRefreshed: Date.now(),
|
|
976
|
-
inboundAddresses: map,
|
|
977
|
-
};
|
|
978
|
-
}
|
|
979
|
-
});
|
|
980
|
-
}
|
|
981
994
|
/**
|
|
982
995
|
* Refreshes the InboundDetailCache Cache
|
|
983
996
|
*
|
|
@@ -1064,22 +1077,26 @@ class ThorchainCache {
|
|
|
1064
1077
|
*/
|
|
1065
1078
|
getExpectedSwapOutput(inputAmount, destinationAsset) {
|
|
1066
1079
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1080
|
+
let swapOutput;
|
|
1067
1081
|
if (xchainUtil.isAssetRuneNative(inputAmount.asset)) {
|
|
1068
1082
|
//singleswap from rune -> asset
|
|
1069
1083
|
const pool = yield this.getPoolForAsset(destinationAsset);
|
|
1070
|
-
|
|
1084
|
+
swapOutput = getSingleSwap(inputAmount, pool, false);
|
|
1071
1085
|
}
|
|
1072
1086
|
else if (xchainUtil.isAssetRuneNative(destinationAsset)) {
|
|
1073
1087
|
//singleswap from asset -> rune
|
|
1074
1088
|
const pool = yield this.getPoolForAsset(inputAmount.asset);
|
|
1075
|
-
|
|
1089
|
+
swapOutput = getSingleSwap(inputAmount, pool, true);
|
|
1076
1090
|
}
|
|
1077
1091
|
else {
|
|
1078
1092
|
//doubleswap asset-> asset
|
|
1079
1093
|
const inPool = yield this.getPoolForAsset(inputAmount.asset);
|
|
1080
1094
|
const destPool = yield this.getPoolForAsset(destinationAsset);
|
|
1081
|
-
|
|
1095
|
+
swapOutput = yield getDoubleSwap(inputAmount, inPool, destPool, this);
|
|
1082
1096
|
}
|
|
1097
|
+
//Note this is needed to return a synth vs. a native asset on swap out
|
|
1098
|
+
swapOutput.output = new CryptoAmount(swapOutput.output.baseAmount, destinationAsset);
|
|
1099
|
+
return swapOutput;
|
|
1083
1100
|
});
|
|
1084
1101
|
}
|
|
1085
1102
|
/**
|
|
@@ -1122,37 +1139,13 @@ class ThorchainCache {
|
|
|
1122
1139
|
}
|
|
1123
1140
|
getRouterAddressForChain(chain) {
|
|
1124
1141
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1125
|
-
const inboundAsgard = (yield this.
|
|
1142
|
+
const inboundAsgard = (yield this.getInboundDetails())[chain];
|
|
1126
1143
|
if (!(inboundAsgard === null || inboundAsgard === void 0 ? void 0 : inboundAsgard.router)) {
|
|
1127
1144
|
throw new Error('router address is not defined');
|
|
1128
1145
|
}
|
|
1129
1146
|
return inboundAsgard === null || inboundAsgard === void 0 ? void 0 : inboundAsgard.router;
|
|
1130
1147
|
});
|
|
1131
1148
|
}
|
|
1132
|
-
/**
|
|
1133
|
-
*
|
|
1134
|
-
* @returns - inbound adresses item
|
|
1135
|
-
*/
|
|
1136
|
-
getInboundAddresses() {
|
|
1137
|
-
var _a;
|
|
1138
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1139
|
-
const millisSinceLastRefeshed = Date.now() - (((_a = this.asgardAssetsCache) === null || _a === void 0 ? void 0 : _a.lastRefreshed) || 0);
|
|
1140
|
-
if (millisSinceLastRefeshed > this.expireAsgardCacheMillis) {
|
|
1141
|
-
try {
|
|
1142
|
-
yield this.refereshAsgardCache();
|
|
1143
|
-
}
|
|
1144
|
-
catch (e) {
|
|
1145
|
-
console.error(e);
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
if (this.asgardAssetsCache) {
|
|
1149
|
-
return this.asgardAssetsCache.inboundAddresses;
|
|
1150
|
-
}
|
|
1151
|
-
else {
|
|
1152
|
-
throw Error(`Could not refresh refereshAsgardCache `);
|
|
1153
|
-
}
|
|
1154
|
-
});
|
|
1155
|
-
}
|
|
1156
1149
|
/**
|
|
1157
1150
|
*
|
|
1158
1151
|
* @returns - inbound details
|
|
@@ -1336,7 +1329,8 @@ class ThorchainQuery {
|
|
|
1336
1329
|
|
|
1337
1330
|
* @returns The SwapEstimate
|
|
1338
1331
|
*/
|
|
1339
|
-
estimateSwap({ input, destinationAsset, destinationAddress, slipLimit
|
|
1332
|
+
estimateSwap({ input, destinationAsset, destinationAddress, slipLimit = new bignumber_js.BigNumber('0.03'), //default to 3%
|
|
1333
|
+
interfaceID = '555', affiliateAddress = '', affiliateFeeBasisPoints = 0, }) {
|
|
1340
1334
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1341
1335
|
yield this.isValidSwap({
|
|
1342
1336
|
input,
|
|
@@ -1344,7 +1338,7 @@ class ThorchainQuery {
|
|
|
1344
1338
|
destinationAddress,
|
|
1345
1339
|
slipLimit,
|
|
1346
1340
|
affiliateAddress,
|
|
1347
|
-
|
|
1341
|
+
affiliateFeeBasisPoints,
|
|
1348
1342
|
interfaceID,
|
|
1349
1343
|
});
|
|
1350
1344
|
const inboundDetails = yield this.thorchainCache.getInboundDetails();
|
|
@@ -1357,7 +1351,7 @@ class ThorchainQuery {
|
|
|
1357
1351
|
destinationAddress,
|
|
1358
1352
|
slipLimit,
|
|
1359
1353
|
affiliateAddress,
|
|
1360
|
-
|
|
1354
|
+
affiliateFeeBasisPoints,
|
|
1361
1355
|
interfaceID,
|
|
1362
1356
|
}, sourceInboundDetails, destinationInboundDetails);
|
|
1363
1357
|
// Calculate transaction expiry time
|
|
@@ -1371,7 +1365,7 @@ class ThorchainQuery {
|
|
|
1371
1365
|
destinationAddress,
|
|
1372
1366
|
slipLimit,
|
|
1373
1367
|
affiliateAddress,
|
|
1374
|
-
|
|
1368
|
+
affiliateFeeBasisPoints,
|
|
1375
1369
|
interfaceID,
|
|
1376
1370
|
}, swapEstimate, sourceInboundDetails, destinationInboundDetails);
|
|
1377
1371
|
const txDetails = {
|
|
@@ -1386,7 +1380,7 @@ class ThorchainQuery {
|
|
|
1386
1380
|
}
|
|
1387
1381
|
else {
|
|
1388
1382
|
txDetails.txEstimate.canSwap = true;
|
|
1389
|
-
const inboundAsgard = (yield this.thorchainCache.
|
|
1383
|
+
const inboundAsgard = (yield this.thorchainCache.getInboundDetails())[input.asset.chain];
|
|
1390
1384
|
txDetails.toAddress = (inboundAsgard === null || inboundAsgard === void 0 ? void 0 : inboundAsgard.address) || '';
|
|
1391
1385
|
// Work out LIM from the slip percentage
|
|
1392
1386
|
let limPercentage = BN_1;
|
|
@@ -1406,7 +1400,7 @@ class ThorchainQuery {
|
|
|
1406
1400
|
limit: xchainUtil.baseAmount(limAssetAmount8Decimals),
|
|
1407
1401
|
destinationAddress: destinationAddress,
|
|
1408
1402
|
affiliateAddress: affiliateAddress,
|
|
1409
|
-
|
|
1403
|
+
affiliateFeeBasisPoints: affiliateFeeBasisPoints,
|
|
1410
1404
|
interfaceID: interfaceID,
|
|
1411
1405
|
});
|
|
1412
1406
|
}
|
|
@@ -1418,6 +1412,7 @@ class ThorchainQuery {
|
|
|
1418
1412
|
* @param params
|
|
1419
1413
|
*/
|
|
1420
1414
|
isValidSwap(params) {
|
|
1415
|
+
var _a, _b;
|
|
1421
1416
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1422
1417
|
if (xchainUtil.isAssetRuneNative(params.input.asset)) {
|
|
1423
1418
|
if (params.input.baseAmount.decimal !== 8)
|
|
@@ -1437,8 +1432,12 @@ class ThorchainQuery {
|
|
|
1437
1432
|
if (params.input.baseAmount.lte(0))
|
|
1438
1433
|
throw Error('inputAmount must be greater than 0');
|
|
1439
1434
|
// Affiliate fee % can't exceed 10% because this is set by TC.
|
|
1440
|
-
if (params.
|
|
1441
|
-
throw Error(`
|
|
1435
|
+
if (params.affiliateFeeBasisPoints && (params.affiliateFeeBasisPoints < 0 || params.affiliateFeeBasisPoints > 1000))
|
|
1436
|
+
throw Error(`affiliateFeeBasisPoints must be between 0 and 1000 basis points`);
|
|
1437
|
+
if (params.affiliateFeeBasisPoints && !Number.isInteger(params.affiliateFeeBasisPoints))
|
|
1438
|
+
throw Error(`affiliateFeeBasisPoints must be an integer`);
|
|
1439
|
+
if (((_a = params.slipLimit) === null || _a === void 0 ? void 0 : _a.lte(0)) || ((_b = params.slipLimit) === null || _b === void 0 ? void 0 : _b.gt(1)))
|
|
1440
|
+
throw Error(`slipLimit must be between 0 and 1`);
|
|
1442
1441
|
});
|
|
1443
1442
|
}
|
|
1444
1443
|
/**
|
|
@@ -1459,51 +1458,48 @@ class ThorchainQuery {
|
|
|
1459
1458
|
const input = params.input.baseAmount.decimal === DEFAULT_THORCHAIN_DECIMALS
|
|
1460
1459
|
? params.input
|
|
1461
1460
|
: yield this.thorchainCache.convert(params.input, params.input.asset);
|
|
1462
|
-
|
|
1463
|
-
const
|
|
1464
|
-
const inboundFeeInAsset = calcNetworkFee(input.asset, sourceInboundDetails);
|
|
1465
|
-
// Retrieve outbound fee from inboundAddressDetails.
|
|
1466
|
-
const outboundFeeInAsset = calcNetworkFee(params.destinationAsset, destinationInboundDetails).times(3);
|
|
1467
|
-
// convert fees to rune
|
|
1468
|
-
const inboundFeeInRune = yield this.thorchainCache.convert(inboundFeeInAsset, xchainUtil.AssetRuneNative);
|
|
1469
|
-
let outboundFeeInRune = yield this.thorchainCache.convert(outboundFeeInAsset, xchainUtil.AssetRuneNative);
|
|
1461
|
+
const inboundFeeInInboundGasAsset = calcNetworkFee(input.asset, sourceInboundDetails);
|
|
1462
|
+
const outboundFeeInOutboundGasAsset = calcOutboundFee(params.destinationAsset, destinationInboundDetails);
|
|
1470
1463
|
// ----------- Remove Fees from inbound before doing the swap -----------
|
|
1471
|
-
const
|
|
1464
|
+
const inboundFeeInInboundAsset = yield this.thorchainCache.convert(inboundFeeInInboundGasAsset, params.input.asset);
|
|
1465
|
+
const inputMinusInboundFeeInAsset = input.minus(inboundFeeInInboundAsset);
|
|
1466
|
+
// console.log('y', inputMinusInboundFeeInAsset.formatedAssetString())
|
|
1472
1467
|
// remove any affiliateFee. netInput * affiliateFee (percentage) of the destination asset type
|
|
1473
|
-
const
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
const checkOutboundFee = (yield this.convert(outboundFeeInRune, usdAsset)).gte(usdMinFee);
|
|
1486
|
-
if (!checkOutboundFee) {
|
|
1487
|
-
const newFee = usdMinFee;
|
|
1488
|
-
outboundFeeInRune = yield this.convert(newFee, xchainUtil.AssetRuneNative);
|
|
1489
|
-
}
|
|
1468
|
+
const affiliateFeePercent = params.affiliateFeeBasisPoints ? params.affiliateFeeBasisPoints / 10000 : 0;
|
|
1469
|
+
const affiliateFeeInAsset = inputMinusInboundFeeInAsset.times(affiliateFeePercent);
|
|
1470
|
+
let affiliateFeeSwapOutputInRune;
|
|
1471
|
+
if (xchainUtil.isAssetRuneNative(affiliateFeeInAsset.asset)) {
|
|
1472
|
+
affiliateFeeSwapOutputInRune = {
|
|
1473
|
+
output: affiliateFeeInAsset,
|
|
1474
|
+
swapFee: new CryptoAmount(xchainUtil.baseAmount(0), xchainUtil.AssetRuneNative),
|
|
1475
|
+
slip: new bignumber_js.BigNumber(0),
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
else {
|
|
1479
|
+
affiliateFeeSwapOutputInRune = yield this.thorchainCache.getExpectedSwapOutput(affiliateFeeInAsset, xchainUtil.AssetRuneNative);
|
|
1490
1480
|
}
|
|
1481
|
+
// remove the affiliate fee from the input.
|
|
1482
|
+
const inputNetInAsset = inputMinusInboundFeeInAsset.minus(affiliateFeeInAsset);
|
|
1491
1483
|
// Now calculate swap output based on inputNetAmount
|
|
1492
|
-
const
|
|
1493
|
-
const swapFeeInRune = yield this.thorchainCache.convert(swapOutput.swapFee, xchainUtil.AssetRuneNative);
|
|
1494
|
-
const outputInRune = yield this.thorchainCache.convert(swapOutput.output, xchainUtil.AssetRuneNative);
|
|
1484
|
+
const swapOutputInDestinationAsset = yield this.thorchainCache.getExpectedSwapOutput(inputNetInAsset, params.destinationAsset);
|
|
1495
1485
|
// ---------------- Remove Outbound Fee ---------------------- /
|
|
1496
|
-
const
|
|
1497
|
-
|
|
1486
|
+
const outboundFeeInDestinationAsset = yield this.thorchainCache.convert(outboundFeeInOutboundGasAsset, params.destinationAsset);
|
|
1487
|
+
// console.log('a', assetToString(swapOutputInDestinationAsset.output.asset))
|
|
1488
|
+
// console.log('b', assetToString(outboundFeeInDestinationAsset.asset))
|
|
1489
|
+
// // console.log('x', swapOutputInDestinationAsset.output.formatedAssetString())
|
|
1490
|
+
// // console.log('y', outboundFeeInDestinationAsset.formatedAssetString())
|
|
1491
|
+
const netOutputInAsset = swapOutputInDestinationAsset.output.minus(outboundFeeInDestinationAsset);
|
|
1498
1492
|
const totalFees = {
|
|
1499
|
-
inboundFee:
|
|
1500
|
-
swapFee:
|
|
1501
|
-
outboundFee:
|
|
1502
|
-
affiliateFee:
|
|
1493
|
+
inboundFee: inboundFeeInInboundGasAsset,
|
|
1494
|
+
swapFee: swapOutputInDestinationAsset.swapFee,
|
|
1495
|
+
outboundFee: outboundFeeInOutboundGasAsset,
|
|
1496
|
+
affiliateFee: affiliateFeeSwapOutputInRune.output,
|
|
1497
|
+
// totalFees: ,
|
|
1503
1498
|
};
|
|
1499
|
+
//const totalFeesInUsd = await this.getFeesIn(totalFees, usdAsset)
|
|
1504
1500
|
const swapEstimate = {
|
|
1505
1501
|
totalFees: totalFees,
|
|
1506
|
-
slipPercentage:
|
|
1502
|
+
slipPercentage: swapOutputInDestinationAsset.slip,
|
|
1507
1503
|
netOutput: netOutputInAsset,
|
|
1508
1504
|
waitTimeSeconds: 0,
|
|
1509
1505
|
canSwap: false,
|
|
@@ -1518,15 +1514,16 @@ class ThorchainQuery {
|
|
|
1518
1514
|
* @returns - constructed memo string
|
|
1519
1515
|
*/
|
|
1520
1516
|
constructSwapMemo(params) {
|
|
1517
|
+
var _a;
|
|
1521
1518
|
const limstring = params.limit.amount().toFixed();
|
|
1522
1519
|
// create LIM with interface ID
|
|
1523
|
-
const lim = limstring.substring(0, limstring.length - 3).concat(params.interfaceID
|
|
1520
|
+
const lim = limstring.substring(0, limstring.length - 3).concat(params.interfaceID);
|
|
1524
1521
|
// create the full memo
|
|
1525
1522
|
let memo = `=:${xchainUtil.assetToString(params.destinationAsset)}`;
|
|
1526
1523
|
// NOTE: we should validate affiliate address is EITHER: a thorname or valid thorchain address, currently we cannot do this without importing xchain-thorchain
|
|
1527
|
-
if (params.affiliateAddress
|
|
1524
|
+
if (((_a = params.affiliateAddress) === null || _a === void 0 ? void 0 : _a.length) > 0) {
|
|
1528
1525
|
// NOTE: we should validate destinationAddress address is valid destination address for the asset type requested
|
|
1529
|
-
memo = memo.concat(`:${params.destinationAddress}:${lim}:${params.affiliateAddress}:${params.
|
|
1526
|
+
memo = memo.concat(`:${params.destinationAddress}:${lim}:${params.affiliateAddress}:${params.affiliateFeeBasisPoints}`);
|
|
1530
1527
|
}
|
|
1531
1528
|
else {
|
|
1532
1529
|
memo = memo.concat(`:${params.destinationAddress}:${lim}`);
|
|
@@ -1641,6 +1638,9 @@ class ThorchainQuery {
|
|
|
1641
1638
|
};
|
|
1642
1639
|
});
|
|
1643
1640
|
}
|
|
1641
|
+
// async getTotalFees(inbound: CryptoAmount, swapFee: CryptoAmount, outbound: CryptoAmount, affiliate: CryptoAmount): Promise<TotalFees> {
|
|
1642
|
+
// return {}
|
|
1643
|
+
// }
|
|
1644
1644
|
/**
|
|
1645
1645
|
* Returns the exchange of a CryptoAmount to a different Asset
|
|
1646
1646
|
*
|
package/lib/thorchain-cache.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { InboundAddress } from '@xchainjs/xchain-thornode';
|
|
2
1
|
import { Address, Asset, Chain } from '@xchainjs/xchain-util';
|
|
3
2
|
import { BigNumber } from 'bignumber.js';
|
|
4
3
|
import { CryptoAmount } from './crypto-amount';
|
|
@@ -13,11 +12,9 @@ export declare class ThorchainCache {
|
|
|
13
12
|
readonly midgard: Midgard;
|
|
14
13
|
readonly thornode: Thornode;
|
|
15
14
|
private poolCache;
|
|
16
|
-
private asgardAssetsCache;
|
|
17
15
|
private inboundDetailCache;
|
|
18
16
|
private networkValuesCache;
|
|
19
17
|
private expirePoolCacheMillis;
|
|
20
|
-
private expireAsgardCacheMillis;
|
|
21
18
|
private expireInboundDetailsCacheMillis;
|
|
22
19
|
private expireNetworkValuesCacheMillis;
|
|
23
20
|
/**
|
|
@@ -30,7 +27,7 @@ export declare class ThorchainCache {
|
|
|
30
27
|
* @param expireNetworkValuesCacheMillis - how long should the Mimir/Constants be cached before expiry
|
|
31
28
|
* @returns ThorchainCache
|
|
32
29
|
*/
|
|
33
|
-
constructor(midgard?: Midgard, thornode?: Thornode, expirePoolCacheMillis?: number,
|
|
30
|
+
constructor(midgard?: Midgard, thornode?: Thornode, expirePoolCacheMillis?: number, expireInboundDetailsCacheMillis?: number, expireNetworkValuesCacheMillis?: number);
|
|
34
31
|
/**
|
|
35
32
|
* Gets the exchange rate of the from asset in terms on the to asset
|
|
36
33
|
*
|
|
@@ -59,13 +56,6 @@ export declare class ThorchainCache {
|
|
|
59
56
|
* which will refresh the cache if it's expired
|
|
60
57
|
*/
|
|
61
58
|
private refereshPoolCache;
|
|
62
|
-
/**
|
|
63
|
-
* Refreshes the asgardAssetsCache Cache
|
|
64
|
-
*
|
|
65
|
-
* NOTE: do not call refereshAsgardCache() directly, call getAsgardAssets() instead
|
|
66
|
-
* which will refresh the cache if it's expired
|
|
67
|
-
*/
|
|
68
|
-
private refereshAsgardCache;
|
|
69
59
|
/**
|
|
70
60
|
* Refreshes the InboundDetailCache Cache
|
|
71
61
|
*
|
|
@@ -105,11 +95,6 @@ export declare class ThorchainCache {
|
|
|
105
95
|
convert(input: CryptoAmount, outAsset: Asset): Promise<CryptoAmount>;
|
|
106
96
|
private getDecimalForAsset;
|
|
107
97
|
getRouterAddressForChain(chain: Chain): Promise<Address>;
|
|
108
|
-
/**
|
|
109
|
-
*
|
|
110
|
-
* @returns - inbound adresses item
|
|
111
|
-
*/
|
|
112
|
-
getInboundAddresses(): Promise<Record<string, InboundAddress>>;
|
|
113
98
|
/**
|
|
114
99
|
*
|
|
115
100
|
* @returns - inbound details
|
package/lib/thorchain-query.d.ts
CHANGED
|
@@ -26,7 +26,8 @@ export declare class ThorchainQuery {
|
|
|
26
26
|
|
|
27
27
|
* @returns The SwapEstimate
|
|
28
28
|
*/
|
|
29
|
-
estimateSwap({ input, destinationAsset, destinationAddress, slipLimit,
|
|
29
|
+
estimateSwap({ input, destinationAsset, destinationAddress, slipLimit, //default to 3%
|
|
30
|
+
interfaceID, affiliateAddress, affiliateFeeBasisPoints, }: EstimateSwapParams): Promise<TxDetails>;
|
|
30
31
|
/**
|
|
31
32
|
* Basic Checks for swap information
|
|
32
33
|
* @param params
|
package/lib/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FeeOption } from '@xchainjs/xchain-client';
|
|
2
|
-
import {
|
|
2
|
+
import { LiquidityProvider } from '@xchainjs/xchain-thornode';
|
|
3
3
|
import { Address, Asset, BaseAmount, Chain } from '@xchainjs/xchain-util';
|
|
4
4
|
import { BigNumber } from 'bignumber.js';
|
|
5
5
|
import { CryptoAmount } from './crypto-amount';
|
|
@@ -22,10 +22,6 @@ export declare type PoolCache = {
|
|
|
22
22
|
lastRefreshed: number;
|
|
23
23
|
pools: Record<string, LiquidityPool>;
|
|
24
24
|
};
|
|
25
|
-
export declare type AsgardCache = {
|
|
26
|
-
lastRefreshed: number;
|
|
27
|
-
inboundAddresses: Record<string, InboundAddress>;
|
|
28
|
-
};
|
|
29
25
|
export declare type InboundDetailCache = {
|
|
30
26
|
lastRefreshed: number;
|
|
31
27
|
inboundDetails: Record<string, InboundDetail>;
|
|
@@ -44,8 +40,8 @@ export declare type EstimateSwapParams = {
|
|
|
44
40
|
destinationAddress: Address;
|
|
45
41
|
slipLimit?: BigNumber;
|
|
46
42
|
affiliateAddress?: Address;
|
|
47
|
-
|
|
48
|
-
interfaceID?:
|
|
43
|
+
affiliateFeeBasisPoints?: number;
|
|
44
|
+
interfaceID?: string;
|
|
49
45
|
};
|
|
50
46
|
export declare type SwapOutput = {
|
|
51
47
|
output: CryptoAmount;
|
|
@@ -91,9 +87,9 @@ export declare type ConstructMemo = {
|
|
|
91
87
|
limit: BaseAmount;
|
|
92
88
|
destinationAddress: Address;
|
|
93
89
|
affiliateAddress: Address;
|
|
94
|
-
|
|
90
|
+
affiliateFeeBasisPoints: number;
|
|
95
91
|
feeOption?: FeeOption;
|
|
96
|
-
interfaceID:
|
|
92
|
+
interfaceID: string;
|
|
97
93
|
};
|
|
98
94
|
export declare type TxDetails = {
|
|
99
95
|
memo: string;
|
package/lib/utils/swap.d.ts
CHANGED
|
@@ -58,6 +58,16 @@ export declare const getDoubleSwap: (inputAmount: CryptoAmount, pool1: Liquidity
|
|
|
58
58
|
* @returns
|
|
59
59
|
*/
|
|
60
60
|
export declare const calcNetworkFee: (asset: Asset, inbound: InboundDetail) => CryptoAmount;
|
|
61
|
+
/**
|
|
62
|
+
* Works out the required outbound fee based on the chain.
|
|
63
|
+
* Call getInboundDetails to get the current outbound fee
|
|
64
|
+
*
|
|
65
|
+
* @param sourceAsset
|
|
66
|
+
* @param inbound detail
|
|
67
|
+
* @see https://dev.thorchain.org/thorchain-dev/thorchain-and-fees#fee-calcuation-by-chain
|
|
68
|
+
* @returns
|
|
69
|
+
*/
|
|
70
|
+
export declare const calcOutboundFee: (asset: Asset, inbound: InboundDetail) => CryptoAmount;
|
|
61
71
|
/**
|
|
62
72
|
* Return the chain for a given Asset This method should live somewhere else.
|
|
63
73
|
* @param chain
|
package/package.json
CHANGED