@strkfarm/sdk 1.0.51 → 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 +94 -35
- package/dist/index.browser.mjs +94 -35
- package/dist/index.d.ts +24 -4
- package/dist/index.js +94 -35
- package/dist/index.mjs +94 -35
- package/package.json +4 -3
- package/src/modules/avnu.ts +7 -2
- package/src/strategies/ekubo-cl-vault.tsx +136 -71
|
@@ -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) {
|
|
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,14 +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
|
-
ekuboBounds
|
|
51782
|
+
ekuboBounds,
|
|
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})`
|
|
51772
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})`);
|
|
51773
51794
|
}
|
|
51774
|
-
|
|
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) {
|
|
51775
51830
|
logger.verbose(
|
|
51776
51831
|
`${_EkuboCLVault.name}: getSwapInfoGivenAmounts::pre => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`
|
|
51777
51832
|
);
|
|
@@ -51784,29 +51839,14 @@ var strkfarm_risk_engine = (() => {
|
|
|
51784
51839
|
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts2: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
|
|
51785
51840
|
);
|
|
51786
51841
|
let retry = 0;
|
|
51787
|
-
const maxRetry =
|
|
51788
|
-
function assertValidAmounts(expectedAmounts2, token0Bal2, token1Bal2) {
|
|
51789
|
-
if (expectedAmounts2.amount0.lessThan(token0Bal2) && expectedAmounts2.amount1.lessThan(token1Bal2)) {
|
|
51790
|
-
throw new Error("Both tokens are decreased, something is wrong");
|
|
51791
|
-
}
|
|
51792
|
-
if (expectedAmounts2.amount0.greaterThan(token0Bal2) && expectedAmounts2.amount1.greaterThan(token1Bal2)) {
|
|
51793
|
-
throw new Error("Both tokens are increased, something is wrong");
|
|
51794
|
-
}
|
|
51795
|
-
}
|
|
51796
|
-
function getSwapParams(expectedAmounts2, poolKey2, token0Bal2, token1Bal2) {
|
|
51797
|
-
const tokenToSell = expectedAmounts2.amount0.lessThan(token0Bal2) ? poolKey2.token0 : poolKey2.token1;
|
|
51798
|
-
const tokenToBuy = tokenToSell == poolKey2.token0 ? poolKey2.token1 : poolKey2.token0;
|
|
51799
|
-
const amountToSell = tokenToSell == poolKey2.token0 ? token0Bal2.minus(expectedAmounts2.amount0) : token1Bal2.minus(expectedAmounts2.amount1);
|
|
51800
|
-
const remainingSellAmount = tokenToSell == poolKey2.token0 ? expectedAmounts2.amount0 : expectedAmounts2.amount1;
|
|
51801
|
-
return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
|
|
51802
|
-
}
|
|
51842
|
+
const maxRetry = maxIterations;
|
|
51803
51843
|
while (retry < maxRetry) {
|
|
51804
51844
|
retry++;
|
|
51805
51845
|
logger.verbose(
|
|
51806
51846
|
`getSwapInfoGivenAmounts::Retry attempt: ${retry}/${maxRetry}`
|
|
51807
51847
|
);
|
|
51808
|
-
assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
|
|
51809
|
-
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);
|
|
51810
51850
|
const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
|
|
51811
51851
|
const expectedRatio = expectedAmounts.ratio;
|
|
51812
51852
|
logger.verbose(
|
|
@@ -51844,6 +51884,9 @@ var strkfarm_risk_engine = (() => {
|
|
|
51844
51884
|
quote.buyAmount.toString(),
|
|
51845
51885
|
tokenToBuyInfo.decimals
|
|
51846
51886
|
);
|
|
51887
|
+
logger.verbose(
|
|
51888
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => amountOut: ${amountOut.toString()}`
|
|
51889
|
+
);
|
|
51847
51890
|
const swapPrice = tokenToSell == poolKey.token0 ? amountOut.dividedBy(amountToSell) : amountToSell.dividedBy(amountOut);
|
|
51848
51891
|
const newRatio = tokenToSell == poolKey.token0 ? remainingSellAmount.dividedBy(token1Bal.plus(amountOut)) : token0Bal.plus(amountOut).dividedBy(remainingSellAmount);
|
|
51849
51892
|
logger.verbose(
|
|
@@ -51853,31 +51896,46 @@ var strkfarm_risk_engine = (() => {
|
|
|
51853
51896
|
newRatio: newRatio.toString()
|
|
51854
51897
|
})}`
|
|
51855
51898
|
);
|
|
51856
|
-
const expectedPrecision = Math.min(
|
|
51857
|
-
|
|
51858
|
-
|
|
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(
|
|
51859
51907
|
token0Bal,
|
|
51860
51908
|
token1Bal,
|
|
51861
51909
|
new Web3Number(Number(expectedRatio).toFixed(13), 18),
|
|
51862
51910
|
Number(swapPrice.toString())
|
|
51863
51911
|
);
|
|
51864
51912
|
logger.verbose(
|
|
51865
|
-
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${
|
|
51913
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmountsNew.amount0.toString()}, ${expectedAmountsNew.amount1.toString()}`
|
|
51866
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;
|
|
51867
51922
|
} else {
|
|
51868
51923
|
const minAmountOut = Web3Number.fromWei(
|
|
51869
51924
|
quote.buyAmount.toString(),
|
|
51870
51925
|
tokenToBuyInfo.decimals
|
|
51871
51926
|
).multipliedBy(0.9999);
|
|
51872
|
-
|
|
51927
|
+
const output3 = await this.avnu.getSwapInfo(
|
|
51873
51928
|
quote,
|
|
51874
51929
|
this.address.address,
|
|
51875
51930
|
0,
|
|
51876
51931
|
this.address.address,
|
|
51877
51932
|
minAmountOut.toWei()
|
|
51878
51933
|
);
|
|
51934
|
+
logger.verbose(
|
|
51935
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => swap info found: ${JSON.stringify(output3)}`
|
|
51936
|
+
);
|
|
51937
|
+
return output3;
|
|
51879
51938
|
}
|
|
51880
|
-
retry++;
|
|
51881
51939
|
}
|
|
51882
51940
|
throw new Error("Failed to get swap info");
|
|
51883
51941
|
}
|
|
@@ -51894,8 +51952,7 @@ var strkfarm_risk_engine = (() => {
|
|
|
51894
51952
|
* @returns Array of contract calls needed for rebalancing
|
|
51895
51953
|
* @throws Error if max retries reached without successful rebalance
|
|
51896
51954
|
*/
|
|
51897
|
-
async rebalanceIter(swapInfo, acc, estimateCall, isSellTokenToken0 = true, retry = 0, lowerLimit = 0n, upperLimit = 0n) {
|
|
51898
|
-
const MAX_RETRIES = 40;
|
|
51955
|
+
async rebalanceIter(swapInfo, acc, estimateCall, isSellTokenToken0 = true, retry = 0, lowerLimit = 0n, upperLimit = 0n, MAX_RETRIES = 40) {
|
|
51899
51956
|
logger.verbose(
|
|
51900
51957
|
`Rebalancing ${this.metadata.name}: retry=${retry}, lowerLimit=${lowerLimit}, upperLimit=${upperLimit}, isSellTokenToken0=${isSellTokenToken0}`
|
|
51901
51958
|
);
|
|
@@ -52048,7 +52105,7 @@ var strkfarm_risk_engine = (() => {
|
|
|
52048
52105
|
amount1
|
|
52049
52106
|
};
|
|
52050
52107
|
}
|
|
52051
|
-
async harvest(acc) {
|
|
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
|
|
@@ -52077,7 +52134,9 @@ var strkfarm_risk_engine = (() => {
|
|
|
52077
52134
|
poolKey,
|
|
52078
52135
|
token0Amt,
|
|
52079
52136
|
token1Amt,
|
|
52080
|
-
bounds
|
|
52137
|
+
bounds,
|
|
52138
|
+
maxIterations,
|
|
52139
|
+
priceRatioPrecision
|
|
52081
52140
|
);
|
|
52082
52141
|
swapInfo.token_to_address = token0Info.address.address;
|
|
52083
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) {
|
|
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,14 +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
|
-
ekuboBounds
|
|
15575
|
+
ekuboBounds,
|
|
15576
|
+
maxIterations,
|
|
15577
|
+
priceRatioPrecision
|
|
15578
|
+
);
|
|
15579
|
+
}
|
|
15580
|
+
assertValidBounds(bounds) {
|
|
15581
|
+
assert(
|
|
15582
|
+
bounds.lowerTick < bounds.upperTick,
|
|
15583
|
+
`Invalid bounds: lowerTick (${bounds.lowerTick}) must be less than upperTick (${bounds.upperTick})`
|
|
15565
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
|
+
}
|
|
15566
15597
|
}
|
|
15567
|
-
|
|
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) {
|
|
15568
15623
|
logger.verbose(
|
|
15569
15624
|
`${_EkuboCLVault.name}: getSwapInfoGivenAmounts::pre => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`
|
|
15570
15625
|
);
|
|
@@ -15577,29 +15632,14 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
15577
15632
|
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts2: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
|
|
15578
15633
|
);
|
|
15579
15634
|
let retry = 0;
|
|
15580
|
-
const maxRetry =
|
|
15581
|
-
function assertValidAmounts(expectedAmounts2, token0Bal2, token1Bal2) {
|
|
15582
|
-
if (expectedAmounts2.amount0.lessThan(token0Bal2) && expectedAmounts2.amount1.lessThan(token1Bal2)) {
|
|
15583
|
-
throw new Error("Both tokens are decreased, something is wrong");
|
|
15584
|
-
}
|
|
15585
|
-
if (expectedAmounts2.amount0.greaterThan(token0Bal2) && expectedAmounts2.amount1.greaterThan(token1Bal2)) {
|
|
15586
|
-
throw new Error("Both tokens are increased, something is wrong");
|
|
15587
|
-
}
|
|
15588
|
-
}
|
|
15589
|
-
function getSwapParams(expectedAmounts2, poolKey2, token0Bal2, token1Bal2) {
|
|
15590
|
-
const tokenToSell = expectedAmounts2.amount0.lessThan(token0Bal2) ? poolKey2.token0 : poolKey2.token1;
|
|
15591
|
-
const tokenToBuy = tokenToSell == poolKey2.token0 ? poolKey2.token1 : poolKey2.token0;
|
|
15592
|
-
const amountToSell = tokenToSell == poolKey2.token0 ? token0Bal2.minus(expectedAmounts2.amount0) : token1Bal2.minus(expectedAmounts2.amount1);
|
|
15593
|
-
const remainingSellAmount = tokenToSell == poolKey2.token0 ? expectedAmounts2.amount0 : expectedAmounts2.amount1;
|
|
15594
|
-
return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
|
|
15595
|
-
}
|
|
15635
|
+
const maxRetry = maxIterations;
|
|
15596
15636
|
while (retry < maxRetry) {
|
|
15597
15637
|
retry++;
|
|
15598
15638
|
logger.verbose(
|
|
15599
15639
|
`getSwapInfoGivenAmounts::Retry attempt: ${retry}/${maxRetry}`
|
|
15600
15640
|
);
|
|
15601
|
-
assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
|
|
15602
|
-
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);
|
|
15603
15643
|
const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
|
|
15604
15644
|
const expectedRatio = expectedAmounts.ratio;
|
|
15605
15645
|
logger.verbose(
|
|
@@ -15637,6 +15677,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
15637
15677
|
quote.buyAmount.toString(),
|
|
15638
15678
|
tokenToBuyInfo.decimals
|
|
15639
15679
|
);
|
|
15680
|
+
logger.verbose(
|
|
15681
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => amountOut: ${amountOut.toString()}`
|
|
15682
|
+
);
|
|
15640
15683
|
const swapPrice = tokenToSell == poolKey.token0 ? amountOut.dividedBy(amountToSell) : amountToSell.dividedBy(amountOut);
|
|
15641
15684
|
const newRatio = tokenToSell == poolKey.token0 ? remainingSellAmount.dividedBy(token1Bal.plus(amountOut)) : token0Bal.plus(amountOut).dividedBy(remainingSellAmount);
|
|
15642
15685
|
logger.verbose(
|
|
@@ -15646,31 +15689,46 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
15646
15689
|
newRatio: newRatio.toString()
|
|
15647
15690
|
})}`
|
|
15648
15691
|
);
|
|
15649
|
-
const expectedPrecision = Math.min(
|
|
15650
|
-
|
|
15651
|
-
|
|
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(
|
|
15652
15700
|
token0Bal,
|
|
15653
15701
|
token1Bal,
|
|
15654
15702
|
new Web3Number(Number(expectedRatio).toFixed(13), 18),
|
|
15655
15703
|
Number(swapPrice.toString())
|
|
15656
15704
|
);
|
|
15657
15705
|
logger.verbose(
|
|
15658
|
-
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${
|
|
15706
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmountsNew.amount0.toString()}, ${expectedAmountsNew.amount1.toString()}`
|
|
15659
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;
|
|
15660
15715
|
} else {
|
|
15661
15716
|
const minAmountOut = Web3Number.fromWei(
|
|
15662
15717
|
quote.buyAmount.toString(),
|
|
15663
15718
|
tokenToBuyInfo.decimals
|
|
15664
15719
|
).multipliedBy(0.9999);
|
|
15665
|
-
|
|
15720
|
+
const output = await this.avnu.getSwapInfo(
|
|
15666
15721
|
quote,
|
|
15667
15722
|
this.address.address,
|
|
15668
15723
|
0,
|
|
15669
15724
|
this.address.address,
|
|
15670
15725
|
minAmountOut.toWei()
|
|
15671
15726
|
);
|
|
15727
|
+
logger.verbose(
|
|
15728
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => swap info found: ${JSON.stringify(output)}`
|
|
15729
|
+
);
|
|
15730
|
+
return output;
|
|
15672
15731
|
}
|
|
15673
|
-
retry++;
|
|
15674
15732
|
}
|
|
15675
15733
|
throw new Error("Failed to get swap info");
|
|
15676
15734
|
}
|
|
@@ -15687,8 +15745,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
15687
15745
|
* @returns Array of contract calls needed for rebalancing
|
|
15688
15746
|
* @throws Error if max retries reached without successful rebalance
|
|
15689
15747
|
*/
|
|
15690
|
-
async rebalanceIter(swapInfo, acc, estimateCall, isSellTokenToken0 = true, retry = 0, lowerLimit = 0n, upperLimit = 0n) {
|
|
15691
|
-
const MAX_RETRIES = 40;
|
|
15748
|
+
async rebalanceIter(swapInfo, acc, estimateCall, isSellTokenToken0 = true, retry = 0, lowerLimit = 0n, upperLimit = 0n, MAX_RETRIES = 40) {
|
|
15692
15749
|
logger.verbose(
|
|
15693
15750
|
`Rebalancing ${this.metadata.name}: retry=${retry}, lowerLimit=${lowerLimit}, upperLimit=${upperLimit}, isSellTokenToken0=${isSellTokenToken0}`
|
|
15694
15751
|
);
|
|
@@ -15841,7 +15898,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
15841
15898
|
amount1
|
|
15842
15899
|
};
|
|
15843
15900
|
}
|
|
15844
|
-
async harvest(acc) {
|
|
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
|
|
@@ -15870,7 +15927,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
15870
15927
|
poolKey,
|
|
15871
15928
|
token0Amt,
|
|
15872
15929
|
token1Amt,
|
|
15873
|
-
bounds
|
|
15930
|
+
bounds,
|
|
15931
|
+
maxIterations,
|
|
15932
|
+
priceRatioPrecision
|
|
15874
15933
|
);
|
|
15875
15934
|
swapInfo.token_to_address = token0Info.address.address;
|
|
15876
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): 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.
|
|
@@ -727,7 +747,7 @@ declare class EkuboCLVault extends BaseStrategy<DualTokenInfo, DualActionAmount>
|
|
|
727
747
|
* @returns Array of contract calls needed for rebalancing
|
|
728
748
|
* @throws Error if max retries reached without successful rebalance
|
|
729
749
|
*/
|
|
730
|
-
rebalanceIter(swapInfo: SwapInfo, acc: Account, estimateCall: (swapInfo: SwapInfo) => Promise<Call[]>, isSellTokenToken0?: boolean, retry?: number, lowerLimit?: bigint, upperLimit?: bigint): Promise<Call[]>;
|
|
750
|
+
rebalanceIter(swapInfo: SwapInfo, acc: Account, estimateCall: (swapInfo: SwapInfo) => Promise<Call[]>, isSellTokenToken0?: boolean, retry?: number, lowerLimit?: bigint, upperLimit?: bigint, MAX_RETRIES?: number): Promise<Call[]>;
|
|
731
751
|
static tickToi129(tick: number): {
|
|
732
752
|
mag: number;
|
|
733
753
|
sign: number;
|
|
@@ -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): Promise<Call[]>;
|
|
769
|
+
harvest(acc: Account, maxIterations?: number, priceRatioPrecision?: number): Promise<Call[]>;
|
|
750
770
|
getInvestmentFlows(): Promise<IInvestmentFlow[]>;
|
|
751
771
|
}
|
|
752
772
|
/**
|