@strkfarm/sdk 1.0.52 → 1.0.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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, maxIterations = 20) {
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,15 +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
15690
  ekuboBounds,
15680
- maxIterations
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})`
15681
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})`);
15682
15702
  }
15683
- async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds, maxIterations = 20) {
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) {
15684
15738
  logger.verbose(
15685
15739
  `${_EkuboCLVault.name}: getSwapInfoGivenAmounts::pre => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`
15686
15740
  );
@@ -15694,28 +15748,13 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15694
15748
  );
15695
15749
  let retry = 0;
15696
15750
  const maxRetry = maxIterations;
15697
- function assertValidAmounts(expectedAmounts2, token0Bal2, token1Bal2) {
15698
- if (expectedAmounts2.amount0.lessThan(token0Bal2) && expectedAmounts2.amount1.lessThan(token1Bal2)) {
15699
- throw new Error("Both tokens are decreased, something is wrong");
15700
- }
15701
- if (expectedAmounts2.amount0.greaterThan(token0Bal2) && expectedAmounts2.amount1.greaterThan(token1Bal2)) {
15702
- throw new Error("Both tokens are increased, something is wrong");
15703
- }
15704
- }
15705
- function getSwapParams(expectedAmounts2, poolKey2, token0Bal2, token1Bal2) {
15706
- const tokenToSell = expectedAmounts2.amount0.lessThan(token0Bal2) ? poolKey2.token0 : poolKey2.token1;
15707
- const tokenToBuy = tokenToSell == poolKey2.token0 ? poolKey2.token1 : poolKey2.token0;
15708
- const amountToSell = tokenToSell == poolKey2.token0 ? token0Bal2.minus(expectedAmounts2.amount0) : token1Bal2.minus(expectedAmounts2.amount1);
15709
- const remainingSellAmount = tokenToSell == poolKey2.token0 ? expectedAmounts2.amount0 : expectedAmounts2.amount1;
15710
- return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
15711
- }
15712
15751
  while (retry < maxRetry) {
15713
15752
  retry++;
15714
15753
  logger.verbose(
15715
15754
  `getSwapInfoGivenAmounts::Retry attempt: ${retry}/${maxRetry}`
15716
15755
  );
15717
- assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
15718
- 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);
15719
15758
  const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
15720
15759
  const expectedRatio = expectedAmounts.ratio;
15721
15760
  logger.verbose(
@@ -15753,6 +15792,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15753
15792
  quote.buyAmount.toString(),
15754
15793
  tokenToBuyInfo.decimals
15755
15794
  );
15795
+ logger.verbose(
15796
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => amountOut: ${amountOut.toString()}`
15797
+ );
15756
15798
  const swapPrice = tokenToSell == poolKey.token0 ? amountOut.dividedBy(amountToSell) : amountToSell.dividedBy(amountOut);
15757
15799
  const newRatio = tokenToSell == poolKey.token0 ? remainingSellAmount.dividedBy(token1Bal.plus(amountOut)) : token0Bal.plus(amountOut).dividedBy(remainingSellAmount);
15758
15800
  logger.verbose(
@@ -15762,31 +15804,46 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15762
15804
  newRatio: newRatio.toString()
15763
15805
  })}`
15764
15806
  );
15765
- const expectedPrecision = Math.min(7, tokenToBuyInfo.decimals - 2);
15766
- if (Number(newRatio.toString()) > expectedRatio * (1 + 1 / 10 ** expectedPrecision) || Number(newRatio.toString()) < expectedRatio * (1 - 1 / 10 ** expectedPrecision)) {
15767
- 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(
15768
15815
  token0Bal,
15769
15816
  token1Bal,
15770
15817
  new Web3Number(Number(expectedRatio).toFixed(13), 18),
15771
15818
  Number(swapPrice.toString())
15772
15819
  );
15773
15820
  logger.verbose(
15774
- `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
15821
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmountsNew.amount0.toString()}, ${expectedAmountsNew.amount1.toString()}`
15775
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;
15776
15830
  } else {
15777
15831
  const minAmountOut = Web3Number.fromWei(
15778
15832
  quote.buyAmount.toString(),
15779
15833
  tokenToBuyInfo.decimals
15780
15834
  ).multipliedBy(0.9999);
15781
- return await this.avnu.getSwapInfo(
15835
+ const output = await this.avnu.getSwapInfo(
15782
15836
  quote,
15783
15837
  this.address.address,
15784
15838
  0,
15785
15839
  this.address.address,
15786
15840
  minAmountOut.toWei()
15787
15841
  );
15842
+ logger.verbose(
15843
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => swap info found: ${JSON.stringify(output)}`
15844
+ );
15845
+ return output;
15788
15846
  }
15789
- retry++;
15790
15847
  }
15791
15848
  throw new Error("Failed to get swap info");
15792
15849
  }
@@ -15956,7 +16013,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15956
16013
  amount1
15957
16014
  };
15958
16015
  }
15959
- async harvest(acc, maxIterations = 20) {
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
@@ -15986,7 +16043,8 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15986
16043
  token0Amt,
15987
16044
  token1Amt,
15988
16045
  bounds,
15989
- maxIterations
16046
+ maxIterations,
16047
+ priceRatioPrecision
15990
16048
  );
15991
16049
  swapInfo.token_to_address = token0Info.address.address;
15992
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, maxIterations = 20) {
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,15 +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
15624
  ekuboBounds,
15614
- maxIterations
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})`
15615
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})`);
15616
15636
  }
15617
- async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds, maxIterations = 20) {
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) {
15618
15672
  logger.verbose(
15619
15673
  `${_EkuboCLVault.name}: getSwapInfoGivenAmounts::pre => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`
15620
15674
  );
@@ -15628,28 +15682,13 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15628
15682
  );
15629
15683
  let retry = 0;
15630
15684
  const maxRetry = maxIterations;
15631
- function assertValidAmounts(expectedAmounts2, token0Bal2, token1Bal2) {
15632
- if (expectedAmounts2.amount0.lessThan(token0Bal2) && expectedAmounts2.amount1.lessThan(token1Bal2)) {
15633
- throw new Error("Both tokens are decreased, something is wrong");
15634
- }
15635
- if (expectedAmounts2.amount0.greaterThan(token0Bal2) && expectedAmounts2.amount1.greaterThan(token1Bal2)) {
15636
- throw new Error("Both tokens are increased, something is wrong");
15637
- }
15638
- }
15639
- function getSwapParams(expectedAmounts2, poolKey2, token0Bal2, token1Bal2) {
15640
- const tokenToSell = expectedAmounts2.amount0.lessThan(token0Bal2) ? poolKey2.token0 : poolKey2.token1;
15641
- const tokenToBuy = tokenToSell == poolKey2.token0 ? poolKey2.token1 : poolKey2.token0;
15642
- const amountToSell = tokenToSell == poolKey2.token0 ? token0Bal2.minus(expectedAmounts2.amount0) : token1Bal2.minus(expectedAmounts2.amount1);
15643
- const remainingSellAmount = tokenToSell == poolKey2.token0 ? expectedAmounts2.amount0 : expectedAmounts2.amount1;
15644
- return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
15645
- }
15646
15685
  while (retry < maxRetry) {
15647
15686
  retry++;
15648
15687
  logger.verbose(
15649
15688
  `getSwapInfoGivenAmounts::Retry attempt: ${retry}/${maxRetry}`
15650
15689
  );
15651
- assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
15652
- 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);
15653
15692
  const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
15654
15693
  const expectedRatio = expectedAmounts.ratio;
15655
15694
  logger.verbose(
@@ -15687,6 +15726,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15687
15726
  quote.buyAmount.toString(),
15688
15727
  tokenToBuyInfo.decimals
15689
15728
  );
15729
+ logger.verbose(
15730
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => amountOut: ${amountOut.toString()}`
15731
+ );
15690
15732
  const swapPrice = tokenToSell == poolKey.token0 ? amountOut.dividedBy(amountToSell) : amountToSell.dividedBy(amountOut);
15691
15733
  const newRatio = tokenToSell == poolKey.token0 ? remainingSellAmount.dividedBy(token1Bal.plus(amountOut)) : token0Bal.plus(amountOut).dividedBy(remainingSellAmount);
15692
15734
  logger.verbose(
@@ -15696,31 +15738,46 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15696
15738
  newRatio: newRatio.toString()
15697
15739
  })}`
15698
15740
  );
15699
- const expectedPrecision = Math.min(7, tokenToBuyInfo.decimals - 2);
15700
- if (Number(newRatio.toString()) > expectedRatio * (1 + 1 / 10 ** expectedPrecision) || Number(newRatio.toString()) < expectedRatio * (1 - 1 / 10 ** expectedPrecision)) {
15701
- 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(
15702
15749
  token0Bal,
15703
15750
  token1Bal,
15704
15751
  new Web3Number(Number(expectedRatio).toFixed(13), 18),
15705
15752
  Number(swapPrice.toString())
15706
15753
  );
15707
15754
  logger.verbose(
15708
- `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
15755
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmountsNew.amount0.toString()}, ${expectedAmountsNew.amount1.toString()}`
15709
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;
15710
15764
  } else {
15711
15765
  const minAmountOut = Web3Number.fromWei(
15712
15766
  quote.buyAmount.toString(),
15713
15767
  tokenToBuyInfo.decimals
15714
15768
  ).multipliedBy(0.9999);
15715
- return await this.avnu.getSwapInfo(
15769
+ const output = await this.avnu.getSwapInfo(
15716
15770
  quote,
15717
15771
  this.address.address,
15718
15772
  0,
15719
15773
  this.address.address,
15720
15774
  minAmountOut.toWei()
15721
15775
  );
15776
+ logger.verbose(
15777
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => swap info found: ${JSON.stringify(output)}`
15778
+ );
15779
+ return output;
15722
15780
  }
15723
- retry++;
15724
15781
  }
15725
15782
  throw new Error("Failed to get swap info");
15726
15783
  }
@@ -15890,7 +15947,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15890
15947
  amount1
15891
15948
  };
15892
15949
  }
15893
- async harvest(acc, maxIterations = 20) {
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
@@ -15920,7 +15977,8 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15920
15977
  token0Amt,
15921
15978
  token1Amt,
15922
15979
  bounds,
15923
- maxIterations
15980
+ maxIterations,
15981
+ priceRatioPrecision
15924
15982
  );
15925
15983
  swapInfo.token_to_address = token0Info.address.address;
15926
15984
  logger.verbose(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strkfarm/sdk",
3
- "version": "1.0.52",
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 = {