@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.
- package/dist/index.browser.global.js +117 -34
- package/dist/index.browser.mjs +117 -34
- package/dist/index.d.ts +25 -3
- package/dist/index.js +117 -34
- package/dist/index.mjs +117 -34
- package/package.json +4 -3
- package/src/dataTypes/_bignumber.ts +15 -0
- package/src/modules/avnu.ts +6 -2
- package/src/strategies/ekubo-cl-vault.tsx +145 -71
|
@@ -745,10 +745,7 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
745
745
|
}
|
|
746
746
|
}
|
|
747
747
|
|
|
748
|
-
const ratioWeb3Number = sampleAmount0
|
|
749
|
-
.multipliedBy(1e18)
|
|
750
|
-
.dividedBy(sampleAmount1.toString())
|
|
751
|
-
.dividedBy(1e18);
|
|
748
|
+
const ratioWeb3Number = this.getRatio(sampleAmount0, sampleAmount1);
|
|
752
749
|
|
|
753
750
|
const ratio: number = Number(ratioWeb3Number.toFixed(18));
|
|
754
751
|
logger.verbose(
|
|
@@ -793,6 +790,17 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
793
790
|
}
|
|
794
791
|
}
|
|
795
792
|
|
|
793
|
+
getRatio(token0Amt: Web3Number, token1Amount: Web3Number) {
|
|
794
|
+
const ratio = token0Amt
|
|
795
|
+
.multipliedBy(1e18)
|
|
796
|
+
.dividedBy(token1Amount.toString())
|
|
797
|
+
.dividedBy(1e18);
|
|
798
|
+
logger.verbose(
|
|
799
|
+
`${EkuboCLVault.name}: getRatio => token0Amt: ${token0Amt.toString()}, token1Amount: ${token1Amount.toString()}, ratio: ${ratio.toString()}`
|
|
800
|
+
);
|
|
801
|
+
return ratio;
|
|
802
|
+
}
|
|
803
|
+
|
|
796
804
|
private _solveExpectedAmountsEq(
|
|
797
805
|
availableAmount0: Web3Number,
|
|
798
806
|
availableAmount1: Web3Number,
|
|
@@ -814,7 +822,7 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
814
822
|
logger.verbose(
|
|
815
823
|
`${
|
|
816
824
|
EkuboCLVault.name
|
|
817
|
-
}: _solveExpectedAmountsEq => x: ${x.toString()}, y: ${y.toString()}, amount0: ${availableAmount0.toString()}, amount1: ${availableAmount1.toString()}`
|
|
825
|
+
}: _solveExpectedAmountsEq => ratio: ${ratio.toString()}, x: ${x.toString()}, y: ${y.toString()}, amount0: ${availableAmount0.toString()}, amount1: ${availableAmount1.toString()}`
|
|
818
826
|
);
|
|
819
827
|
|
|
820
828
|
if (ratio.eq(0)) {
|
|
@@ -872,7 +880,7 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
872
880
|
};
|
|
873
881
|
}
|
|
874
882
|
|
|
875
|
-
async getSwapInfoToHandleUnused(considerRebalance: boolean = true, newBounds: EkuboBounds | null = null, maxIterations = 20): Promise<SwapInfo> {
|
|
883
|
+
async getSwapInfoToHandleUnused(considerRebalance: boolean = true, newBounds: EkuboBounds | null = null, maxIterations = 20, priceRatioPrecision = 4): Promise<SwapInfo> {
|
|
876
884
|
const poolKey = await this.getPoolKey();
|
|
877
885
|
|
|
878
886
|
// fetch current unused balances of vault
|
|
@@ -928,21 +936,100 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
928
936
|
`${EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${ekuboBounds.lowerTick}, ${ekuboBounds.upperTick}`
|
|
929
937
|
);
|
|
930
938
|
|
|
939
|
+
// assert bounds are valid
|
|
940
|
+
this.assertValidBounds(ekuboBounds);
|
|
941
|
+
|
|
931
942
|
return await this.getSwapInfoGivenAmounts(
|
|
932
943
|
poolKey,
|
|
933
944
|
token0Bal,
|
|
934
945
|
token1Bal,
|
|
935
946
|
ekuboBounds,
|
|
936
|
-
maxIterations
|
|
947
|
+
maxIterations,
|
|
948
|
+
priceRatioPrecision
|
|
937
949
|
);
|
|
938
950
|
}
|
|
939
951
|
|
|
952
|
+
assertValidBounds(bounds: EkuboBounds) {
|
|
953
|
+
// Ensure bounds are valid
|
|
954
|
+
assert(
|
|
955
|
+
bounds.lowerTick < bounds.upperTick,
|
|
956
|
+
`Invalid bounds: lowerTick (${bounds.lowerTick}) must be less than upperTick (${bounds.upperTick})`
|
|
957
|
+
);
|
|
958
|
+
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})`);
|
|
959
|
+
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})`);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// Helper to check for invalid states:
|
|
963
|
+
// Throws if both tokens are decreased or both are increased, which is not expected
|
|
964
|
+
assertValidAmounts(
|
|
965
|
+
expectedAmounts: any,
|
|
966
|
+
token0Bal: Web3Number,
|
|
967
|
+
token1Bal: Web3Number
|
|
968
|
+
) {
|
|
969
|
+
if (
|
|
970
|
+
expectedAmounts.amount0.lessThan(token0Bal) &&
|
|
971
|
+
expectedAmounts.amount1.lessThan(token1Bal)
|
|
972
|
+
) {
|
|
973
|
+
throw new Error("Both tokens are decreased, something is wrong");
|
|
974
|
+
}
|
|
975
|
+
if (
|
|
976
|
+
expectedAmounts.amount0.greaterThan(token0Bal) &&
|
|
977
|
+
expectedAmounts.amount1.greaterThan(token1Bal)
|
|
978
|
+
) {
|
|
979
|
+
throw new Error("Both tokens are increased, something is wrong");
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// Helper to determine which token to sell, which to buy, and the amounts to use
|
|
984
|
+
getSwapParams(
|
|
985
|
+
expectedAmounts: any,
|
|
986
|
+
poolKey: EkuboPoolKey,
|
|
987
|
+
token0Bal: Web3Number,
|
|
988
|
+
token1Bal: Web3Number
|
|
989
|
+
) {
|
|
990
|
+
// Decide which token to sell based on which expected amount is less than the balance
|
|
991
|
+
const tokenToSell = expectedAmounts.amount0.lessThan(token0Bal)
|
|
992
|
+
? poolKey.token0
|
|
993
|
+
: poolKey.token1;
|
|
994
|
+
// The other token is the one to buy
|
|
995
|
+
const tokenToBuy =
|
|
996
|
+
tokenToSell == poolKey.token0 ? poolKey.token1 : poolKey.token0;
|
|
997
|
+
// Calculate how much to sell
|
|
998
|
+
const amountToSell =
|
|
999
|
+
tokenToSell == poolKey.token0
|
|
1000
|
+
? token0Bal.minus(expectedAmounts.amount0)
|
|
1001
|
+
: token1Bal.minus(expectedAmounts.amount1);
|
|
1002
|
+
if (amountToSell.eq(0)) {
|
|
1003
|
+
throw new Error(
|
|
1004
|
+
`No amount to sell for ${tokenToSell.address}`
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
// The remaining amount of the sold token after swap
|
|
1008
|
+
const remainingSellAmount =
|
|
1009
|
+
tokenToSell == poolKey.token0
|
|
1010
|
+
? expectedAmounts.amount0
|
|
1011
|
+
: expectedAmounts.amount1;
|
|
1012
|
+
return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
* @description Calculates swap info based on given amounts of token0 and token1
|
|
1017
|
+
* Use token0 and token1 balances to determine the expected amounts for new bounds
|
|
1018
|
+
* @param poolKey
|
|
1019
|
+
* @param token0Bal
|
|
1020
|
+
* @param token1Bal
|
|
1021
|
+
* @param bounds // new bounds
|
|
1022
|
+
* @param maxIterations
|
|
1023
|
+
* @returns {Promise<SwapInfo>}
|
|
1024
|
+
*
|
|
1025
|
+
*/
|
|
940
1026
|
async getSwapInfoGivenAmounts(
|
|
941
1027
|
poolKey: EkuboPoolKey,
|
|
942
1028
|
token0Bal: Web3Number,
|
|
943
1029
|
token1Bal: Web3Number,
|
|
944
1030
|
bounds: EkuboBounds,
|
|
945
|
-
maxIterations: number = 20
|
|
1031
|
+
maxIterations: number = 20,
|
|
1032
|
+
priceRatioPrecision: number = 4
|
|
946
1033
|
): Promise<SwapInfo> {
|
|
947
1034
|
logger.verbose(
|
|
948
1035
|
`${
|
|
@@ -965,54 +1052,6 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
965
1052
|
let retry = 0;
|
|
966
1053
|
const maxRetry = maxIterations;
|
|
967
1054
|
|
|
968
|
-
// Helper to check for invalid states:
|
|
969
|
-
// Throws if both tokens are decreased or both are increased, which is not expected
|
|
970
|
-
function assertValidAmounts(
|
|
971
|
-
expectedAmounts: any,
|
|
972
|
-
token0Bal: Web3Number,
|
|
973
|
-
token1Bal: Web3Number
|
|
974
|
-
) {
|
|
975
|
-
if (
|
|
976
|
-
expectedAmounts.amount0.lessThan(token0Bal) &&
|
|
977
|
-
expectedAmounts.amount1.lessThan(token1Bal)
|
|
978
|
-
) {
|
|
979
|
-
throw new Error("Both tokens are decreased, something is wrong");
|
|
980
|
-
}
|
|
981
|
-
if (
|
|
982
|
-
expectedAmounts.amount0.greaterThan(token0Bal) &&
|
|
983
|
-
expectedAmounts.amount1.greaterThan(token1Bal)
|
|
984
|
-
) {
|
|
985
|
-
throw new Error("Both tokens are increased, something is wrong");
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
// Helper to determine which token to sell, which to buy, and the amounts to use
|
|
990
|
-
function getSwapParams(
|
|
991
|
-
expectedAmounts: any,
|
|
992
|
-
poolKey: EkuboPoolKey,
|
|
993
|
-
token0Bal: Web3Number,
|
|
994
|
-
token1Bal: Web3Number
|
|
995
|
-
) {
|
|
996
|
-
// Decide which token to sell based on which expected amount is less than the balance
|
|
997
|
-
const tokenToSell = expectedAmounts.amount0.lessThan(token0Bal)
|
|
998
|
-
? poolKey.token0
|
|
999
|
-
: poolKey.token1;
|
|
1000
|
-
// The other token is the one to buy
|
|
1001
|
-
const tokenToBuy =
|
|
1002
|
-
tokenToSell == poolKey.token0 ? poolKey.token1 : poolKey.token0;
|
|
1003
|
-
// Calculate how much to sell
|
|
1004
|
-
const amountToSell =
|
|
1005
|
-
tokenToSell == poolKey.token0
|
|
1006
|
-
? token0Bal.minus(expectedAmounts.amount0)
|
|
1007
|
-
: token1Bal.minus(expectedAmounts.amount1);
|
|
1008
|
-
// The remaining amount of the sold token after swap
|
|
1009
|
-
const remainingSellAmount =
|
|
1010
|
-
tokenToSell == poolKey.token0
|
|
1011
|
-
? expectedAmounts.amount0
|
|
1012
|
-
: expectedAmounts.amount1;
|
|
1013
|
-
return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
1055
|
// Main retry loop: attempts to find a swap that matches the expected ratio within tolerance
|
|
1017
1056
|
while (retry < maxRetry) {
|
|
1018
1057
|
retry++;
|
|
@@ -1021,11 +1060,11 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1021
1060
|
);
|
|
1022
1061
|
|
|
1023
1062
|
// Ensure the expected amounts are valid for swap logic
|
|
1024
|
-
assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
|
|
1063
|
+
this.assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
|
|
1025
1064
|
|
|
1026
1065
|
// Get swap parameters for this iteration
|
|
1027
1066
|
const { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount } =
|
|
1028
|
-
getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal);
|
|
1067
|
+
this.getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal);
|
|
1029
1068
|
|
|
1030
1069
|
const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
|
|
1031
1070
|
const expectedRatio = expectedAmounts.ratio;
|
|
@@ -1067,12 +1106,16 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1067
1106
|
);
|
|
1068
1107
|
}
|
|
1069
1108
|
|
|
1070
|
-
//
|
|
1109
|
+
// Raw amount out
|
|
1071
1110
|
const amountOut = Web3Number.fromWei(
|
|
1072
1111
|
quote.buyAmount.toString(),
|
|
1073
1112
|
tokenToBuyInfo.decimals
|
|
1074
1113
|
);
|
|
1114
|
+
logger.verbose(
|
|
1115
|
+
`${EkuboCLVault.name}: getSwapInfoToHandleUnused => amountOut: ${amountOut.toString()}`
|
|
1116
|
+
);
|
|
1075
1117
|
// Calculate the swap price depending on which token is being sold
|
|
1118
|
+
// price is token1 / token0
|
|
1076
1119
|
const swapPrice =
|
|
1077
1120
|
tokenToSell == poolKey.token0
|
|
1078
1121
|
? amountOut.dividedBy(amountToSell)
|
|
@@ -1091,12 +1134,19 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1091
1134
|
);
|
|
1092
1135
|
|
|
1093
1136
|
// If the new ratio is not within tolerance, adjust expected amounts and retry
|
|
1094
|
-
const expectedPrecision = Math.min(7,
|
|
1137
|
+
const expectedPrecision = Math.min(priceRatioPrecision); // e.g 7 for STRK, 4 for USDC
|
|
1138
|
+
const isWithInTolerance =
|
|
1139
|
+
Number(newRatio.toString()) <=
|
|
1140
|
+
expectedRatio * (1 + 1 / 10 ** expectedPrecision) &&
|
|
1141
|
+
Number(newRatio.toString()) >= expectedRatio * (1 - 1 / 10 ** expectedPrecision);
|
|
1142
|
+
const currentPrecision = (expectedRatio - Number(newRatio.toString())) / expectedRatio;
|
|
1143
|
+
logger.verbose(
|
|
1144
|
+
`${EkuboCLVault.name}: getSwapInfoToHandleUnused => isWithInTolerance: ${isWithInTolerance}, currentPrecision: ${currentPrecision.toString()}, expectedPrecision: ${expectedPrecision}`
|
|
1145
|
+
);
|
|
1095
1146
|
if (
|
|
1096
|
-
|
|
1097
|
-
Number(newRatio.toString()) < expectedRatio * (1 - 1 / 10 ** expectedPrecision)
|
|
1147
|
+
!isWithInTolerance
|
|
1098
1148
|
) {
|
|
1099
|
-
|
|
1149
|
+
const expectedAmountsNew = await this._solveExpectedAmountsEq(
|
|
1100
1150
|
token0Bal,
|
|
1101
1151
|
token1Bal,
|
|
1102
1152
|
new Web3Number(Number(expectedRatio).toFixed(13), 18),
|
|
@@ -1105,23 +1155,34 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1105
1155
|
logger.verbose(
|
|
1106
1156
|
`${
|
|
1107
1157
|
EkuboCLVault.name
|
|
1108
|
-
}: getSwapInfoToHandleUnused => expectedAmounts: ${
|
|
1158
|
+
}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmountsNew.amount0.toString()}, ${expectedAmountsNew.amount1.toString()}`
|
|
1109
1159
|
);
|
|
1160
|
+
if (expectedAmountsNew.amount0.eq(expectedAmounts.amount0.toString()) && expectedAmountsNew.amount1.eq(expectedAmounts.amount1.toString())) {
|
|
1161
|
+
// If the expected amounts did not change, we are stuck in a loop
|
|
1162
|
+
logger.error(
|
|
1163
|
+
`getSwapInfoGivenAmounts: stuck in loop, expected amounts did not change`
|
|
1164
|
+
);
|
|
1165
|
+
throw new Error("Stuck in loop, expected amounts did not change");
|
|
1166
|
+
}
|
|
1167
|
+
expectedAmounts = expectedAmountsNew;
|
|
1110
1168
|
} else {
|
|
1111
1169
|
// Otherwise, return the swap info with a slippage buffer
|
|
1112
1170
|
const minAmountOut = Web3Number.fromWei(
|
|
1113
1171
|
quote.buyAmount.toString(),
|
|
1114
1172
|
tokenToBuyInfo.decimals
|
|
1115
1173
|
).multipliedBy(0.9999);
|
|
1116
|
-
|
|
1174
|
+
const output = await this.avnu.getSwapInfo(
|
|
1117
1175
|
quote,
|
|
1118
1176
|
this.address.address,
|
|
1119
1177
|
0,
|
|
1120
1178
|
this.address.address,
|
|
1121
1179
|
minAmountOut.toWei()
|
|
1122
1180
|
);
|
|
1181
|
+
logger.verbose(
|
|
1182
|
+
`${EkuboCLVault.name}: getSwapInfoToHandleUnused => swap info found: ${JSON.stringify(output)}`
|
|
1183
|
+
);
|
|
1184
|
+
return output;
|
|
1123
1185
|
}
|
|
1124
|
-
retry++;
|
|
1125
1186
|
}
|
|
1126
1187
|
|
|
1127
1188
|
// If no suitable swap found after max retries, throw error
|
|
@@ -1336,7 +1397,7 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1336
1397
|
};
|
|
1337
1398
|
}
|
|
1338
1399
|
|
|
1339
|
-
async harvest(acc: Account, maxIterations = 20): Promise<Call[]> {
|
|
1400
|
+
async harvest(acc: Account, maxIterations = 20, priceRatioPrecision = 4): Promise<Call[]> {
|
|
1340
1401
|
const ekuboHarvests = new EkuboHarvests(this.config);
|
|
1341
1402
|
const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
|
|
1342
1403
|
this.address
|
|
@@ -1380,7 +1441,8 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1380
1441
|
token0Amt,
|
|
1381
1442
|
token1Amt,
|
|
1382
1443
|
bounds,
|
|
1383
|
-
maxIterations
|
|
1444
|
+
maxIterations,
|
|
1445
|
+
priceRatioPrecision
|
|
1384
1446
|
);
|
|
1385
1447
|
swapInfo.token_to_address = token0Info.address.address;
|
|
1386
1448
|
logger.verbose(
|
|
@@ -1394,11 +1456,19 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1394
1456
|
const swap1Amount = Web3Number.fromWei(
|
|
1395
1457
|
uint256.uint256ToBN(swapInfo1.token_from_amount).toString(),
|
|
1396
1458
|
18 // cause its always STRK?
|
|
1397
|
-
)
|
|
1459
|
+
).minimum(
|
|
1460
|
+
postFeeAmount.toFixed(18) // cause always strk
|
|
1461
|
+
); // ensure we don't swap more than we have
|
|
1462
|
+
swapInfo.token_from_amount = uint256.bnToUint256(swap1Amount.toWei());
|
|
1463
|
+
swapInfo.token_to_min_amount = uint256.bnToUint256(
|
|
1464
|
+
swap1Amount.multipliedBy(0).toWei() // placeholder
|
|
1465
|
+
); // 0.01% slippage
|
|
1466
|
+
|
|
1398
1467
|
logger.verbose(
|
|
1399
1468
|
`${EkuboCLVault.name}: harvest => swap1Amount: ${swap1Amount}`
|
|
1400
1469
|
);
|
|
1401
|
-
|
|
1470
|
+
|
|
1471
|
+
const remainingAmount = postFeeAmount.minus(swap1Amount).maximum(0);
|
|
1402
1472
|
logger.verbose(
|
|
1403
1473
|
`${EkuboCLVault.name}: harvest => remainingAmount: ${remainingAmount}`
|
|
1404
1474
|
);
|
|
@@ -1438,7 +1508,11 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1438
1508
|
const _callsFinal = await this.rebalanceIter(
|
|
1439
1509
|
swapInfo,
|
|
1440
1510
|
acc,
|
|
1441
|
-
harvestEstimateCall
|
|
1511
|
+
harvestEstimateCall,
|
|
1512
|
+
claim.token.eq(poolKey.token0),
|
|
1513
|
+
0,
|
|
1514
|
+
0n,
|
|
1515
|
+
BigInt(postFeeAmount.toWei()), // upper limit is the post fee amount
|
|
1442
1516
|
);
|
|
1443
1517
|
logger.verbose(
|
|
1444
1518
|
`${EkuboCLVault.name}: harvest => _callsFinal: ${JSON.stringify(
|