@strkfarm/sdk 1.0.52 → 1.0.53
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/dist/index.browser.global.js +90 -32
- package/dist/index.browser.mjs +90 -32
- package/dist/index.d.ts +23 -3
- package/dist/index.js +90 -32
- package/dist/index.mjs +90 -32
- package/package.json +4 -3
- package/src/modules/avnu.ts +7 -2
- package/src/strategies/ekubo-cl-vault.tsx +130 -68
|
@@ -38124,14 +38124,16 @@ var strkfarm_risk_engine = (() => {
|
|
|
38124
38124
|
};
|
|
38125
38125
|
assert3(fromToken != toToken, "From and to tokens are the same");
|
|
38126
38126
|
const quotes = await fetchQuotes(params);
|
|
38127
|
-
|
|
38127
|
+
const filteredQuotes = quotes.filter((q) => q.sellAmount.toString() == amountWei);
|
|
38128
|
+
if (filteredQuotes.length == 0) {
|
|
38128
38129
|
if (retry < MAX_RETRY) {
|
|
38129
38130
|
await new Promise((res) => setTimeout(res, 3e3));
|
|
38130
38131
|
return await this.getQuotes(fromToken, toToken, amountWei, taker, retry + 1);
|
|
38131
38132
|
}
|
|
38132
38133
|
throw new Error("no quotes found");
|
|
38133
38134
|
}
|
|
38134
|
-
|
|
38135
|
+
logger.verbose(`${_AvnuWrapper.name}: getQuotes => Found ${JSON.stringify(filteredQuotes[0])}`);
|
|
38136
|
+
return filteredQuotes[0];
|
|
38135
38137
|
}
|
|
38136
38138
|
async getSwapInfo(quote, taker, integratorFeeBps, integratorFeeRecipient, minAmount) {
|
|
38137
38139
|
const calldata = await fetchBuildExecuteTransaction(quote.quoteId);
|
|
@@ -38154,6 +38156,7 @@ var strkfarm_risk_engine = (() => {
|
|
|
38154
38156
|
startIndex += 5 + swap_params_len;
|
|
38155
38157
|
}
|
|
38156
38158
|
const _minAmount = minAmount || (quote.buyAmount * 95n / 100n).toString();
|
|
38159
|
+
logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => sellToken: ${quote.sellTokenAddress}, sellAmount: ${quote.sellAmount}`);
|
|
38157
38160
|
logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => buyToken: ${quote.buyTokenAddress}`);
|
|
38158
38161
|
logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => buyAmount: ${quote.buyAmount}, minAmount: ${_minAmount}`);
|
|
38159
38162
|
const swapInfo = {
|
|
@@ -51645,7 +51648,7 @@ var strkfarm_risk_engine = (() => {
|
|
|
51645
51648
|
};
|
|
51646
51649
|
}
|
|
51647
51650
|
}
|
|
51648
|
-
const ratioWeb3Number =
|
|
51651
|
+
const ratioWeb3Number = this.getRatio(sampleAmount0, sampleAmount1);
|
|
51649
51652
|
const ratio = Number(ratioWeb3Number.toFixed(18));
|
|
51650
51653
|
logger.verbose(
|
|
51651
51654
|
`${_EkuboCLVault.name}: ${this.metadata.name} => ratio: ${ratio.toString()}`
|
|
@@ -51677,11 +51680,18 @@ var strkfarm_risk_engine = (() => {
|
|
|
51677
51680
|
);
|
|
51678
51681
|
}
|
|
51679
51682
|
}
|
|
51683
|
+
getRatio(token0Amt, token1Amount) {
|
|
51684
|
+
const ratio = token0Amt.multipliedBy(1e18).dividedBy(token1Amount.toString()).dividedBy(1e18);
|
|
51685
|
+
logger.verbose(
|
|
51686
|
+
`${_EkuboCLVault.name}: getRatio => token0Amt: ${token0Amt.toString()}, token1Amount: ${token1Amount.toString()}, ratio: ${ratio.toString()}`
|
|
51687
|
+
);
|
|
51688
|
+
return ratio;
|
|
51689
|
+
}
|
|
51680
51690
|
_solveExpectedAmountsEq(availableAmount0, availableAmount1, ratio, price) {
|
|
51681
51691
|
const y = ratio.multipliedBy(availableAmount1).minus(availableAmount0).dividedBy(ratio.plus(1 / price));
|
|
51682
51692
|
const x = y.dividedBy(price);
|
|
51683
51693
|
logger.verbose(
|
|
51684
|
-
`${_EkuboCLVault.name}: _solveExpectedAmountsEq => x: ${x.toString()}, y: ${y.toString()}, amount0: ${availableAmount0.toString()}, amount1: ${availableAmount1.toString()}`
|
|
51694
|
+
`${_EkuboCLVault.name}: _solveExpectedAmountsEq => ratio: ${ratio.toString()}, x: ${x.toString()}, y: ${y.toString()}, amount0: ${availableAmount0.toString()}, amount1: ${availableAmount1.toString()}`
|
|
51685
51695
|
);
|
|
51686
51696
|
if (ratio.eq(0)) {
|
|
51687
51697
|
return {
|
|
@@ -51731,7 +51741,7 @@ var strkfarm_risk_engine = (() => {
|
|
|
51731
51741
|
}
|
|
51732
51742
|
};
|
|
51733
51743
|
}
|
|
51734
|
-
async getSwapInfoToHandleUnused(considerRebalance = true, newBounds = null, maxIterations = 20) {
|
|
51744
|
+
async getSwapInfoToHandleUnused(considerRebalance = true, newBounds = null, maxIterations = 20, priceRatioPrecision = 4) {
|
|
51735
51745
|
const poolKey = await this.getPoolKey();
|
|
51736
51746
|
const unusedBalances = await this.unusedBalances(poolKey);
|
|
51737
51747
|
const { amount: token0Bal1, usdValue: token0PriceUsd } = unusedBalances.token0;
|
|
@@ -51764,15 +51774,59 @@ var strkfarm_risk_engine = (() => {
|
|
|
51764
51774
|
logger.verbose(
|
|
51765
51775
|
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${ekuboBounds.lowerTick}, ${ekuboBounds.upperTick}`
|
|
51766
51776
|
);
|
|
51777
|
+
this.assertValidBounds(ekuboBounds);
|
|
51767
51778
|
return await this.getSwapInfoGivenAmounts(
|
|
51768
51779
|
poolKey,
|
|
51769
51780
|
token0Bal,
|
|
51770
51781
|
token1Bal,
|
|
51771
51782
|
ekuboBounds,
|
|
51772
|
-
maxIterations
|
|
51783
|
+
maxIterations,
|
|
51784
|
+
priceRatioPrecision
|
|
51785
|
+
);
|
|
51786
|
+
}
|
|
51787
|
+
assertValidBounds(bounds) {
|
|
51788
|
+
assert3(
|
|
51789
|
+
bounds.lowerTick < bounds.upperTick,
|
|
51790
|
+
`Invalid bounds: lowerTick (${bounds.lowerTick}) must be less than upperTick (${bounds.upperTick})`
|
|
51773
51791
|
);
|
|
51792
|
+
assert3(Number(bounds.lowerTick) % Number(this.poolKey?.tick_spacing) === 0, `Lower tick (${bounds.lowerTick}) must be a multiple of tick spacing (${this.poolKey?.tick_spacing})`);
|
|
51793
|
+
assert3(Number(bounds.upperTick) % Number(this.poolKey?.tick_spacing) === 0, `Upper tick (${bounds.upperTick}) must be a multiple of tick spacing (${this.poolKey?.tick_spacing})`);
|
|
51774
51794
|
}
|
|
51775
|
-
|
|
51795
|
+
// Helper to check for invalid states:
|
|
51796
|
+
// Throws if both tokens are decreased or both are increased, which is not expected
|
|
51797
|
+
assertValidAmounts(expectedAmounts, token0Bal, token1Bal) {
|
|
51798
|
+
if (expectedAmounts.amount0.lessThan(token0Bal) && expectedAmounts.amount1.lessThan(token1Bal)) {
|
|
51799
|
+
throw new Error("Both tokens are decreased, something is wrong");
|
|
51800
|
+
}
|
|
51801
|
+
if (expectedAmounts.amount0.greaterThan(token0Bal) && expectedAmounts.amount1.greaterThan(token1Bal)) {
|
|
51802
|
+
throw new Error("Both tokens are increased, something is wrong");
|
|
51803
|
+
}
|
|
51804
|
+
}
|
|
51805
|
+
// Helper to determine which token to sell, which to buy, and the amounts to use
|
|
51806
|
+
getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal) {
|
|
51807
|
+
const tokenToSell = expectedAmounts.amount0.lessThan(token0Bal) ? poolKey.token0 : poolKey.token1;
|
|
51808
|
+
const tokenToBuy = tokenToSell == poolKey.token0 ? poolKey.token1 : poolKey.token0;
|
|
51809
|
+
const amountToSell = tokenToSell == poolKey.token0 ? token0Bal.minus(expectedAmounts.amount0) : token1Bal.minus(expectedAmounts.amount1);
|
|
51810
|
+
if (amountToSell.eq(0)) {
|
|
51811
|
+
throw new Error(
|
|
51812
|
+
`No amount to sell for ${tokenToSell.address}`
|
|
51813
|
+
);
|
|
51814
|
+
}
|
|
51815
|
+
const remainingSellAmount = tokenToSell == poolKey.token0 ? expectedAmounts.amount0 : expectedAmounts.amount1;
|
|
51816
|
+
return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
|
|
51817
|
+
}
|
|
51818
|
+
/**
|
|
51819
|
+
* @description Calculates swap info based on given amounts of token0 and token1
|
|
51820
|
+
* Use token0 and token1 balances to determine the expected amounts for new bounds
|
|
51821
|
+
* @param poolKey
|
|
51822
|
+
* @param token0Bal
|
|
51823
|
+
* @param token1Bal
|
|
51824
|
+
* @param bounds // new bounds
|
|
51825
|
+
* @param maxIterations
|
|
51826
|
+
* @returns {Promise<SwapInfo>}
|
|
51827
|
+
*
|
|
51828
|
+
*/
|
|
51829
|
+
async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds, maxIterations = 20, priceRatioPrecision = 4) {
|
|
51776
51830
|
logger.verbose(
|
|
51777
51831
|
`${_EkuboCLVault.name}: getSwapInfoGivenAmounts::pre => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`
|
|
51778
51832
|
);
|
|
@@ -51786,28 +51840,13 @@ var strkfarm_risk_engine = (() => {
|
|
|
51786
51840
|
);
|
|
51787
51841
|
let retry = 0;
|
|
51788
51842
|
const maxRetry = maxIterations;
|
|
51789
|
-
function assertValidAmounts(expectedAmounts2, token0Bal2, token1Bal2) {
|
|
51790
|
-
if (expectedAmounts2.amount0.lessThan(token0Bal2) && expectedAmounts2.amount1.lessThan(token1Bal2)) {
|
|
51791
|
-
throw new Error("Both tokens are decreased, something is wrong");
|
|
51792
|
-
}
|
|
51793
|
-
if (expectedAmounts2.amount0.greaterThan(token0Bal2) && expectedAmounts2.amount1.greaterThan(token1Bal2)) {
|
|
51794
|
-
throw new Error("Both tokens are increased, something is wrong");
|
|
51795
|
-
}
|
|
51796
|
-
}
|
|
51797
|
-
function getSwapParams(expectedAmounts2, poolKey2, token0Bal2, token1Bal2) {
|
|
51798
|
-
const tokenToSell = expectedAmounts2.amount0.lessThan(token0Bal2) ? poolKey2.token0 : poolKey2.token1;
|
|
51799
|
-
const tokenToBuy = tokenToSell == poolKey2.token0 ? poolKey2.token1 : poolKey2.token0;
|
|
51800
|
-
const amountToSell = tokenToSell == poolKey2.token0 ? token0Bal2.minus(expectedAmounts2.amount0) : token1Bal2.minus(expectedAmounts2.amount1);
|
|
51801
|
-
const remainingSellAmount = tokenToSell == poolKey2.token0 ? expectedAmounts2.amount0 : expectedAmounts2.amount1;
|
|
51802
|
-
return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
|
|
51803
|
-
}
|
|
51804
51843
|
while (retry < maxRetry) {
|
|
51805
51844
|
retry++;
|
|
51806
51845
|
logger.verbose(
|
|
51807
51846
|
`getSwapInfoGivenAmounts::Retry attempt: ${retry}/${maxRetry}`
|
|
51808
51847
|
);
|
|
51809
|
-
assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
|
|
51810
|
-
const { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount } = getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal);
|
|
51848
|
+
this.assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
|
|
51849
|
+
const { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount } = this.getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal);
|
|
51811
51850
|
const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
|
|
51812
51851
|
const expectedRatio = expectedAmounts.ratio;
|
|
51813
51852
|
logger.verbose(
|
|
@@ -51845,6 +51884,9 @@ var strkfarm_risk_engine = (() => {
|
|
|
51845
51884
|
quote.buyAmount.toString(),
|
|
51846
51885
|
tokenToBuyInfo.decimals
|
|
51847
51886
|
);
|
|
51887
|
+
logger.verbose(
|
|
51888
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => amountOut: ${amountOut.toString()}`
|
|
51889
|
+
);
|
|
51848
51890
|
const swapPrice = tokenToSell == poolKey.token0 ? amountOut.dividedBy(amountToSell) : amountToSell.dividedBy(amountOut);
|
|
51849
51891
|
const newRatio = tokenToSell == poolKey.token0 ? remainingSellAmount.dividedBy(token1Bal.plus(amountOut)) : token0Bal.plus(amountOut).dividedBy(remainingSellAmount);
|
|
51850
51892
|
logger.verbose(
|
|
@@ -51854,31 +51896,46 @@ var strkfarm_risk_engine = (() => {
|
|
|
51854
51896
|
newRatio: newRatio.toString()
|
|
51855
51897
|
})}`
|
|
51856
51898
|
);
|
|
51857
|
-
const expectedPrecision = Math.min(
|
|
51858
|
-
|
|
51859
|
-
|
|
51899
|
+
const expectedPrecision = Math.min(priceRatioPrecision);
|
|
51900
|
+
const isWithInTolerance = Number(newRatio.toString()) <= expectedRatio * (1 + 1 / 10 ** expectedPrecision) && Number(newRatio.toString()) >= expectedRatio * (1 - 1 / 10 ** expectedPrecision);
|
|
51901
|
+
const currentPrecision = (expectedRatio - Number(newRatio.toString())) / expectedRatio;
|
|
51902
|
+
logger.verbose(
|
|
51903
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => isWithInTolerance: ${isWithInTolerance}, currentPrecision: ${currentPrecision.toString()}, expectedPrecision: ${expectedPrecision}`
|
|
51904
|
+
);
|
|
51905
|
+
if (!isWithInTolerance) {
|
|
51906
|
+
const expectedAmountsNew = await this._solveExpectedAmountsEq(
|
|
51860
51907
|
token0Bal,
|
|
51861
51908
|
token1Bal,
|
|
51862
51909
|
new Web3Number(Number(expectedRatio).toFixed(13), 18),
|
|
51863
51910
|
Number(swapPrice.toString())
|
|
51864
51911
|
);
|
|
51865
51912
|
logger.verbose(
|
|
51866
|
-
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${
|
|
51913
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmountsNew.amount0.toString()}, ${expectedAmountsNew.amount1.toString()}`
|
|
51867
51914
|
);
|
|
51915
|
+
if (expectedAmountsNew.amount0.eq(expectedAmounts.amount0.toString()) && expectedAmountsNew.amount1.eq(expectedAmounts.amount1.toString())) {
|
|
51916
|
+
logger.error(
|
|
51917
|
+
`getSwapInfoGivenAmounts: stuck in loop, expected amounts did not change`
|
|
51918
|
+
);
|
|
51919
|
+
throw new Error("Stuck in loop, expected amounts did not change");
|
|
51920
|
+
}
|
|
51921
|
+
expectedAmounts = expectedAmountsNew;
|
|
51868
51922
|
} else {
|
|
51869
51923
|
const minAmountOut = Web3Number.fromWei(
|
|
51870
51924
|
quote.buyAmount.toString(),
|
|
51871
51925
|
tokenToBuyInfo.decimals
|
|
51872
51926
|
).multipliedBy(0.9999);
|
|
51873
|
-
|
|
51927
|
+
const output3 = await this.avnu.getSwapInfo(
|
|
51874
51928
|
quote,
|
|
51875
51929
|
this.address.address,
|
|
51876
51930
|
0,
|
|
51877
51931
|
this.address.address,
|
|
51878
51932
|
minAmountOut.toWei()
|
|
51879
51933
|
);
|
|
51934
|
+
logger.verbose(
|
|
51935
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => swap info found: ${JSON.stringify(output3)}`
|
|
51936
|
+
);
|
|
51937
|
+
return output3;
|
|
51880
51938
|
}
|
|
51881
|
-
retry++;
|
|
51882
51939
|
}
|
|
51883
51940
|
throw new Error("Failed to get swap info");
|
|
51884
51941
|
}
|
|
@@ -52048,7 +52105,7 @@ var strkfarm_risk_engine = (() => {
|
|
|
52048
52105
|
amount1
|
|
52049
52106
|
};
|
|
52050
52107
|
}
|
|
52051
|
-
async harvest(acc, maxIterations = 20) {
|
|
52108
|
+
async harvest(acc, maxIterations = 20, priceRatioPrecision = 4) {
|
|
52052
52109
|
const ekuboHarvests = new EkuboHarvests(this.config);
|
|
52053
52110
|
const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
|
|
52054
52111
|
this.address
|
|
@@ -52078,7 +52135,8 @@ var strkfarm_risk_engine = (() => {
|
|
|
52078
52135
|
token0Amt,
|
|
52079
52136
|
token1Amt,
|
|
52080
52137
|
bounds,
|
|
52081
|
-
maxIterations
|
|
52138
|
+
maxIterations,
|
|
52139
|
+
priceRatioPrecision
|
|
52082
52140
|
);
|
|
52083
52141
|
swapInfo.token_to_address = token0Info.address.address;
|
|
52084
52142
|
logger.verbose(
|
package/dist/index.browser.mjs
CHANGED
|
@@ -1903,14 +1903,16 @@ var AvnuWrapper = class _AvnuWrapper {
|
|
|
1903
1903
|
};
|
|
1904
1904
|
assert(fromToken != toToken, "From and to tokens are the same");
|
|
1905
1905
|
const quotes = await fetchQuotes(params);
|
|
1906
|
-
|
|
1906
|
+
const filteredQuotes = quotes.filter((q) => q.sellAmount.toString() == amountWei);
|
|
1907
|
+
if (filteredQuotes.length == 0) {
|
|
1907
1908
|
if (retry < MAX_RETRY) {
|
|
1908
1909
|
await new Promise((res) => setTimeout(res, 3e3));
|
|
1909
1910
|
return await this.getQuotes(fromToken, toToken, amountWei, taker, retry + 1);
|
|
1910
1911
|
}
|
|
1911
1912
|
throw new Error("no quotes found");
|
|
1912
1913
|
}
|
|
1913
|
-
|
|
1914
|
+
logger.verbose(`${_AvnuWrapper.name}: getQuotes => Found ${JSON.stringify(filteredQuotes[0])}`);
|
|
1915
|
+
return filteredQuotes[0];
|
|
1914
1916
|
}
|
|
1915
1917
|
async getSwapInfo(quote, taker, integratorFeeBps, integratorFeeRecipient, minAmount) {
|
|
1916
1918
|
const calldata = await fetchBuildExecuteTransaction(quote.quoteId);
|
|
@@ -1933,6 +1935,7 @@ var AvnuWrapper = class _AvnuWrapper {
|
|
|
1933
1935
|
startIndex += 5 + swap_params_len;
|
|
1934
1936
|
}
|
|
1935
1937
|
const _minAmount = minAmount || (quote.buyAmount * 95n / 100n).toString();
|
|
1938
|
+
logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => sellToken: ${quote.sellTokenAddress}, sellAmount: ${quote.sellAmount}`);
|
|
1936
1939
|
logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => buyToken: ${quote.buyTokenAddress}`);
|
|
1937
1940
|
logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => buyAmount: ${quote.buyAmount}, minAmount: ${_minAmount}`);
|
|
1938
1941
|
const swapInfo = {
|
|
@@ -15438,7 +15441,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
15438
15441
|
};
|
|
15439
15442
|
}
|
|
15440
15443
|
}
|
|
15441
|
-
const ratioWeb3Number =
|
|
15444
|
+
const ratioWeb3Number = this.getRatio(sampleAmount0, sampleAmount1);
|
|
15442
15445
|
const ratio = Number(ratioWeb3Number.toFixed(18));
|
|
15443
15446
|
logger.verbose(
|
|
15444
15447
|
`${_EkuboCLVault.name}: ${this.metadata.name} => ratio: ${ratio.toString()}`
|
|
@@ -15470,11 +15473,18 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
15470
15473
|
);
|
|
15471
15474
|
}
|
|
15472
15475
|
}
|
|
15476
|
+
getRatio(token0Amt, token1Amount) {
|
|
15477
|
+
const ratio = token0Amt.multipliedBy(1e18).dividedBy(token1Amount.toString()).dividedBy(1e18);
|
|
15478
|
+
logger.verbose(
|
|
15479
|
+
`${_EkuboCLVault.name}: getRatio => token0Amt: ${token0Amt.toString()}, token1Amount: ${token1Amount.toString()}, ratio: ${ratio.toString()}`
|
|
15480
|
+
);
|
|
15481
|
+
return ratio;
|
|
15482
|
+
}
|
|
15473
15483
|
_solveExpectedAmountsEq(availableAmount0, availableAmount1, ratio, price) {
|
|
15474
15484
|
const y = ratio.multipliedBy(availableAmount1).minus(availableAmount0).dividedBy(ratio.plus(1 / price));
|
|
15475
15485
|
const x = y.dividedBy(price);
|
|
15476
15486
|
logger.verbose(
|
|
15477
|
-
`${_EkuboCLVault.name}: _solveExpectedAmountsEq => x: ${x.toString()}, y: ${y.toString()}, amount0: ${availableAmount0.toString()}, amount1: ${availableAmount1.toString()}`
|
|
15487
|
+
`${_EkuboCLVault.name}: _solveExpectedAmountsEq => ratio: ${ratio.toString()}, x: ${x.toString()}, y: ${y.toString()}, amount0: ${availableAmount0.toString()}, amount1: ${availableAmount1.toString()}`
|
|
15478
15488
|
);
|
|
15479
15489
|
if (ratio.eq(0)) {
|
|
15480
15490
|
return {
|
|
@@ -15524,7 +15534,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
15524
15534
|
}
|
|
15525
15535
|
};
|
|
15526
15536
|
}
|
|
15527
|
-
async getSwapInfoToHandleUnused(considerRebalance = true, newBounds = null, maxIterations = 20) {
|
|
15537
|
+
async getSwapInfoToHandleUnused(considerRebalance = true, newBounds = null, maxIterations = 20, priceRatioPrecision = 4) {
|
|
15528
15538
|
const poolKey = await this.getPoolKey();
|
|
15529
15539
|
const unusedBalances = await this.unusedBalances(poolKey);
|
|
15530
15540
|
const { amount: token0Bal1, usdValue: token0PriceUsd } = unusedBalances.token0;
|
|
@@ -15557,15 +15567,59 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
15557
15567
|
logger.verbose(
|
|
15558
15568
|
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${ekuboBounds.lowerTick}, ${ekuboBounds.upperTick}`
|
|
15559
15569
|
);
|
|
15570
|
+
this.assertValidBounds(ekuboBounds);
|
|
15560
15571
|
return await this.getSwapInfoGivenAmounts(
|
|
15561
15572
|
poolKey,
|
|
15562
15573
|
token0Bal,
|
|
15563
15574
|
token1Bal,
|
|
15564
15575
|
ekuboBounds,
|
|
15565
|
-
maxIterations
|
|
15576
|
+
maxIterations,
|
|
15577
|
+
priceRatioPrecision
|
|
15566
15578
|
);
|
|
15567
15579
|
}
|
|
15568
|
-
|
|
15580
|
+
assertValidBounds(bounds) {
|
|
15581
|
+
assert(
|
|
15582
|
+
bounds.lowerTick < bounds.upperTick,
|
|
15583
|
+
`Invalid bounds: lowerTick (${bounds.lowerTick}) must be less than upperTick (${bounds.upperTick})`
|
|
15584
|
+
);
|
|
15585
|
+
assert(Number(bounds.lowerTick) % Number(this.poolKey?.tick_spacing) === 0, `Lower tick (${bounds.lowerTick}) must be a multiple of tick spacing (${this.poolKey?.tick_spacing})`);
|
|
15586
|
+
assert(Number(bounds.upperTick) % Number(this.poolKey?.tick_spacing) === 0, `Upper tick (${bounds.upperTick}) must be a multiple of tick spacing (${this.poolKey?.tick_spacing})`);
|
|
15587
|
+
}
|
|
15588
|
+
// Helper to check for invalid states:
|
|
15589
|
+
// Throws if both tokens are decreased or both are increased, which is not expected
|
|
15590
|
+
assertValidAmounts(expectedAmounts, token0Bal, token1Bal) {
|
|
15591
|
+
if (expectedAmounts.amount0.lessThan(token0Bal) && expectedAmounts.amount1.lessThan(token1Bal)) {
|
|
15592
|
+
throw new Error("Both tokens are decreased, something is wrong");
|
|
15593
|
+
}
|
|
15594
|
+
if (expectedAmounts.amount0.greaterThan(token0Bal) && expectedAmounts.amount1.greaterThan(token1Bal)) {
|
|
15595
|
+
throw new Error("Both tokens are increased, something is wrong");
|
|
15596
|
+
}
|
|
15597
|
+
}
|
|
15598
|
+
// Helper to determine which token to sell, which to buy, and the amounts to use
|
|
15599
|
+
getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal) {
|
|
15600
|
+
const tokenToSell = expectedAmounts.amount0.lessThan(token0Bal) ? poolKey.token0 : poolKey.token1;
|
|
15601
|
+
const tokenToBuy = tokenToSell == poolKey.token0 ? poolKey.token1 : poolKey.token0;
|
|
15602
|
+
const amountToSell = tokenToSell == poolKey.token0 ? token0Bal.minus(expectedAmounts.amount0) : token1Bal.minus(expectedAmounts.amount1);
|
|
15603
|
+
if (amountToSell.eq(0)) {
|
|
15604
|
+
throw new Error(
|
|
15605
|
+
`No amount to sell for ${tokenToSell.address}`
|
|
15606
|
+
);
|
|
15607
|
+
}
|
|
15608
|
+
const remainingSellAmount = tokenToSell == poolKey.token0 ? expectedAmounts.amount0 : expectedAmounts.amount1;
|
|
15609
|
+
return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
|
|
15610
|
+
}
|
|
15611
|
+
/**
|
|
15612
|
+
* @description Calculates swap info based on given amounts of token0 and token1
|
|
15613
|
+
* Use token0 and token1 balances to determine the expected amounts for new bounds
|
|
15614
|
+
* @param poolKey
|
|
15615
|
+
* @param token0Bal
|
|
15616
|
+
* @param token1Bal
|
|
15617
|
+
* @param bounds // new bounds
|
|
15618
|
+
* @param maxIterations
|
|
15619
|
+
* @returns {Promise<SwapInfo>}
|
|
15620
|
+
*
|
|
15621
|
+
*/
|
|
15622
|
+
async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds, maxIterations = 20, priceRatioPrecision = 4) {
|
|
15569
15623
|
logger.verbose(
|
|
15570
15624
|
`${_EkuboCLVault.name}: getSwapInfoGivenAmounts::pre => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`
|
|
15571
15625
|
);
|
|
@@ -15579,28 +15633,13 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
15579
15633
|
);
|
|
15580
15634
|
let retry = 0;
|
|
15581
15635
|
const maxRetry = maxIterations;
|
|
15582
|
-
function assertValidAmounts(expectedAmounts2, token0Bal2, token1Bal2) {
|
|
15583
|
-
if (expectedAmounts2.amount0.lessThan(token0Bal2) && expectedAmounts2.amount1.lessThan(token1Bal2)) {
|
|
15584
|
-
throw new Error("Both tokens are decreased, something is wrong");
|
|
15585
|
-
}
|
|
15586
|
-
if (expectedAmounts2.amount0.greaterThan(token0Bal2) && expectedAmounts2.amount1.greaterThan(token1Bal2)) {
|
|
15587
|
-
throw new Error("Both tokens are increased, something is wrong");
|
|
15588
|
-
}
|
|
15589
|
-
}
|
|
15590
|
-
function getSwapParams(expectedAmounts2, poolKey2, token0Bal2, token1Bal2) {
|
|
15591
|
-
const tokenToSell = expectedAmounts2.amount0.lessThan(token0Bal2) ? poolKey2.token0 : poolKey2.token1;
|
|
15592
|
-
const tokenToBuy = tokenToSell == poolKey2.token0 ? poolKey2.token1 : poolKey2.token0;
|
|
15593
|
-
const amountToSell = tokenToSell == poolKey2.token0 ? token0Bal2.minus(expectedAmounts2.amount0) : token1Bal2.minus(expectedAmounts2.amount1);
|
|
15594
|
-
const remainingSellAmount = tokenToSell == poolKey2.token0 ? expectedAmounts2.amount0 : expectedAmounts2.amount1;
|
|
15595
|
-
return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
|
|
15596
|
-
}
|
|
15597
15636
|
while (retry < maxRetry) {
|
|
15598
15637
|
retry++;
|
|
15599
15638
|
logger.verbose(
|
|
15600
15639
|
`getSwapInfoGivenAmounts::Retry attempt: ${retry}/${maxRetry}`
|
|
15601
15640
|
);
|
|
15602
|
-
assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
|
|
15603
|
-
const { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount } = getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal);
|
|
15641
|
+
this.assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
|
|
15642
|
+
const { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount } = this.getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal);
|
|
15604
15643
|
const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
|
|
15605
15644
|
const expectedRatio = expectedAmounts.ratio;
|
|
15606
15645
|
logger.verbose(
|
|
@@ -15638,6 +15677,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
15638
15677
|
quote.buyAmount.toString(),
|
|
15639
15678
|
tokenToBuyInfo.decimals
|
|
15640
15679
|
);
|
|
15680
|
+
logger.verbose(
|
|
15681
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => amountOut: ${amountOut.toString()}`
|
|
15682
|
+
);
|
|
15641
15683
|
const swapPrice = tokenToSell == poolKey.token0 ? amountOut.dividedBy(amountToSell) : amountToSell.dividedBy(amountOut);
|
|
15642
15684
|
const newRatio = tokenToSell == poolKey.token0 ? remainingSellAmount.dividedBy(token1Bal.plus(amountOut)) : token0Bal.plus(amountOut).dividedBy(remainingSellAmount);
|
|
15643
15685
|
logger.verbose(
|
|
@@ -15647,31 +15689,46 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
15647
15689
|
newRatio: newRatio.toString()
|
|
15648
15690
|
})}`
|
|
15649
15691
|
);
|
|
15650
|
-
const expectedPrecision = Math.min(
|
|
15651
|
-
|
|
15652
|
-
|
|
15692
|
+
const expectedPrecision = Math.min(priceRatioPrecision);
|
|
15693
|
+
const isWithInTolerance = Number(newRatio.toString()) <= expectedRatio * (1 + 1 / 10 ** expectedPrecision) && Number(newRatio.toString()) >= expectedRatio * (1 - 1 / 10 ** expectedPrecision);
|
|
15694
|
+
const currentPrecision = (expectedRatio - Number(newRatio.toString())) / expectedRatio;
|
|
15695
|
+
logger.verbose(
|
|
15696
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => isWithInTolerance: ${isWithInTolerance}, currentPrecision: ${currentPrecision.toString()}, expectedPrecision: ${expectedPrecision}`
|
|
15697
|
+
);
|
|
15698
|
+
if (!isWithInTolerance) {
|
|
15699
|
+
const expectedAmountsNew = await this._solveExpectedAmountsEq(
|
|
15653
15700
|
token0Bal,
|
|
15654
15701
|
token1Bal,
|
|
15655
15702
|
new Web3Number(Number(expectedRatio).toFixed(13), 18),
|
|
15656
15703
|
Number(swapPrice.toString())
|
|
15657
15704
|
);
|
|
15658
15705
|
logger.verbose(
|
|
15659
|
-
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${
|
|
15706
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmountsNew.amount0.toString()}, ${expectedAmountsNew.amount1.toString()}`
|
|
15660
15707
|
);
|
|
15708
|
+
if (expectedAmountsNew.amount0.eq(expectedAmounts.amount0.toString()) && expectedAmountsNew.amount1.eq(expectedAmounts.amount1.toString())) {
|
|
15709
|
+
logger.error(
|
|
15710
|
+
`getSwapInfoGivenAmounts: stuck in loop, expected amounts did not change`
|
|
15711
|
+
);
|
|
15712
|
+
throw new Error("Stuck in loop, expected amounts did not change");
|
|
15713
|
+
}
|
|
15714
|
+
expectedAmounts = expectedAmountsNew;
|
|
15661
15715
|
} else {
|
|
15662
15716
|
const minAmountOut = Web3Number.fromWei(
|
|
15663
15717
|
quote.buyAmount.toString(),
|
|
15664
15718
|
tokenToBuyInfo.decimals
|
|
15665
15719
|
).multipliedBy(0.9999);
|
|
15666
|
-
|
|
15720
|
+
const output = await this.avnu.getSwapInfo(
|
|
15667
15721
|
quote,
|
|
15668
15722
|
this.address.address,
|
|
15669
15723
|
0,
|
|
15670
15724
|
this.address.address,
|
|
15671
15725
|
minAmountOut.toWei()
|
|
15672
15726
|
);
|
|
15727
|
+
logger.verbose(
|
|
15728
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => swap info found: ${JSON.stringify(output)}`
|
|
15729
|
+
);
|
|
15730
|
+
return output;
|
|
15673
15731
|
}
|
|
15674
|
-
retry++;
|
|
15675
15732
|
}
|
|
15676
15733
|
throw new Error("Failed to get swap info");
|
|
15677
15734
|
}
|
|
@@ -15841,7 +15898,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
15841
15898
|
amount1
|
|
15842
15899
|
};
|
|
15843
15900
|
}
|
|
15844
|
-
async harvest(acc, maxIterations = 20) {
|
|
15901
|
+
async harvest(acc, maxIterations = 20, priceRatioPrecision = 4) {
|
|
15845
15902
|
const ekuboHarvests = new EkuboHarvests(this.config);
|
|
15846
15903
|
const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
|
|
15847
15904
|
this.address
|
|
@@ -15871,7 +15928,8 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
15871
15928
|
token0Amt,
|
|
15872
15929
|
token1Amt,
|
|
15873
15930
|
bounds,
|
|
15874
|
-
maxIterations
|
|
15931
|
+
maxIterations,
|
|
15932
|
+
priceRatioPrecision
|
|
15875
15933
|
);
|
|
15876
15934
|
swapInfo.token_to_address = token0Info.address.address;
|
|
15877
15935
|
logger.verbose(
|
package/dist/index.d.ts
CHANGED
|
@@ -699,6 +699,7 @@ declare class EkuboCLVault extends BaseStrategy<DualTokenInfo, DualActionAmount>
|
|
|
699
699
|
* @returns {amount0, amount1}
|
|
700
700
|
*/
|
|
701
701
|
private _getExpectedAmountsForLiquidity;
|
|
702
|
+
getRatio(token0Amt: Web3Number, token1Amount: Web3Number): Web3Number;
|
|
702
703
|
private _solveExpectedAmountsEq;
|
|
703
704
|
unusedBalances(_poolKey?: EkuboPoolKey): Promise<{
|
|
704
705
|
token0: {
|
|
@@ -712,8 +713,27 @@ declare class EkuboCLVault extends BaseStrategy<DualTokenInfo, DualActionAmount>
|
|
|
712
713
|
usdValue: number;
|
|
713
714
|
};
|
|
714
715
|
}>;
|
|
715
|
-
getSwapInfoToHandleUnused(considerRebalance?: boolean, newBounds?: EkuboBounds | null, maxIterations?: number): Promise<SwapInfo>;
|
|
716
|
-
|
|
716
|
+
getSwapInfoToHandleUnused(considerRebalance?: boolean, newBounds?: EkuboBounds | null, maxIterations?: number, priceRatioPrecision?: number): Promise<SwapInfo>;
|
|
717
|
+
assertValidBounds(bounds: EkuboBounds): void;
|
|
718
|
+
assertValidAmounts(expectedAmounts: any, token0Bal: Web3Number, token1Bal: Web3Number): void;
|
|
719
|
+
getSwapParams(expectedAmounts: any, poolKey: EkuboPoolKey, token0Bal: Web3Number, token1Bal: Web3Number): {
|
|
720
|
+
tokenToSell: ContractAddr;
|
|
721
|
+
tokenToBuy: ContractAddr;
|
|
722
|
+
amountToSell: Web3Number;
|
|
723
|
+
remainingSellAmount: any;
|
|
724
|
+
};
|
|
725
|
+
/**
|
|
726
|
+
* @description Calculates swap info based on given amounts of token0 and token1
|
|
727
|
+
* Use token0 and token1 balances to determine the expected amounts for new bounds
|
|
728
|
+
* @param poolKey
|
|
729
|
+
* @param token0Bal
|
|
730
|
+
* @param token1Bal
|
|
731
|
+
* @param bounds // new bounds
|
|
732
|
+
* @param maxIterations
|
|
733
|
+
* @returns {Promise<SwapInfo>}
|
|
734
|
+
*
|
|
735
|
+
*/
|
|
736
|
+
getSwapInfoGivenAmounts(poolKey: EkuboPoolKey, token0Bal: Web3Number, token1Bal: Web3Number, bounds: EkuboBounds, maxIterations?: number, priceRatioPrecision?: number): Promise<SwapInfo>;
|
|
717
737
|
/**
|
|
718
738
|
* Attempts to rebalance the vault by iteratively adjusting swap amounts if initial attempt fails.
|
|
719
739
|
* Uses binary search approach to find optimal swap amount.
|
|
@@ -746,7 +766,7 @@ declare class EkuboCLVault extends BaseStrategy<DualTokenInfo, DualActionAmount>
|
|
|
746
766
|
amount0: Web3Number;
|
|
747
767
|
amount1: Web3Number;
|
|
748
768
|
}>;
|
|
749
|
-
harvest(acc: Account, maxIterations?: number): Promise<Call[]>;
|
|
769
|
+
harvest(acc: Account, maxIterations?: number, priceRatioPrecision?: number): Promise<Call[]>;
|
|
750
770
|
getInvestmentFlows(): Promise<IInvestmentFlow[]>;
|
|
751
771
|
}
|
|
752
772
|
/**
|