@strkfarm/sdk 1.0.52 → 1.0.54

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.
@@ -19770,6 +19770,19 @@ var strkfarm_risk_engine = (() => {
19770
19770
  }
19771
19771
  return value.toFixed(this.maxToFixedDecimals());
19772
19772
  }
19773
+ minimum(value) {
19774
+ const _value = new import_bignumber.default(value);
19775
+ const _valueMe = new import_bignumber.default(this.toString());
19776
+ const answer = _value.lessThanOrEqualTo(_valueMe) ? _value : _valueMe;
19777
+ return this.construct(answer.toString(), this.decimals);
19778
+ }
19779
+ maximum(value) {
19780
+ const _value = new import_bignumber.default(value);
19781
+ const _valueMe = new import_bignumber.default(this.toString());
19782
+ console.warn(`maximum: _value: ${_value.toString()}, _valueMe: ${_valueMe.toString()}`);
19783
+ const answer = _value.greaterThanOrEqualTo(_valueMe) ? _value : _valueMe;
19784
+ return this.construct(answer.toString(), this.decimals);
19785
+ }
19773
19786
  };
19774
19787
  import_bignumber.default.config({ DECIMAL_PLACES: 18, ROUNDING_MODE: import_bignumber.default.ROUND_DOWN });
19775
19788
  _Web3Number.config({ DECIMAL_PLACES: 18, ROUNDING_MODE: import_bignumber.default.ROUND_DOWN });
@@ -38124,14 +38137,15 @@ var strkfarm_risk_engine = (() => {
38124
38137
  };
38125
38138
  assert3(fromToken != toToken, "From and to tokens are the same");
38126
38139
  const quotes = await fetchQuotes(params);
38127
- if (quotes.length == 0) {
38140
+ const filteredQuotes = quotes.filter((q) => q.sellAmount.toString() == amountWei);
38141
+ if (filteredQuotes.length == 0) {
38128
38142
  if (retry < MAX_RETRY) {
38129
38143
  await new Promise((res) => setTimeout(res, 3e3));
38130
38144
  return await this.getQuotes(fromToken, toToken, amountWei, taker, retry + 1);
38131
38145
  }
38132
38146
  throw new Error("no quotes found");
38133
38147
  }
38134
- return quotes[0];
38148
+ return filteredQuotes[0];
38135
38149
  }
38136
38150
  async getSwapInfo(quote, taker, integratorFeeBps, integratorFeeRecipient, minAmount) {
38137
38151
  const calldata = await fetchBuildExecuteTransaction(quote.quoteId);
@@ -38154,6 +38168,7 @@ var strkfarm_risk_engine = (() => {
38154
38168
  startIndex += 5 + swap_params_len;
38155
38169
  }
38156
38170
  const _minAmount = minAmount || (quote.buyAmount * 95n / 100n).toString();
38171
+ logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => sellToken: ${quote.sellTokenAddress}, sellAmount: ${quote.sellAmount}`);
38157
38172
  logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => buyToken: ${quote.buyTokenAddress}`);
38158
38173
  logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => buyAmount: ${quote.buyAmount}, minAmount: ${_minAmount}`);
38159
38174
  const swapInfo = {
@@ -51645,7 +51660,7 @@ var strkfarm_risk_engine = (() => {
51645
51660
  };
51646
51661
  }
51647
51662
  }
51648
- const ratioWeb3Number = sampleAmount0.multipliedBy(1e18).dividedBy(sampleAmount1.toString()).dividedBy(1e18);
51663
+ const ratioWeb3Number = this.getRatio(sampleAmount0, sampleAmount1);
51649
51664
  const ratio = Number(ratioWeb3Number.toFixed(18));
51650
51665
  logger.verbose(
51651
51666
  `${_EkuboCLVault.name}: ${this.metadata.name} => ratio: ${ratio.toString()}`
@@ -51677,11 +51692,18 @@ var strkfarm_risk_engine = (() => {
51677
51692
  );
51678
51693
  }
51679
51694
  }
51695
+ getRatio(token0Amt, token1Amount) {
51696
+ const ratio = token0Amt.multipliedBy(1e18).dividedBy(token1Amount.toString()).dividedBy(1e18);
51697
+ logger.verbose(
51698
+ `${_EkuboCLVault.name}: getRatio => token0Amt: ${token0Amt.toString()}, token1Amount: ${token1Amount.toString()}, ratio: ${ratio.toString()}`
51699
+ );
51700
+ return ratio;
51701
+ }
51680
51702
  _solveExpectedAmountsEq(availableAmount0, availableAmount1, ratio, price) {
51681
51703
  const y = ratio.multipliedBy(availableAmount1).minus(availableAmount0).dividedBy(ratio.plus(1 / price));
51682
51704
  const x = y.dividedBy(price);
51683
51705
  logger.verbose(
51684
- `${_EkuboCLVault.name}: _solveExpectedAmountsEq => x: ${x.toString()}, y: ${y.toString()}, amount0: ${availableAmount0.toString()}, amount1: ${availableAmount1.toString()}`
51706
+ `${_EkuboCLVault.name}: _solveExpectedAmountsEq => ratio: ${ratio.toString()}, x: ${x.toString()}, y: ${y.toString()}, amount0: ${availableAmount0.toString()}, amount1: ${availableAmount1.toString()}`
51685
51707
  );
51686
51708
  if (ratio.eq(0)) {
51687
51709
  return {
@@ -51731,7 +51753,7 @@ var strkfarm_risk_engine = (() => {
51731
51753
  }
51732
51754
  };
51733
51755
  }
51734
- async getSwapInfoToHandleUnused(considerRebalance = true, newBounds = null, maxIterations = 20) {
51756
+ async getSwapInfoToHandleUnused(considerRebalance = true, newBounds = null, maxIterations = 20, priceRatioPrecision = 4) {
51735
51757
  const poolKey = await this.getPoolKey();
51736
51758
  const unusedBalances = await this.unusedBalances(poolKey);
51737
51759
  const { amount: token0Bal1, usdValue: token0PriceUsd } = unusedBalances.token0;
@@ -51764,15 +51786,59 @@ var strkfarm_risk_engine = (() => {
51764
51786
  logger.verbose(
51765
51787
  `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${ekuboBounds.lowerTick}, ${ekuboBounds.upperTick}`
51766
51788
  );
51789
+ this.assertValidBounds(ekuboBounds);
51767
51790
  return await this.getSwapInfoGivenAmounts(
51768
51791
  poolKey,
51769
51792
  token0Bal,
51770
51793
  token1Bal,
51771
51794
  ekuboBounds,
51772
- maxIterations
51795
+ maxIterations,
51796
+ priceRatioPrecision
51773
51797
  );
51774
51798
  }
51775
- async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds, maxIterations = 20) {
51799
+ assertValidBounds(bounds) {
51800
+ assert3(
51801
+ bounds.lowerTick < bounds.upperTick,
51802
+ `Invalid bounds: lowerTick (${bounds.lowerTick}) must be less than upperTick (${bounds.upperTick})`
51803
+ );
51804
+ 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})`);
51805
+ 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})`);
51806
+ }
51807
+ // Helper to check for invalid states:
51808
+ // Throws if both tokens are decreased or both are increased, which is not expected
51809
+ assertValidAmounts(expectedAmounts, token0Bal, token1Bal) {
51810
+ if (expectedAmounts.amount0.lessThan(token0Bal) && expectedAmounts.amount1.lessThan(token1Bal)) {
51811
+ throw new Error("Both tokens are decreased, something is wrong");
51812
+ }
51813
+ if (expectedAmounts.amount0.greaterThan(token0Bal) && expectedAmounts.amount1.greaterThan(token1Bal)) {
51814
+ throw new Error("Both tokens are increased, something is wrong");
51815
+ }
51816
+ }
51817
+ // Helper to determine which token to sell, which to buy, and the amounts to use
51818
+ getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal) {
51819
+ const tokenToSell = expectedAmounts.amount0.lessThan(token0Bal) ? poolKey.token0 : poolKey.token1;
51820
+ const tokenToBuy = tokenToSell == poolKey.token0 ? poolKey.token1 : poolKey.token0;
51821
+ const amountToSell = tokenToSell == poolKey.token0 ? token0Bal.minus(expectedAmounts.amount0) : token1Bal.minus(expectedAmounts.amount1);
51822
+ if (amountToSell.eq(0)) {
51823
+ throw new Error(
51824
+ `No amount to sell for ${tokenToSell.address}`
51825
+ );
51826
+ }
51827
+ const remainingSellAmount = tokenToSell == poolKey.token0 ? expectedAmounts.amount0 : expectedAmounts.amount1;
51828
+ return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
51829
+ }
51830
+ /**
51831
+ * @description Calculates swap info based on given amounts of token0 and token1
51832
+ * Use token0 and token1 balances to determine the expected amounts for new bounds
51833
+ * @param poolKey
51834
+ * @param token0Bal
51835
+ * @param token1Bal
51836
+ * @param bounds // new bounds
51837
+ * @param maxIterations
51838
+ * @returns {Promise<SwapInfo>}
51839
+ *
51840
+ */
51841
+ async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds, maxIterations = 20, priceRatioPrecision = 4) {
51776
51842
  logger.verbose(
51777
51843
  `${_EkuboCLVault.name}: getSwapInfoGivenAmounts::pre => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`
51778
51844
  );
@@ -51786,28 +51852,13 @@ var strkfarm_risk_engine = (() => {
51786
51852
  );
51787
51853
  let retry = 0;
51788
51854
  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
51855
  while (retry < maxRetry) {
51805
51856
  retry++;
51806
51857
  logger.verbose(
51807
51858
  `getSwapInfoGivenAmounts::Retry attempt: ${retry}/${maxRetry}`
51808
51859
  );
51809
- assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
51810
- const { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount } = getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal);
51860
+ this.assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
51861
+ const { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount } = this.getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal);
51811
51862
  const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
51812
51863
  const expectedRatio = expectedAmounts.ratio;
51813
51864
  logger.verbose(
@@ -51845,6 +51896,9 @@ var strkfarm_risk_engine = (() => {
51845
51896
  quote.buyAmount.toString(),
51846
51897
  tokenToBuyInfo.decimals
51847
51898
  );
51899
+ logger.verbose(
51900
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => amountOut: ${amountOut.toString()}`
51901
+ );
51848
51902
  const swapPrice = tokenToSell == poolKey.token0 ? amountOut.dividedBy(amountToSell) : amountToSell.dividedBy(amountOut);
51849
51903
  const newRatio = tokenToSell == poolKey.token0 ? remainingSellAmount.dividedBy(token1Bal.plus(amountOut)) : token0Bal.plus(amountOut).dividedBy(remainingSellAmount);
51850
51904
  logger.verbose(
@@ -51854,31 +51908,46 @@ var strkfarm_risk_engine = (() => {
51854
51908
  newRatio: newRatio.toString()
51855
51909
  })}`
51856
51910
  );
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(
51911
+ const expectedPrecision = Math.min(priceRatioPrecision);
51912
+ const isWithInTolerance = Number(newRatio.toString()) <= expectedRatio * (1 + 1 / 10 ** expectedPrecision) && Number(newRatio.toString()) >= expectedRatio * (1 - 1 / 10 ** expectedPrecision);
51913
+ const currentPrecision = (expectedRatio - Number(newRatio.toString())) / expectedRatio;
51914
+ logger.verbose(
51915
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => isWithInTolerance: ${isWithInTolerance}, currentPrecision: ${currentPrecision.toString()}, expectedPrecision: ${expectedPrecision}`
51916
+ );
51917
+ if (!isWithInTolerance) {
51918
+ const expectedAmountsNew = await this._solveExpectedAmountsEq(
51860
51919
  token0Bal,
51861
51920
  token1Bal,
51862
51921
  new Web3Number(Number(expectedRatio).toFixed(13), 18),
51863
51922
  Number(swapPrice.toString())
51864
51923
  );
51865
51924
  logger.verbose(
51866
- `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
51925
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmountsNew.amount0.toString()}, ${expectedAmountsNew.amount1.toString()}`
51867
51926
  );
51927
+ if (expectedAmountsNew.amount0.eq(expectedAmounts.amount0.toString()) && expectedAmountsNew.amount1.eq(expectedAmounts.amount1.toString())) {
51928
+ logger.error(
51929
+ `getSwapInfoGivenAmounts: stuck in loop, expected amounts did not change`
51930
+ );
51931
+ throw new Error("Stuck in loop, expected amounts did not change");
51932
+ }
51933
+ expectedAmounts = expectedAmountsNew;
51868
51934
  } else {
51869
51935
  const minAmountOut = Web3Number.fromWei(
51870
51936
  quote.buyAmount.toString(),
51871
51937
  tokenToBuyInfo.decimals
51872
51938
  ).multipliedBy(0.9999);
51873
- return await this.avnu.getSwapInfo(
51939
+ const output3 = await this.avnu.getSwapInfo(
51874
51940
  quote,
51875
51941
  this.address.address,
51876
51942
  0,
51877
51943
  this.address.address,
51878
51944
  minAmountOut.toWei()
51879
51945
  );
51946
+ logger.verbose(
51947
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => swap info found: ${JSON.stringify(output3)}`
51948
+ );
51949
+ return output3;
51880
51950
  }
51881
- retry++;
51882
51951
  }
51883
51952
  throw new Error("Failed to get swap info");
51884
51953
  }
@@ -52048,7 +52117,7 @@ var strkfarm_risk_engine = (() => {
52048
52117
  amount1
52049
52118
  };
52050
52119
  }
52051
- async harvest(acc, maxIterations = 20) {
52120
+ async harvest(acc, maxIterations = 20, priceRatioPrecision = 4) {
52052
52121
  const ekuboHarvests = new EkuboHarvests(this.config);
52053
52122
  const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
52054
52123
  this.address
@@ -52078,7 +52147,8 @@ var strkfarm_risk_engine = (() => {
52078
52147
  token0Amt,
52079
52148
  token1Amt,
52080
52149
  bounds,
52081
- maxIterations
52150
+ maxIterations,
52151
+ priceRatioPrecision
52082
52152
  );
52083
52153
  swapInfo.token_to_address = token0Info.address.address;
52084
52154
  logger.verbose(
@@ -52092,11 +52162,19 @@ var strkfarm_risk_engine = (() => {
52092
52162
  uint256_exports.uint256ToBN(swapInfo1.token_from_amount).toString(),
52093
52163
  18
52094
52164
  // cause its always STRK?
52165
+ ).minimum(
52166
+ postFeeAmount.toFixed(18)
52167
+ // cause always strk
52168
+ );
52169
+ swapInfo.token_from_amount = uint256_exports.bnToUint256(swap1Amount.toWei());
52170
+ swapInfo.token_to_min_amount = uint256_exports.bnToUint256(
52171
+ swap1Amount.multipliedBy(0).toWei()
52172
+ // placeholder
52095
52173
  );
52096
52174
  logger.verbose(
52097
52175
  `${_EkuboCLVault.name}: harvest => swap1Amount: ${swap1Amount}`
52098
52176
  );
52099
- const remainingAmount = postFeeAmount.minus(swap1Amount);
52177
+ const remainingAmount = postFeeAmount.minus(swap1Amount).maximum(0);
52100
52178
  logger.verbose(
52101
52179
  `${_EkuboCLVault.name}: harvest => remainingAmount: ${remainingAmount}`
52102
52180
  );
@@ -52136,7 +52214,12 @@ var strkfarm_risk_engine = (() => {
52136
52214
  const _callsFinal = await this.rebalanceIter(
52137
52215
  swapInfo,
52138
52216
  acc,
52139
- harvestEstimateCall
52217
+ harvestEstimateCall,
52218
+ claim.token.eq(poolKey.token0),
52219
+ 0,
52220
+ 0n,
52221
+ BigInt(postFeeAmount.toWei())
52222
+ // upper limit is the post fee amount
52140
52223
  );
52141
52224
  logger.verbose(
52142
52225
  `${_EkuboCLVault.name}: harvest => _callsFinal: ${JSON.stringify(
@@ -51,6 +51,19 @@ var _Web3Number = class extends BigNumber {
51
51
  }
52
52
  return value.toFixed(this.maxToFixedDecimals());
53
53
  }
54
+ minimum(value) {
55
+ const _value = new BigNumber(value);
56
+ const _valueMe = new BigNumber(this.toString());
57
+ const answer = _value.lessThanOrEqualTo(_valueMe) ? _value : _valueMe;
58
+ return this.construct(answer.toString(), this.decimals);
59
+ }
60
+ maximum(value) {
61
+ const _value = new BigNumber(value);
62
+ const _valueMe = new BigNumber(this.toString());
63
+ console.warn(`maximum: _value: ${_value.toString()}, _valueMe: ${_valueMe.toString()}`);
64
+ const answer = _value.greaterThanOrEqualTo(_valueMe) ? _value : _valueMe;
65
+ return this.construct(answer.toString(), this.decimals);
66
+ }
54
67
  };
55
68
  BigNumber.config({ DECIMAL_PLACES: 18, ROUNDING_MODE: BigNumber.ROUND_DOWN });
56
69
  _Web3Number.config({ DECIMAL_PLACES: 18, ROUNDING_MODE: BigNumber.ROUND_DOWN });
@@ -1903,14 +1916,15 @@ var AvnuWrapper = class _AvnuWrapper {
1903
1916
  };
1904
1917
  assert(fromToken != toToken, "From and to tokens are the same");
1905
1918
  const quotes = await fetchQuotes(params);
1906
- if (quotes.length == 0) {
1919
+ const filteredQuotes = quotes.filter((q) => q.sellAmount.toString() == amountWei);
1920
+ if (filteredQuotes.length == 0) {
1907
1921
  if (retry < MAX_RETRY) {
1908
1922
  await new Promise((res) => setTimeout(res, 3e3));
1909
1923
  return await this.getQuotes(fromToken, toToken, amountWei, taker, retry + 1);
1910
1924
  }
1911
1925
  throw new Error("no quotes found");
1912
1926
  }
1913
- return quotes[0];
1927
+ return filteredQuotes[0];
1914
1928
  }
1915
1929
  async getSwapInfo(quote, taker, integratorFeeBps, integratorFeeRecipient, minAmount) {
1916
1930
  const calldata = await fetchBuildExecuteTransaction(quote.quoteId);
@@ -1933,6 +1947,7 @@ var AvnuWrapper = class _AvnuWrapper {
1933
1947
  startIndex += 5 + swap_params_len;
1934
1948
  }
1935
1949
  const _minAmount = minAmount || (quote.buyAmount * 95n / 100n).toString();
1950
+ logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => sellToken: ${quote.sellTokenAddress}, sellAmount: ${quote.sellAmount}`);
1936
1951
  logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => buyToken: ${quote.buyTokenAddress}`);
1937
1952
  logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => buyAmount: ${quote.buyAmount}, minAmount: ${_minAmount}`);
1938
1953
  const swapInfo = {
@@ -15438,7 +15453,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15438
15453
  };
15439
15454
  }
15440
15455
  }
15441
- const ratioWeb3Number = sampleAmount0.multipliedBy(1e18).dividedBy(sampleAmount1.toString()).dividedBy(1e18);
15456
+ const ratioWeb3Number = this.getRatio(sampleAmount0, sampleAmount1);
15442
15457
  const ratio = Number(ratioWeb3Number.toFixed(18));
15443
15458
  logger.verbose(
15444
15459
  `${_EkuboCLVault.name}: ${this.metadata.name} => ratio: ${ratio.toString()}`
@@ -15470,11 +15485,18 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15470
15485
  );
15471
15486
  }
15472
15487
  }
15488
+ getRatio(token0Amt, token1Amount) {
15489
+ const ratio = token0Amt.multipliedBy(1e18).dividedBy(token1Amount.toString()).dividedBy(1e18);
15490
+ logger.verbose(
15491
+ `${_EkuboCLVault.name}: getRatio => token0Amt: ${token0Amt.toString()}, token1Amount: ${token1Amount.toString()}, ratio: ${ratio.toString()}`
15492
+ );
15493
+ return ratio;
15494
+ }
15473
15495
  _solveExpectedAmountsEq(availableAmount0, availableAmount1, ratio, price) {
15474
15496
  const y = ratio.multipliedBy(availableAmount1).minus(availableAmount0).dividedBy(ratio.plus(1 / price));
15475
15497
  const x = y.dividedBy(price);
15476
15498
  logger.verbose(
15477
- `${_EkuboCLVault.name}: _solveExpectedAmountsEq => x: ${x.toString()}, y: ${y.toString()}, amount0: ${availableAmount0.toString()}, amount1: ${availableAmount1.toString()}`
15499
+ `${_EkuboCLVault.name}: _solveExpectedAmountsEq => ratio: ${ratio.toString()}, x: ${x.toString()}, y: ${y.toString()}, amount0: ${availableAmount0.toString()}, amount1: ${availableAmount1.toString()}`
15478
15500
  );
15479
15501
  if (ratio.eq(0)) {
15480
15502
  return {
@@ -15524,7 +15546,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15524
15546
  }
15525
15547
  };
15526
15548
  }
15527
- async getSwapInfoToHandleUnused(considerRebalance = true, newBounds = null, maxIterations = 20) {
15549
+ async getSwapInfoToHandleUnused(considerRebalance = true, newBounds = null, maxIterations = 20, priceRatioPrecision = 4) {
15528
15550
  const poolKey = await this.getPoolKey();
15529
15551
  const unusedBalances = await this.unusedBalances(poolKey);
15530
15552
  const { amount: token0Bal1, usdValue: token0PriceUsd } = unusedBalances.token0;
@@ -15557,15 +15579,59 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15557
15579
  logger.verbose(
15558
15580
  `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${ekuboBounds.lowerTick}, ${ekuboBounds.upperTick}`
15559
15581
  );
15582
+ this.assertValidBounds(ekuboBounds);
15560
15583
  return await this.getSwapInfoGivenAmounts(
15561
15584
  poolKey,
15562
15585
  token0Bal,
15563
15586
  token1Bal,
15564
15587
  ekuboBounds,
15565
- maxIterations
15588
+ maxIterations,
15589
+ priceRatioPrecision
15566
15590
  );
15567
15591
  }
15568
- async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds, maxIterations = 20) {
15592
+ assertValidBounds(bounds) {
15593
+ assert(
15594
+ bounds.lowerTick < bounds.upperTick,
15595
+ `Invalid bounds: lowerTick (${bounds.lowerTick}) must be less than upperTick (${bounds.upperTick})`
15596
+ );
15597
+ 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})`);
15598
+ 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})`);
15599
+ }
15600
+ // Helper to check for invalid states:
15601
+ // Throws if both tokens are decreased or both are increased, which is not expected
15602
+ assertValidAmounts(expectedAmounts, token0Bal, token1Bal) {
15603
+ if (expectedAmounts.amount0.lessThan(token0Bal) && expectedAmounts.amount1.lessThan(token1Bal)) {
15604
+ throw new Error("Both tokens are decreased, something is wrong");
15605
+ }
15606
+ if (expectedAmounts.amount0.greaterThan(token0Bal) && expectedAmounts.amount1.greaterThan(token1Bal)) {
15607
+ throw new Error("Both tokens are increased, something is wrong");
15608
+ }
15609
+ }
15610
+ // Helper to determine which token to sell, which to buy, and the amounts to use
15611
+ getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal) {
15612
+ const tokenToSell = expectedAmounts.amount0.lessThan(token0Bal) ? poolKey.token0 : poolKey.token1;
15613
+ const tokenToBuy = tokenToSell == poolKey.token0 ? poolKey.token1 : poolKey.token0;
15614
+ const amountToSell = tokenToSell == poolKey.token0 ? token0Bal.minus(expectedAmounts.amount0) : token1Bal.minus(expectedAmounts.amount1);
15615
+ if (amountToSell.eq(0)) {
15616
+ throw new Error(
15617
+ `No amount to sell for ${tokenToSell.address}`
15618
+ );
15619
+ }
15620
+ const remainingSellAmount = tokenToSell == poolKey.token0 ? expectedAmounts.amount0 : expectedAmounts.amount1;
15621
+ return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
15622
+ }
15623
+ /**
15624
+ * @description Calculates swap info based on given amounts of token0 and token1
15625
+ * Use token0 and token1 balances to determine the expected amounts for new bounds
15626
+ * @param poolKey
15627
+ * @param token0Bal
15628
+ * @param token1Bal
15629
+ * @param bounds // new bounds
15630
+ * @param maxIterations
15631
+ * @returns {Promise<SwapInfo>}
15632
+ *
15633
+ */
15634
+ async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds, maxIterations = 20, priceRatioPrecision = 4) {
15569
15635
  logger.verbose(
15570
15636
  `${_EkuboCLVault.name}: getSwapInfoGivenAmounts::pre => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`
15571
15637
  );
@@ -15579,28 +15645,13 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15579
15645
  );
15580
15646
  let retry = 0;
15581
15647
  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
15648
  while (retry < maxRetry) {
15598
15649
  retry++;
15599
15650
  logger.verbose(
15600
15651
  `getSwapInfoGivenAmounts::Retry attempt: ${retry}/${maxRetry}`
15601
15652
  );
15602
- assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
15603
- const { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount } = getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal);
15653
+ this.assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
15654
+ const { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount } = this.getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal);
15604
15655
  const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
15605
15656
  const expectedRatio = expectedAmounts.ratio;
15606
15657
  logger.verbose(
@@ -15638,6 +15689,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15638
15689
  quote.buyAmount.toString(),
15639
15690
  tokenToBuyInfo.decimals
15640
15691
  );
15692
+ logger.verbose(
15693
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => amountOut: ${amountOut.toString()}`
15694
+ );
15641
15695
  const swapPrice = tokenToSell == poolKey.token0 ? amountOut.dividedBy(amountToSell) : amountToSell.dividedBy(amountOut);
15642
15696
  const newRatio = tokenToSell == poolKey.token0 ? remainingSellAmount.dividedBy(token1Bal.plus(amountOut)) : token0Bal.plus(amountOut).dividedBy(remainingSellAmount);
15643
15697
  logger.verbose(
@@ -15647,31 +15701,46 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15647
15701
  newRatio: newRatio.toString()
15648
15702
  })}`
15649
15703
  );
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(
15704
+ const expectedPrecision = Math.min(priceRatioPrecision);
15705
+ const isWithInTolerance = Number(newRatio.toString()) <= expectedRatio * (1 + 1 / 10 ** expectedPrecision) && Number(newRatio.toString()) >= expectedRatio * (1 - 1 / 10 ** expectedPrecision);
15706
+ const currentPrecision = (expectedRatio - Number(newRatio.toString())) / expectedRatio;
15707
+ logger.verbose(
15708
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => isWithInTolerance: ${isWithInTolerance}, currentPrecision: ${currentPrecision.toString()}, expectedPrecision: ${expectedPrecision}`
15709
+ );
15710
+ if (!isWithInTolerance) {
15711
+ const expectedAmountsNew = await this._solveExpectedAmountsEq(
15653
15712
  token0Bal,
15654
15713
  token1Bal,
15655
15714
  new Web3Number(Number(expectedRatio).toFixed(13), 18),
15656
15715
  Number(swapPrice.toString())
15657
15716
  );
15658
15717
  logger.verbose(
15659
- `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
15718
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmountsNew.amount0.toString()}, ${expectedAmountsNew.amount1.toString()}`
15660
15719
  );
15720
+ if (expectedAmountsNew.amount0.eq(expectedAmounts.amount0.toString()) && expectedAmountsNew.amount1.eq(expectedAmounts.amount1.toString())) {
15721
+ logger.error(
15722
+ `getSwapInfoGivenAmounts: stuck in loop, expected amounts did not change`
15723
+ );
15724
+ throw new Error("Stuck in loop, expected amounts did not change");
15725
+ }
15726
+ expectedAmounts = expectedAmountsNew;
15661
15727
  } else {
15662
15728
  const minAmountOut = Web3Number.fromWei(
15663
15729
  quote.buyAmount.toString(),
15664
15730
  tokenToBuyInfo.decimals
15665
15731
  ).multipliedBy(0.9999);
15666
- return await this.avnu.getSwapInfo(
15732
+ const output = await this.avnu.getSwapInfo(
15667
15733
  quote,
15668
15734
  this.address.address,
15669
15735
  0,
15670
15736
  this.address.address,
15671
15737
  minAmountOut.toWei()
15672
15738
  );
15739
+ logger.verbose(
15740
+ `${_EkuboCLVault.name}: getSwapInfoToHandleUnused => swap info found: ${JSON.stringify(output)}`
15741
+ );
15742
+ return output;
15673
15743
  }
15674
- retry++;
15675
15744
  }
15676
15745
  throw new Error("Failed to get swap info");
15677
15746
  }
@@ -15841,7 +15910,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15841
15910
  amount1
15842
15911
  };
15843
15912
  }
15844
- async harvest(acc, maxIterations = 20) {
15913
+ async harvest(acc, maxIterations = 20, priceRatioPrecision = 4) {
15845
15914
  const ekuboHarvests = new EkuboHarvests(this.config);
15846
15915
  const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
15847
15916
  this.address
@@ -15871,7 +15940,8 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15871
15940
  token0Amt,
15872
15941
  token1Amt,
15873
15942
  bounds,
15874
- maxIterations
15943
+ maxIterations,
15944
+ priceRatioPrecision
15875
15945
  );
15876
15946
  swapInfo.token_to_address = token0Info.address.address;
15877
15947
  logger.verbose(
@@ -15885,11 +15955,19 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15885
15955
  uint2564.uint256ToBN(swapInfo1.token_from_amount).toString(),
15886
15956
  18
15887
15957
  // cause its always STRK?
15958
+ ).minimum(
15959
+ postFeeAmount.toFixed(18)
15960
+ // cause always strk
15961
+ );
15962
+ swapInfo.token_from_amount = uint2564.bnToUint256(swap1Amount.toWei());
15963
+ swapInfo.token_to_min_amount = uint2564.bnToUint256(
15964
+ swap1Amount.multipliedBy(0).toWei()
15965
+ // placeholder
15888
15966
  );
15889
15967
  logger.verbose(
15890
15968
  `${_EkuboCLVault.name}: harvest => swap1Amount: ${swap1Amount}`
15891
15969
  );
15892
- const remainingAmount = postFeeAmount.minus(swap1Amount);
15970
+ const remainingAmount = postFeeAmount.minus(swap1Amount).maximum(0);
15893
15971
  logger.verbose(
15894
15972
  `${_EkuboCLVault.name}: harvest => remainingAmount: ${remainingAmount}`
15895
15973
  );
@@ -15929,7 +16007,12 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
15929
16007
  const _callsFinal = await this.rebalanceIter(
15930
16008
  swapInfo,
15931
16009
  acc,
15932
- harvestEstimateCall
16010
+ harvestEstimateCall,
16011
+ claim.token.eq(poolKey.token0),
16012
+ 0,
16013
+ 0n,
16014
+ BigInt(postFeeAmount.toWei())
16015
+ // upper limit is the post fee amount
15933
16016
  );
15934
16017
  logger.verbose(
15935
16018
  `${_EkuboCLVault.name}: harvest => _callsFinal: ${JSON.stringify(
package/dist/index.d.ts CHANGED
@@ -19,6 +19,8 @@ declare class _Web3Number<T extends _Web3Number<T>> extends BigNumber {
19
19
  valueOf(): string;
20
20
  private maxToFixedDecimals;
21
21
  private getStandardString;
22
+ minimum(value: string | number | T): T;
23
+ maximum(value: string | number | T): T;
22
24
  }
23
25
 
24
26
  declare class Web3Number extends _Web3Number<Web3Number> {
@@ -699,6 +701,7 @@ declare class EkuboCLVault extends BaseStrategy<DualTokenInfo, DualActionAmount>
699
701
  * @returns {amount0, amount1}
700
702
  */
701
703
  private _getExpectedAmountsForLiquidity;
704
+ getRatio(token0Amt: Web3Number, token1Amount: Web3Number): Web3Number;
702
705
  private _solveExpectedAmountsEq;
703
706
  unusedBalances(_poolKey?: EkuboPoolKey): Promise<{
704
707
  token0: {
@@ -712,8 +715,27 @@ declare class EkuboCLVault extends BaseStrategy<DualTokenInfo, DualActionAmount>
712
715
  usdValue: number;
713
716
  };
714
717
  }>;
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>;
718
+ getSwapInfoToHandleUnused(considerRebalance?: boolean, newBounds?: EkuboBounds | null, maxIterations?: number, priceRatioPrecision?: number): Promise<SwapInfo>;
719
+ assertValidBounds(bounds: EkuboBounds): void;
720
+ assertValidAmounts(expectedAmounts: any, token0Bal: Web3Number, token1Bal: Web3Number): void;
721
+ getSwapParams(expectedAmounts: any, poolKey: EkuboPoolKey, token0Bal: Web3Number, token1Bal: Web3Number): {
722
+ tokenToSell: ContractAddr;
723
+ tokenToBuy: ContractAddr;
724
+ amountToSell: Web3Number;
725
+ remainingSellAmount: any;
726
+ };
727
+ /**
728
+ * @description Calculates swap info based on given amounts of token0 and token1
729
+ * Use token0 and token1 balances to determine the expected amounts for new bounds
730
+ * @param poolKey
731
+ * @param token0Bal
732
+ * @param token1Bal
733
+ * @param bounds // new bounds
734
+ * @param maxIterations
735
+ * @returns {Promise<SwapInfo>}
736
+ *
737
+ */
738
+ getSwapInfoGivenAmounts(poolKey: EkuboPoolKey, token0Bal: Web3Number, token1Bal: Web3Number, bounds: EkuboBounds, maxIterations?: number, priceRatioPrecision?: number): Promise<SwapInfo>;
717
739
  /**
718
740
  * Attempts to rebalance the vault by iteratively adjusting swap amounts if initial attempt fails.
719
741
  * Uses binary search approach to find optimal swap amount.
@@ -746,7 +768,7 @@ declare class EkuboCLVault extends BaseStrategy<DualTokenInfo, DualActionAmount>
746
768
  amount0: Web3Number;
747
769
  amount1: Web3Number;
748
770
  }>;
749
- harvest(acc: Account, maxIterations?: number): Promise<Call[]>;
771
+ harvest(acc: Account, maxIterations?: number, priceRatioPrecision?: number): Promise<Call[]>;
750
772
  getInvestmentFlows(): Promise<IInvestmentFlow[]>;
751
773
  }
752
774
  /**