@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.browser.global.js +90 -32
- package/dist/index.browser.mjs +90 -32
- package/dist/index.d.ts +23 -3
- package/dist/index.js +90 -32
- package/dist/index.mjs +90 -32
- package/package.json +4 -3
- package/src/modules/avnu.ts +7 -2
- package/src/strategies/ekubo-cl-vault.tsx +130 -68
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
15766
|
-
|
|
15767
|
-
|
|
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: ${
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
15700
|
-
|
|
15701
|
-
|
|
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: ${
|
|
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
|
-
|
|
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.
|
|
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",
|
package/src/modules/avnu.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 = {
|