@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.
@@ -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, 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
- async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds, maxIterations = 20) {
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(7, tokenToBuyInfo.decimals - 2);
51858
- if (Number(newRatio.toString()) > expectedRatio * (1 + 1 / 10 ** expectedPrecision) || Number(newRatio.toString()) < expectedRatio * (1 - 1 / 10 ** expectedPrecision)) {
51859
- 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(
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: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
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
- return await this.avnu.getSwapInfo(
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(
@@ -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, 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
- async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds, maxIterations = 20) {
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(7, tokenToBuyInfo.decimals - 2);
15651
- if (Number(newRatio.toString()) > expectedRatio * (1 + 1 / 10 ** expectedPrecision) || Number(newRatio.toString()) < expectedRatio * (1 - 1 / 10 ** expectedPrecision)) {
15652
- 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(
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: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
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
- return await this.avnu.getSwapInfo(
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
- getSwapInfoGivenAmounts(poolKey: EkuboPoolKey, token0Bal: Web3Number, token1Bal: Web3Number, bounds: EkuboBounds, maxIterations?: number): 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.
@@ -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
  /**