@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.
@@ -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
- if (quotes.length == 0) {
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
- return quotes[0];
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 = sampleAmount0.multipliedBy(1e18).dividedBy(sampleAmount1.toString()).dividedBy(1e18);
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
- async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds) {
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 = 10;
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(7, tokenToBuyInfo.decimals - 2);
51857
- if (Number(newRatio.toString()) > expectedRatio * (1 + 1 / 10 ** expectedPrecision) || Number(newRatio.toString()) < expectedRatio * (1 - 1 / 10 ** expectedPrecision)) {
51858
- expectedAmounts = await this._solveExpectedAmountsEq(
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: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
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
- return await this.avnu.getSwapInfo(
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(
@@ -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
- if (quotes.length == 0) {
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
- return quotes[0];
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 = sampleAmount0.multipliedBy(1e18).dividedBy(sampleAmount1.toString()).dividedBy(1e18);
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
- async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds) {
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 = 10;
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(7, tokenToBuyInfo.decimals - 2);
15650
- if (Number(newRatio.toString()) > expectedRatio * (1 + 1 / 10 ** expectedPrecision) || Number(newRatio.toString()) < expectedRatio * (1 - 1 / 10 ** expectedPrecision)) {
15651
- expectedAmounts = await this._solveExpectedAmountsEq(
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: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
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
- return await this.avnu.getSwapInfo(
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
- getSwapInfoGivenAmounts(poolKey: EkuboPoolKey, token0Bal: Web3Number, token1Bal: Web3Number, bounds: EkuboBounds): Promise<SwapInfo>;
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
  /**