@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.js CHANGED
@@ -2022,14 +2022,16 @@ var AvnuWrapper = class _AvnuWrapper {
2022
2022
  };
2023
2023
  assert(fromToken != toToken, "From and to tokens are the same");
2024
2024
  const quotes = await (0, import_avnu_sdk.fetchQuotes)(params);
2025
- if (quotes.length == 0) {
2025
+ const filteredQuotes = quotes.filter((q) => q.sellAmount.toString() == amountWei);
2026
+ if (filteredQuotes.length == 0) {
2026
2027
  if (retry < MAX_RETRY) {
2027
2028
  await new Promise((res) => setTimeout(res, 3e3));
2028
2029
  return await this.getQuotes(fromToken, toToken, amountWei, taker, retry + 1);
2029
2030
  }
2030
2031
  throw new Error("no quotes found");
2031
2032
  }
2032
- return quotes[0];
2033
+ logger.verbose(`${_AvnuWrapper.name}: getQuotes => Found ${JSON.stringify(filteredQuotes[0])}`);
2034
+ return filteredQuotes[0];
2033
2035
  }
2034
2036
  async getSwapInfo(quote, taker, integratorFeeBps, integratorFeeRecipient, minAmount) {
2035
2037
  const calldata = await (0, import_avnu_sdk.fetchBuildExecuteTransaction)(quote.quoteId);
@@ -2052,6 +2054,7 @@ var AvnuWrapper = class _AvnuWrapper {
2052
2054
  startIndex += 5 + swap_params_len;
2053
2055
  }
2054
2056
  const _minAmount = minAmount || (quote.buyAmount * 95n / 100n).toString();
2057
+ logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => sellToken: ${quote.sellTokenAddress}, sellAmount: ${quote.sellAmount}`);
2055
2058
  logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => buyToken: ${quote.buyTokenAddress}`);
2056
2059
  logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => buyAmount: ${quote.buyAmount}, minAmount: ${_minAmount}`);
2057
2060
  const swapInfo = {
@@ -15553,7 +15556,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15553
15556
  };
15554
15557
  }
15555
15558
  }
15556
- const ratioWeb3Number = sampleAmount0.multipliedBy(1e18).dividedBy(sampleAmount1.toString()).dividedBy(1e18);
15559
+ const ratioWeb3Number = this.getRatio(sampleAmount0, sampleAmount1);
15557
15560
  const ratio = Number(ratioWeb3Number.toFixed(18));
15558
15561
  logger.verbose(
15559
15562
  `${_EkuboCLVault.name}: ${this.metadata.name} => ratio: ${ratio.toString()}`
@@ -15585,11 +15588,18 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15585
15588
  );
15586
15589
  }
15587
15590
  }
15591
+ getRatio(token0Amt, token1Amount) {
15592
+ const ratio = token0Amt.multipliedBy(1e18).dividedBy(token1Amount.toString()).dividedBy(1e18);
15593
+ logger.verbose(
15594
+ `${_EkuboCLVault.name}: getRatio => token0Amt: ${token0Amt.toString()}, token1Amount: ${token1Amount.toString()}, ratio: ${ratio.toString()}`
15595
+ );
15596
+ return ratio;
15597
+ }
15588
15598
  _solveExpectedAmountsEq(availableAmount0, availableAmount1, ratio, price) {
15589
15599
  const y = ratio.multipliedBy(availableAmount1).minus(availableAmount0).dividedBy(ratio.plus(1 / price));
15590
15600
  const x = y.dividedBy(price);
15591
15601
  logger.verbose(
15592
- `${_EkuboCLVault.name}: _solveExpectedAmountsEq => x: ${x.toString()}, y: ${y.toString()}, amount0: ${availableAmount0.toString()}, amount1: ${availableAmount1.toString()}`
15602
+ `${_EkuboCLVault.name}: _solveExpectedAmountsEq => ratio: ${ratio.toString()}, x: ${x.toString()}, y: ${y.toString()}, amount0: ${availableAmount0.toString()}, amount1: ${availableAmount1.toString()}`
15593
15603
  );
15594
15604
  if (ratio.eq(0)) {
15595
15605
  return {
@@ -15639,7 +15649,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15639
15649
  }
15640
15650
  };
15641
15651
  }
15642
- async getSwapInfoToHandleUnused(considerRebalance = true, newBounds = null) {
15652
+ async getSwapInfoToHandleUnused(considerRebalance = true, newBounds = null, maxIterations = 20, priceRatioPrecision = 4) {
15643
15653
  const poolKey = await this.getPoolKey();
15644
15654
  const unusedBalances = await this.unusedBalances(poolKey);
15645
15655
  const { amount: token0Bal1, usdValue: token0PriceUsd } = unusedBalances.token0;
@@ -15672,14 +15682,59 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15672
15682
  logger.verbose(
15673
15683
  `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${ekuboBounds.lowerTick}, ${ekuboBounds.upperTick}`
15674
15684
  );
15685
+ this.assertValidBounds(ekuboBounds);
15675
15686
  return await this.getSwapInfoGivenAmounts(
15676
15687
  poolKey,
15677
15688
  token0Bal,
15678
15689
  token1Bal,
15679
- ekuboBounds
15690
+ ekuboBounds,
15691
+ maxIterations,
15692
+ priceRatioPrecision
15693
+ );
15694
+ }
15695
+ assertValidBounds(bounds) {
15696
+ assert(
15697
+ bounds.lowerTick < bounds.upperTick,
15698
+ `Invalid bounds: lowerTick (${bounds.lowerTick}) must be less than upperTick (${bounds.upperTick})`
15680
15699
  );
15700
+ 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})`);
15701
+ 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})`);
15681
15702
  }
15682
- async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds) {
15703
+ // Helper to check for invalid states:
15704
+ // Throws if both tokens are decreased or both are increased, which is not expected
15705
+ assertValidAmounts(expectedAmounts, token0Bal, token1Bal) {
15706
+ if (expectedAmounts.amount0.lessThan(token0Bal) && expectedAmounts.amount1.lessThan(token1Bal)) {
15707
+ throw new Error("Both tokens are decreased, something is wrong");
15708
+ }
15709
+ if (expectedAmounts.amount0.greaterThan(token0Bal) && expectedAmounts.amount1.greaterThan(token1Bal)) {
15710
+ throw new Error("Both tokens are increased, something is wrong");
15711
+ }
15712
+ }
15713
+ // Helper to determine which token to sell, which to buy, and the amounts to use
15714
+ getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal) {
15715
+ const tokenToSell = expectedAmounts.amount0.lessThan(token0Bal) ? poolKey.token0 : poolKey.token1;
15716
+ const tokenToBuy = tokenToSell == poolKey.token0 ? poolKey.token1 : poolKey.token0;
15717
+ const amountToSell = tokenToSell == poolKey.token0 ? token0Bal.minus(expectedAmounts.amount0) : token1Bal.minus(expectedAmounts.amount1);
15718
+ if (amountToSell.eq(0)) {
15719
+ throw new Error(
15720
+ `No amount to sell for ${tokenToSell.address}`
15721
+ );
15722
+ }
15723
+ const remainingSellAmount = tokenToSell == poolKey.token0 ? expectedAmounts.amount0 : expectedAmounts.amount1;
15724
+ return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
15725
+ }
15726
+ /**
15727
+ * @description Calculates swap info based on given amounts of token0 and token1
15728
+ * Use token0 and token1 balances to determine the expected amounts for new bounds
15729
+ * @param poolKey
15730
+ * @param token0Bal
15731
+ * @param token1Bal
15732
+ * @param bounds // new bounds
15733
+ * @param maxIterations
15734
+ * @returns {Promise<SwapInfo>}
15735
+ *
15736
+ */
15737
+ async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds, maxIterations = 20, priceRatioPrecision = 4) {
15683
15738
  logger.verbose(
15684
15739
  `${_EkuboCLVault.name}: getSwapInfoGivenAmounts::pre => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`
15685
15740
  );
@@ -15692,29 +15747,14 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15692
15747
  `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts2: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
15693
15748
  );
15694
15749
  let retry = 0;
15695
- const maxRetry = 10;
15696
- function assertValidAmounts(expectedAmounts2, token0Bal2, token1Bal2) {
15697
- if (expectedAmounts2.amount0.lessThan(token0Bal2) && expectedAmounts2.amount1.lessThan(token1Bal2)) {
15698
- throw new Error("Both tokens are decreased, something is wrong");
15699
- }
15700
- if (expectedAmounts2.amount0.greaterThan(token0Bal2) && expectedAmounts2.amount1.greaterThan(token1Bal2)) {
15701
- throw new Error("Both tokens are increased, something is wrong");
15702
- }
15703
- }
15704
- function getSwapParams(expectedAmounts2, poolKey2, token0Bal2, token1Bal2) {
15705
- const tokenToSell = expectedAmounts2.amount0.lessThan(token0Bal2) ? poolKey2.token0 : poolKey2.token1;
15706
- const tokenToBuy = tokenToSell == poolKey2.token0 ? poolKey2.token1 : poolKey2.token0;
15707
- const amountToSell = tokenToSell == poolKey2.token0 ? token0Bal2.minus(expectedAmounts2.amount0) : token1Bal2.minus(expectedAmounts2.amount1);
15708
- const remainingSellAmount = tokenToSell == poolKey2.token0 ? expectedAmounts2.amount0 : expectedAmounts2.amount1;
15709
- return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
15710
- }
15750
+ const maxRetry = maxIterations;
15711
15751
  while (retry < maxRetry) {
15712
15752
  retry++;
15713
15753
  logger.verbose(
15714
15754
  `getSwapInfoGivenAmounts::Retry attempt: ${retry}/${maxRetry}`
15715
15755
  );
15716
- assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
15717
- const { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount } = getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal);
15756
+ this.assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
15757
+ const { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount } = this.getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal);
15718
15758
  const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
15719
15759
  const expectedRatio = expectedAmounts.ratio;
15720
15760
  logger.verbose(
@@ -15752,6 +15792,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15752
15792
  quote.buyAmount.toString(),
15753
15793
  tokenToBuyInfo.decimals
15754
15794
  );
15795
+ logger.verbose(
15796
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => amountOut: ${amountOut.toString()}`
15797
+ );
15755
15798
  const swapPrice = tokenToSell == poolKey.token0 ? amountOut.dividedBy(amountToSell) : amountToSell.dividedBy(amountOut);
15756
15799
  const newRatio = tokenToSell == poolKey.token0 ? remainingSellAmount.dividedBy(token1Bal.plus(amountOut)) : token0Bal.plus(amountOut).dividedBy(remainingSellAmount);
15757
15800
  logger.verbose(
@@ -15761,31 +15804,46 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15761
15804
  newRatio: newRatio.toString()
15762
15805
  })}`
15763
15806
  );
15764
- const expectedPrecision = Math.min(7, tokenToBuyInfo.decimals - 2);
15765
- if (Number(newRatio.toString()) > expectedRatio * (1 + 1 / 10 ** expectedPrecision) || Number(newRatio.toString()) < expectedRatio * (1 - 1 / 10 ** expectedPrecision)) {
15766
- expectedAmounts = await this._solveExpectedAmountsEq(
15807
+ const expectedPrecision = Math.min(priceRatioPrecision);
15808
+ const isWithInTolerance = Number(newRatio.toString()) <= expectedRatio * (1 + 1 / 10 ** expectedPrecision) && Number(newRatio.toString()) >= expectedRatio * (1 - 1 / 10 ** expectedPrecision);
15809
+ const currentPrecision = (expectedRatio - Number(newRatio.toString())) / expectedRatio;
15810
+ logger.verbose(
15811
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => isWithInTolerance: ${isWithInTolerance}, currentPrecision: ${currentPrecision.toString()}, expectedPrecision: ${expectedPrecision}`
15812
+ );
15813
+ if (!isWithInTolerance) {
15814
+ const expectedAmountsNew = await this._solveExpectedAmountsEq(
15767
15815
  token0Bal,
15768
15816
  token1Bal,
15769
15817
  new Web3Number(Number(expectedRatio).toFixed(13), 18),
15770
15818
  Number(swapPrice.toString())
15771
15819
  );
15772
15820
  logger.verbose(
15773
- `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
15821
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmountsNew.amount0.toString()}, ${expectedAmountsNew.amount1.toString()}`
15774
15822
  );
15823
+ if (expectedAmountsNew.amount0.eq(expectedAmounts.amount0.toString()) && expectedAmountsNew.amount1.eq(expectedAmounts.amount1.toString())) {
15824
+ logger.error(
15825
+ `getSwapInfoGivenAmounts: stuck in loop, expected amounts did not change`
15826
+ );
15827
+ throw new Error("Stuck in loop, expected amounts did not change");
15828
+ }
15829
+ expectedAmounts = expectedAmountsNew;
15775
15830
  } else {
15776
15831
  const minAmountOut = Web3Number.fromWei(
15777
15832
  quote.buyAmount.toString(),
15778
15833
  tokenToBuyInfo.decimals
15779
15834
  ).multipliedBy(0.9999);
15780
- return await this.avnu.getSwapInfo(
15835
+ const output = await this.avnu.getSwapInfo(
15781
15836
  quote,
15782
15837
  this.address.address,
15783
15838
  0,
15784
15839
  this.address.address,
15785
15840
  minAmountOut.toWei()
15786
15841
  );
15842
+ logger.verbose(
15843
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => swap info found: ${JSON.stringify(output)}`
15844
+ );
15845
+ return output;
15787
15846
  }
15788
- retry++;
15789
15847
  }
15790
15848
  throw new Error("Failed to get swap info");
15791
15849
  }
@@ -15802,8 +15860,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15802
15860
  * @returns Array of contract calls needed for rebalancing
15803
15861
  * @throws Error if max retries reached without successful rebalance
15804
15862
  */
15805
- async rebalanceIter(swapInfo, acc, estimateCall, isSellTokenToken0 = true, retry = 0, lowerLimit = 0n, upperLimit = 0n) {
15806
- const MAX_RETRIES = 40;
15863
+ async rebalanceIter(swapInfo, acc, estimateCall, isSellTokenToken0 = true, retry = 0, lowerLimit = 0n, upperLimit = 0n, MAX_RETRIES = 40) {
15807
15864
  logger.verbose(
15808
15865
  `Rebalancing ${this.metadata.name}: retry=${retry}, lowerLimit=${lowerLimit}, upperLimit=${upperLimit}, isSellTokenToken0=${isSellTokenToken0}`
15809
15866
  );
@@ -15956,7 +16013,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15956
16013
  amount1
15957
16014
  };
15958
16015
  }
15959
- async harvest(acc) {
16016
+ async harvest(acc, maxIterations = 20, priceRatioPrecision = 4) {
15960
16017
  const ekuboHarvests = new EkuboHarvests(this.config);
15961
16018
  const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
15962
16019
  this.address
@@ -15985,7 +16042,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15985
16042
  poolKey,
15986
16043
  token0Amt,
15987
16044
  token1Amt,
15988
- bounds
16045
+ bounds,
16046
+ maxIterations,
16047
+ priceRatioPrecision
15989
16048
  );
15990
16049
  swapInfo.token_to_address = token0Info.address.address;
15991
16050
  logger.verbose(
package/dist/index.mjs CHANGED
@@ -1952,14 +1952,16 @@ var AvnuWrapper = class _AvnuWrapper {
1952
1952
  };
1953
1953
  assert(fromToken != toToken, "From and to tokens are the same");
1954
1954
  const quotes = await fetchQuotes(params);
1955
- if (quotes.length == 0) {
1955
+ const filteredQuotes = quotes.filter((q) => q.sellAmount.toString() == amountWei);
1956
+ if (filteredQuotes.length == 0) {
1956
1957
  if (retry < MAX_RETRY) {
1957
1958
  await new Promise((res) => setTimeout(res, 3e3));
1958
1959
  return await this.getQuotes(fromToken, toToken, amountWei, taker, retry + 1);
1959
1960
  }
1960
1961
  throw new Error("no quotes found");
1961
1962
  }
1962
- return quotes[0];
1963
+ logger.verbose(`${_AvnuWrapper.name}: getQuotes => Found ${JSON.stringify(filteredQuotes[0])}`);
1964
+ return filteredQuotes[0];
1963
1965
  }
1964
1966
  async getSwapInfo(quote, taker, integratorFeeBps, integratorFeeRecipient, minAmount) {
1965
1967
  const calldata = await fetchBuildExecuteTransaction(quote.quoteId);
@@ -1982,6 +1984,7 @@ var AvnuWrapper = class _AvnuWrapper {
1982
1984
  startIndex += 5 + swap_params_len;
1983
1985
  }
1984
1986
  const _minAmount = minAmount || (quote.buyAmount * 95n / 100n).toString();
1987
+ logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => sellToken: ${quote.sellTokenAddress}, sellAmount: ${quote.sellAmount}`);
1985
1988
  logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => buyToken: ${quote.buyTokenAddress}`);
1986
1989
  logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => buyAmount: ${quote.buyAmount}, minAmount: ${_minAmount}`);
1987
1990
  const swapInfo = {
@@ -15487,7 +15490,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15487
15490
  };
15488
15491
  }
15489
15492
  }
15490
- const ratioWeb3Number = sampleAmount0.multipliedBy(1e18).dividedBy(sampleAmount1.toString()).dividedBy(1e18);
15493
+ const ratioWeb3Number = this.getRatio(sampleAmount0, sampleAmount1);
15491
15494
  const ratio = Number(ratioWeb3Number.toFixed(18));
15492
15495
  logger.verbose(
15493
15496
  `${_EkuboCLVault.name}: ${this.metadata.name} => ratio: ${ratio.toString()}`
@@ -15519,11 +15522,18 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15519
15522
  );
15520
15523
  }
15521
15524
  }
15525
+ getRatio(token0Amt, token1Amount) {
15526
+ const ratio = token0Amt.multipliedBy(1e18).dividedBy(token1Amount.toString()).dividedBy(1e18);
15527
+ logger.verbose(
15528
+ `${_EkuboCLVault.name}: getRatio => token0Amt: ${token0Amt.toString()}, token1Amount: ${token1Amount.toString()}, ratio: ${ratio.toString()}`
15529
+ );
15530
+ return ratio;
15531
+ }
15522
15532
  _solveExpectedAmountsEq(availableAmount0, availableAmount1, ratio, price) {
15523
15533
  const y = ratio.multipliedBy(availableAmount1).minus(availableAmount0).dividedBy(ratio.plus(1 / price));
15524
15534
  const x = y.dividedBy(price);
15525
15535
  logger.verbose(
15526
- `${_EkuboCLVault.name}: _solveExpectedAmountsEq => x: ${x.toString()}, y: ${y.toString()}, amount0: ${availableAmount0.toString()}, amount1: ${availableAmount1.toString()}`
15536
+ `${_EkuboCLVault.name}: _solveExpectedAmountsEq => ratio: ${ratio.toString()}, x: ${x.toString()}, y: ${y.toString()}, amount0: ${availableAmount0.toString()}, amount1: ${availableAmount1.toString()}`
15527
15537
  );
15528
15538
  if (ratio.eq(0)) {
15529
15539
  return {
@@ -15573,7 +15583,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15573
15583
  }
15574
15584
  };
15575
15585
  }
15576
- async getSwapInfoToHandleUnused(considerRebalance = true, newBounds = null) {
15586
+ async getSwapInfoToHandleUnused(considerRebalance = true, newBounds = null, maxIterations = 20, priceRatioPrecision = 4) {
15577
15587
  const poolKey = await this.getPoolKey();
15578
15588
  const unusedBalances = await this.unusedBalances(poolKey);
15579
15589
  const { amount: token0Bal1, usdValue: token0PriceUsd } = unusedBalances.token0;
@@ -15606,14 +15616,59 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15606
15616
  logger.verbose(
15607
15617
  `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${ekuboBounds.lowerTick}, ${ekuboBounds.upperTick}`
15608
15618
  );
15619
+ this.assertValidBounds(ekuboBounds);
15609
15620
  return await this.getSwapInfoGivenAmounts(
15610
15621
  poolKey,
15611
15622
  token0Bal,
15612
15623
  token1Bal,
15613
- ekuboBounds
15624
+ ekuboBounds,
15625
+ maxIterations,
15626
+ priceRatioPrecision
15627
+ );
15628
+ }
15629
+ assertValidBounds(bounds) {
15630
+ assert(
15631
+ bounds.lowerTick < bounds.upperTick,
15632
+ `Invalid bounds: lowerTick (${bounds.lowerTick}) must be less than upperTick (${bounds.upperTick})`
15614
15633
  );
15634
+ 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})`);
15635
+ 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})`);
15615
15636
  }
15616
- async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds) {
15637
+ // Helper to check for invalid states:
15638
+ // Throws if both tokens are decreased or both are increased, which is not expected
15639
+ assertValidAmounts(expectedAmounts, token0Bal, token1Bal) {
15640
+ if (expectedAmounts.amount0.lessThan(token0Bal) && expectedAmounts.amount1.lessThan(token1Bal)) {
15641
+ throw new Error("Both tokens are decreased, something is wrong");
15642
+ }
15643
+ if (expectedAmounts.amount0.greaterThan(token0Bal) && expectedAmounts.amount1.greaterThan(token1Bal)) {
15644
+ throw new Error("Both tokens are increased, something is wrong");
15645
+ }
15646
+ }
15647
+ // Helper to determine which token to sell, which to buy, and the amounts to use
15648
+ getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal) {
15649
+ const tokenToSell = expectedAmounts.amount0.lessThan(token0Bal) ? poolKey.token0 : poolKey.token1;
15650
+ const tokenToBuy = tokenToSell == poolKey.token0 ? poolKey.token1 : poolKey.token0;
15651
+ const amountToSell = tokenToSell == poolKey.token0 ? token0Bal.minus(expectedAmounts.amount0) : token1Bal.minus(expectedAmounts.amount1);
15652
+ if (amountToSell.eq(0)) {
15653
+ throw new Error(
15654
+ `No amount to sell for ${tokenToSell.address}`
15655
+ );
15656
+ }
15657
+ const remainingSellAmount = tokenToSell == poolKey.token0 ? expectedAmounts.amount0 : expectedAmounts.amount1;
15658
+ return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
15659
+ }
15660
+ /**
15661
+ * @description Calculates swap info based on given amounts of token0 and token1
15662
+ * Use token0 and token1 balances to determine the expected amounts for new bounds
15663
+ * @param poolKey
15664
+ * @param token0Bal
15665
+ * @param token1Bal
15666
+ * @param bounds // new bounds
15667
+ * @param maxIterations
15668
+ * @returns {Promise<SwapInfo>}
15669
+ *
15670
+ */
15671
+ async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds, maxIterations = 20, priceRatioPrecision = 4) {
15617
15672
  logger.verbose(
15618
15673
  `${_EkuboCLVault.name}: getSwapInfoGivenAmounts::pre => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`
15619
15674
  );
@@ -15626,29 +15681,14 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15626
15681
  `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts2: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
15627
15682
  );
15628
15683
  let retry = 0;
15629
- const maxRetry = 10;
15630
- function assertValidAmounts(expectedAmounts2, token0Bal2, token1Bal2) {
15631
- if (expectedAmounts2.amount0.lessThan(token0Bal2) && expectedAmounts2.amount1.lessThan(token1Bal2)) {
15632
- throw new Error("Both tokens are decreased, something is wrong");
15633
- }
15634
- if (expectedAmounts2.amount0.greaterThan(token0Bal2) && expectedAmounts2.amount1.greaterThan(token1Bal2)) {
15635
- throw new Error("Both tokens are increased, something is wrong");
15636
- }
15637
- }
15638
- function getSwapParams(expectedAmounts2, poolKey2, token0Bal2, token1Bal2) {
15639
- const tokenToSell = expectedAmounts2.amount0.lessThan(token0Bal2) ? poolKey2.token0 : poolKey2.token1;
15640
- const tokenToBuy = tokenToSell == poolKey2.token0 ? poolKey2.token1 : poolKey2.token0;
15641
- const amountToSell = tokenToSell == poolKey2.token0 ? token0Bal2.minus(expectedAmounts2.amount0) : token1Bal2.minus(expectedAmounts2.amount1);
15642
- const remainingSellAmount = tokenToSell == poolKey2.token0 ? expectedAmounts2.amount0 : expectedAmounts2.amount1;
15643
- return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
15644
- }
15684
+ const maxRetry = maxIterations;
15645
15685
  while (retry < maxRetry) {
15646
15686
  retry++;
15647
15687
  logger.verbose(
15648
15688
  `getSwapInfoGivenAmounts::Retry attempt: ${retry}/${maxRetry}`
15649
15689
  );
15650
- assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
15651
- const { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount } = getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal);
15690
+ this.assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
15691
+ const { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount } = this.getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal);
15652
15692
  const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
15653
15693
  const expectedRatio = expectedAmounts.ratio;
15654
15694
  logger.verbose(
@@ -15686,6 +15726,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15686
15726
  quote.buyAmount.toString(),
15687
15727
  tokenToBuyInfo.decimals
15688
15728
  );
15729
+ logger.verbose(
15730
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => amountOut: ${amountOut.toString()}`
15731
+ );
15689
15732
  const swapPrice = tokenToSell == poolKey.token0 ? amountOut.dividedBy(amountToSell) : amountToSell.dividedBy(amountOut);
15690
15733
  const newRatio = tokenToSell == poolKey.token0 ? remainingSellAmount.dividedBy(token1Bal.plus(amountOut)) : token0Bal.plus(amountOut).dividedBy(remainingSellAmount);
15691
15734
  logger.verbose(
@@ -15695,31 +15738,46 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15695
15738
  newRatio: newRatio.toString()
15696
15739
  })}`
15697
15740
  );
15698
- const expectedPrecision = Math.min(7, tokenToBuyInfo.decimals - 2);
15699
- if (Number(newRatio.toString()) > expectedRatio * (1 + 1 / 10 ** expectedPrecision) || Number(newRatio.toString()) < expectedRatio * (1 - 1 / 10 ** expectedPrecision)) {
15700
- expectedAmounts = await this._solveExpectedAmountsEq(
15741
+ const expectedPrecision = Math.min(priceRatioPrecision);
15742
+ const isWithInTolerance = Number(newRatio.toString()) <= expectedRatio * (1 + 1 / 10 ** expectedPrecision) && Number(newRatio.toString()) >= expectedRatio * (1 - 1 / 10 ** expectedPrecision);
15743
+ const currentPrecision = (expectedRatio - Number(newRatio.toString())) / expectedRatio;
15744
+ logger.verbose(
15745
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => isWithInTolerance: ${isWithInTolerance}, currentPrecision: ${currentPrecision.toString()}, expectedPrecision: ${expectedPrecision}`
15746
+ );
15747
+ if (!isWithInTolerance) {
15748
+ const expectedAmountsNew = await this._solveExpectedAmountsEq(
15701
15749
  token0Bal,
15702
15750
  token1Bal,
15703
15751
  new Web3Number(Number(expectedRatio).toFixed(13), 18),
15704
15752
  Number(swapPrice.toString())
15705
15753
  );
15706
15754
  logger.verbose(
15707
- `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
15755
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmountsNew.amount0.toString()}, ${expectedAmountsNew.amount1.toString()}`
15708
15756
  );
15757
+ if (expectedAmountsNew.amount0.eq(expectedAmounts.amount0.toString()) && expectedAmountsNew.amount1.eq(expectedAmounts.amount1.toString())) {
15758
+ logger.error(
15759
+ `getSwapInfoGivenAmounts: stuck in loop, expected amounts did not change`
15760
+ );
15761
+ throw new Error("Stuck in loop, expected amounts did not change");
15762
+ }
15763
+ expectedAmounts = expectedAmountsNew;
15709
15764
  } else {
15710
15765
  const minAmountOut = Web3Number.fromWei(
15711
15766
  quote.buyAmount.toString(),
15712
15767
  tokenToBuyInfo.decimals
15713
15768
  ).multipliedBy(0.9999);
15714
- return await this.avnu.getSwapInfo(
15769
+ const output = await this.avnu.getSwapInfo(
15715
15770
  quote,
15716
15771
  this.address.address,
15717
15772
  0,
15718
15773
  this.address.address,
15719
15774
  minAmountOut.toWei()
15720
15775
  );
15776
+ logger.verbose(
15777
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => swap info found: ${JSON.stringify(output)}`
15778
+ );
15779
+ return output;
15721
15780
  }
15722
- retry++;
15723
15781
  }
15724
15782
  throw new Error("Failed to get swap info");
15725
15783
  }
@@ -15736,8 +15794,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15736
15794
  * @returns Array of contract calls needed for rebalancing
15737
15795
  * @throws Error if max retries reached without successful rebalance
15738
15796
  */
15739
- async rebalanceIter(swapInfo, acc, estimateCall, isSellTokenToken0 = true, retry = 0, lowerLimit = 0n, upperLimit = 0n) {
15740
- const MAX_RETRIES = 40;
15797
+ async rebalanceIter(swapInfo, acc, estimateCall, isSellTokenToken0 = true, retry = 0, lowerLimit = 0n, upperLimit = 0n, MAX_RETRIES = 40) {
15741
15798
  logger.verbose(
15742
15799
  `Rebalancing ${this.metadata.name}: retry=${retry}, lowerLimit=${lowerLimit}, upperLimit=${upperLimit}, isSellTokenToken0=${isSellTokenToken0}`
15743
15800
  );
@@ -15890,7 +15947,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15890
15947
  amount1
15891
15948
  };
15892
15949
  }
15893
- async harvest(acc) {
15950
+ async harvest(acc, maxIterations = 20, priceRatioPrecision = 4) {
15894
15951
  const ekuboHarvests = new EkuboHarvests(this.config);
15895
15952
  const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
15896
15953
  this.address
@@ -15919,7 +15976,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15919
15976
  poolKey,
15920
15977
  token0Amt,
15921
15978
  token1Amt,
15922
- bounds
15979
+ bounds,
15980
+ maxIterations,
15981
+ priceRatioPrecision
15923
15982
  );
15924
15983
  swapInfo.token_to_address = token0Info.address.address;
15925
15984
  logger.verbose(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strkfarm/sdk",
3
- "version": "1.0.51",
3
+ "version": "1.0.53",
4
4
  "description": "STRKFarm TS SDK (Meant for our internal use, but feel free to use it)",
5
5
  "typings": "dist/index.d.ts",
6
6
  "types": "dist/index.d.ts",
@@ -44,6 +44,7 @@
44
44
  "@types/react": "^19.1.2",
45
45
  "jest": "^29.7.0",
46
46
  "jest-environment-jsdom": "^29.7.0",
47
+ "request": "^2.88.2",
47
48
  "ts-jest": "^29.1.5",
48
49
  "ts-node": "^10.9.2",
49
50
  "tsup": "^8.1.0",
@@ -51,9 +52,9 @@
51
52
  "typescript": "^5.5.3"
52
53
  },
53
54
  "peerDependencies": {
55
+ "axios": "^1.7.2",
54
56
  "react": "19.1.0",
55
- "starknet": "^6.11.0",
56
- "axios": "^1.7.2"
57
+ "starknet": "^6.11.0"
57
58
  },
58
59
  "dependencies": {
59
60
  "@avnu/avnu-sdk": "3.0.2",
@@ -50,14 +50,18 @@ export class AvnuWrapper {
50
50
  assert(fromToken != toToken, "From and to tokens are the same");
51
51
 
52
52
  const quotes = await fetchQuotes(params);
53
- if (quotes.length == 0) {
53
+ // precision is important for us
54
+ const filteredQuotes = quotes.filter((q) => q.sellAmount.toString() == amountWei);
55
+ if (filteredQuotes.length == 0) {
54
56
  if (retry < MAX_RETRY) {
55
57
  await new Promise((res) => setTimeout(res, 3000))
56
58
  return await this.getQuotes(fromToken, toToken, amountWei, taker, retry + 1);
57
59
  }
58
60
  throw new Error('no quotes found')
59
61
  }
60
- return quotes[0];
62
+
63
+ logger.verbose(`${AvnuWrapper.name}: getQuotes => Found ${JSON.stringify(filteredQuotes[0])}`);
64
+ return filteredQuotes[0];
61
65
  }
62
66
 
63
67
  async getSwapInfo(
@@ -96,6 +100,7 @@ export class AvnuWrapper {
96
100
  // swapInfo as expected by the strategy
97
101
  // fallback, max 1% slippage
98
102
  const _minAmount = minAmount || (quote.buyAmount * 95n / 100n).toString();
103
+ logger.verbose(`${AvnuWrapper.name}: getSwapInfo => sellToken: ${quote.sellTokenAddress}, sellAmount: ${quote.sellAmount}`);
99
104
  logger.verbose(`${AvnuWrapper.name}: getSwapInfo => buyToken: ${quote.buyTokenAddress}`);
100
105
  logger.verbose(`${AvnuWrapper.name}: getSwapInfo => buyAmount: ${quote.buyAmount}, minAmount: ${_minAmount}`);
101
106
  const swapInfo: SwapInfo = {