@strkfarm/sdk 1.0.40 → 1.0.42
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 +138 -54
- package/dist/index.browser.mjs +138 -54
- package/dist/index.d.ts +18 -0
- package/dist/index.js +138 -54
- package/dist/index.mjs +138 -54
- package/package.json +1 -1
- package/src/modules/avnu.ts +18 -0
- package/src/strategies/ekubo-cl-vault.tsx +197 -79
|
@@ -62,6 +62,11 @@ export interface CLVaultStrategySettings {
|
|
|
62
62
|
lstContract?: ContractAddr;
|
|
63
63
|
truePrice?: number; // useful for pools where price is known (e.g. USDC/USDT as 1)
|
|
64
64
|
feeBps: number;
|
|
65
|
+
rebalanceConditions: {
|
|
66
|
+
minWaitHours: number; // number of hours out of range to rebalance
|
|
67
|
+
direction: "any" | "uponly"; // any for pools like USDC/USDT, uponly for pools like xSTRK/STRK
|
|
68
|
+
customShouldRebalance: (currentPoolPrice: number) => Promise<boolean>; // any additional logic for deciding factor to rebalance or not based on pools
|
|
69
|
+
};
|
|
65
70
|
}
|
|
66
71
|
|
|
67
72
|
export class EkuboCLVault extends BaseStrategy<
|
|
@@ -297,7 +302,10 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
297
302
|
? (await this.config.provider.getBlockWithTxs(blockIdentifier))
|
|
298
303
|
.timestamp
|
|
299
304
|
: new Date().getTime() / 1000;
|
|
300
|
-
const blockBefore = Math.max(
|
|
305
|
+
const blockBefore = Math.max(
|
|
306
|
+
blockNow - sinceBlocks,
|
|
307
|
+
this.metadata.launchBlock
|
|
308
|
+
);
|
|
301
309
|
const adjustedSupplyNow = supplyNow.minus(
|
|
302
310
|
await this.getHarvestRewardShares(blockBefore, blockNow)
|
|
303
311
|
);
|
|
@@ -369,7 +377,7 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
369
377
|
blockIdentifier: BlockIdentifier = "pending"
|
|
370
378
|
): Promise<Web3Number> {
|
|
371
379
|
let bal = await this.contract.call("balance_of", [user.address], {
|
|
372
|
-
blockIdentifier
|
|
380
|
+
blockIdentifier,
|
|
373
381
|
});
|
|
374
382
|
return Web3Number.fromWei(bal.toString(), 18);
|
|
375
383
|
}
|
|
@@ -807,6 +815,19 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
807
815
|
.minus(availableAmount0)
|
|
808
816
|
.dividedBy(ratio.plus(1 / price));
|
|
809
817
|
const x = y.dividedBy(price);
|
|
818
|
+
logger.verbose(
|
|
819
|
+
`${
|
|
820
|
+
EkuboCLVault.name
|
|
821
|
+
}: _solveExpectedAmountsEq => x: ${x.toString()}, y: ${y.toString()}, amount0: ${availableAmount0.toString()}, amount1: ${availableAmount1.toString()}`
|
|
822
|
+
);
|
|
823
|
+
|
|
824
|
+
if (ratio.eq(0)) {
|
|
825
|
+
return {
|
|
826
|
+
amount0: Web3Number.fromWei("0", availableAmount0.decimals),
|
|
827
|
+
amount1: availableAmount1.minus(y),
|
|
828
|
+
ratio: 0,
|
|
829
|
+
};
|
|
830
|
+
}
|
|
810
831
|
return {
|
|
811
832
|
amount0: availableAmount0.plus(x),
|
|
812
833
|
amount1: availableAmount1.minus(y),
|
|
@@ -814,10 +835,8 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
814
835
|
};
|
|
815
836
|
}
|
|
816
837
|
|
|
817
|
-
async
|
|
818
|
-
const poolKey = await this.getPoolKey();
|
|
819
|
-
|
|
820
|
-
// fetch current unused balances of vault
|
|
838
|
+
async unusedBalances(_poolKey?: EkuboPoolKey) {
|
|
839
|
+
const poolKey = _poolKey || (await this.getPoolKey());
|
|
821
840
|
const erc20Mod = new ERC20(this.config);
|
|
822
841
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
823
842
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
@@ -831,6 +850,7 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
831
850
|
this.address.address,
|
|
832
851
|
token1Info.decimals
|
|
833
852
|
);
|
|
853
|
+
|
|
834
854
|
logger.verbose(
|
|
835
855
|
`${
|
|
836
856
|
EkuboCLVault.name
|
|
@@ -841,16 +861,41 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
841
861
|
const token1Price = await this.pricer.getPrice(token1Info.symbol);
|
|
842
862
|
const token0PriceUsd = token0Price.price * Number(token0Bal1.toFixed(13));
|
|
843
863
|
const token1PriceUsd = token1Price.price * Number(token1Bal1.toFixed(13));
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
864
|
+
|
|
865
|
+
return {
|
|
866
|
+
token0: {
|
|
867
|
+
amount: token0Bal1,
|
|
868
|
+
tokenInfo: token0Info,
|
|
869
|
+
usdValue: token0PriceUsd,
|
|
870
|
+
},
|
|
871
|
+
token1: {
|
|
872
|
+
amount: token1Bal1,
|
|
873
|
+
tokenInfo: token1Info,
|
|
874
|
+
usdValue: token1PriceUsd,
|
|
875
|
+
},
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
async getSwapInfoToHandleUnused(considerRebalance: boolean = true) {
|
|
880
|
+
const poolKey = await this.getPoolKey();
|
|
881
|
+
|
|
882
|
+
// fetch current unused balances of vault
|
|
883
|
+
const unusedBalances = await this.unusedBalances(poolKey);
|
|
884
|
+
const { amount: token0Bal1, usdValue: token0PriceUsd } =
|
|
885
|
+
unusedBalances.token0;
|
|
886
|
+
const { amount: token1Bal1, usdValue: token1PriceUsd } =
|
|
887
|
+
unusedBalances.token1;
|
|
888
|
+
|
|
889
|
+
// if (token0PriceUsd > 1 && token1PriceUsd > 1) {
|
|
890
|
+
// // the swap is designed to handle one token only.
|
|
891
|
+
// // i.e. all balance should be in one token
|
|
892
|
+
// // except small amount of dust
|
|
893
|
+
// // so we need to call handle_fees first, which will atleast use
|
|
894
|
+
// // most of one token
|
|
895
|
+
// throw new Error(
|
|
896
|
+
// "Both tokens are non-zero and above $1, call handle_fees first"
|
|
897
|
+
// );
|
|
898
|
+
// }
|
|
854
899
|
|
|
855
900
|
let token0Bal = token0Bal1;
|
|
856
901
|
let token1Bal = token1Bal1;
|
|
@@ -875,16 +920,21 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
875
920
|
);
|
|
876
921
|
|
|
877
922
|
// get expected amounts for liquidity
|
|
878
|
-
|
|
923
|
+
let ekuboBounds: EkuboBounds;
|
|
924
|
+
if (considerRebalance) {
|
|
925
|
+
ekuboBounds = await this.getNewBounds();
|
|
926
|
+
} else {
|
|
927
|
+
ekuboBounds = await this.getCurrentBounds();
|
|
928
|
+
}
|
|
879
929
|
logger.verbose(
|
|
880
|
-
`${EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${
|
|
930
|
+
`${EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${ekuboBounds.lowerTick}, ${ekuboBounds.upperTick}`
|
|
881
931
|
);
|
|
882
932
|
|
|
883
933
|
return await this.getSwapInfoGivenAmounts(
|
|
884
934
|
poolKey,
|
|
885
935
|
token0Bal,
|
|
886
936
|
token1Bal,
|
|
887
|
-
|
|
937
|
+
ekuboBounds
|
|
888
938
|
);
|
|
889
939
|
}
|
|
890
940
|
|
|
@@ -894,6 +944,13 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
894
944
|
token1Bal: Web3Number,
|
|
895
945
|
bounds: EkuboBounds
|
|
896
946
|
): Promise<SwapInfo> {
|
|
947
|
+
logger.verbose(
|
|
948
|
+
`${
|
|
949
|
+
EkuboCLVault.name
|
|
950
|
+
}: getSwapInfoGivenAmounts::pre => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`
|
|
951
|
+
);
|
|
952
|
+
|
|
953
|
+
// Compute the expected amounts of token0 and token1 for the given liquidity bounds
|
|
897
954
|
let expectedAmounts = await this._getExpectedAmountsForLiquidity(
|
|
898
955
|
token0Bal,
|
|
899
956
|
token1Bal,
|
|
@@ -902,17 +959,19 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
902
959
|
logger.verbose(
|
|
903
960
|
`${
|
|
904
961
|
EkuboCLVault.name
|
|
905
|
-
}: getSwapInfoToHandleUnused =>
|
|
962
|
+
}: getSwapInfoToHandleUnused => expectedAmounts2: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
|
|
906
963
|
);
|
|
907
964
|
|
|
908
|
-
// get swap info
|
|
909
|
-
// fetch avnu routes to ensure expected amounts
|
|
910
965
|
let retry = 0;
|
|
911
966
|
const maxRetry = 10;
|
|
912
|
-
while (retry < maxRetry) {
|
|
913
|
-
retry++;
|
|
914
|
-
// assert one token is increased and other is decreased
|
|
915
967
|
|
|
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
|
+
) {
|
|
916
975
|
if (
|
|
917
976
|
expectedAmounts.amount0.lessThan(token0Bal) &&
|
|
918
977
|
expectedAmounts.amount1.lessThan(token1Bal)
|
|
@@ -925,58 +984,75 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
925
984
|
) {
|
|
926
985
|
throw new Error("Both tokens are increased, something is wrong");
|
|
927
986
|
}
|
|
987
|
+
}
|
|
928
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
|
|
929
997
|
const tokenToSell = expectedAmounts.amount0.lessThan(token0Bal)
|
|
930
998
|
? poolKey.token0
|
|
931
999
|
: poolKey.token1;
|
|
1000
|
+
// The other token is the one to buy
|
|
932
1001
|
const tokenToBuy =
|
|
933
1002
|
tokenToSell == poolKey.token0 ? poolKey.token1 : poolKey.token0;
|
|
934
|
-
|
|
1003
|
+
// Calculate how much to sell
|
|
1004
|
+
const amountToSell =
|
|
935
1005
|
tokenToSell == poolKey.token0
|
|
936
1006
|
? token0Bal.minus(expectedAmounts.amount0)
|
|
937
1007
|
: token1Bal.minus(expectedAmounts.amount1);
|
|
1008
|
+
// The remaining amount of the sold token after swap
|
|
938
1009
|
const remainingSellAmount =
|
|
939
1010
|
tokenToSell == poolKey.token0
|
|
940
1011
|
? expectedAmounts.amount0
|
|
941
1012
|
: expectedAmounts.amount1;
|
|
942
|
-
|
|
943
|
-
|
|
1013
|
+
return { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount };
|
|
1014
|
+
}
|
|
944
1015
|
|
|
1016
|
+
// Main retry loop: attempts to find a swap that matches the expected ratio within tolerance
|
|
1017
|
+
while (retry < maxRetry) {
|
|
1018
|
+
retry++;
|
|
945
1019
|
logger.verbose(
|
|
946
|
-
|
|
947
|
-
tokenToSell.address
|
|
948
|
-
}, tokenToBuy: ${
|
|
949
|
-
tokenToBuy.address
|
|
950
|
-
}, amountToSell: ${amountToSell.toWei()}`
|
|
951
|
-
);
|
|
952
|
-
logger.verbose(
|
|
953
|
-
`${
|
|
954
|
-
EkuboCLVault.name
|
|
955
|
-
}: getSwapInfoToHandleUnused => remainingSellAmount: ${remainingSellAmount.toString()}`
|
|
956
|
-
);
|
|
957
|
-
logger.verbose(
|
|
958
|
-
`${EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedRatio: ${expectedRatio}`
|
|
1020
|
+
`getSwapInfoGivenAmounts::Retry attempt: ${retry}/${maxRetry}`
|
|
959
1021
|
);
|
|
960
1022
|
|
|
1023
|
+
// Ensure the expected amounts are valid for swap logic
|
|
1024
|
+
assertValidAmounts(expectedAmounts, token0Bal, token1Bal);
|
|
1025
|
+
|
|
1026
|
+
// Get swap parameters for this iteration
|
|
1027
|
+
const { tokenToSell, tokenToBuy, amountToSell, remainingSellAmount } =
|
|
1028
|
+
getSwapParams(expectedAmounts, poolKey, token0Bal, token1Bal);
|
|
1029
|
+
|
|
1030
|
+
const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
|
|
1031
|
+
const expectedRatio = expectedAmounts.ratio;
|
|
1032
|
+
|
|
1033
|
+
logger.verbose(
|
|
1034
|
+
`${EkuboCLVault.name}: getSwapInfoToHandleUnused => iteration info: ${JSON.stringify({
|
|
1035
|
+
tokenToSell: tokenToSell.address,
|
|
1036
|
+
tokenToBuy: tokenToBuy.address,
|
|
1037
|
+
amountToSell: amountToSell.toString(),
|
|
1038
|
+
remainingSellAmount: remainingSellAmount.toString(),
|
|
1039
|
+
expectedRatio: expectedRatio
|
|
1040
|
+
})}`);
|
|
1041
|
+
|
|
1042
|
+
// If nothing to sell, return a zero swap
|
|
961
1043
|
if (amountToSell.eq(0)) {
|
|
962
|
-
return
|
|
963
|
-
token_from_address: tokenToSell.address,
|
|
964
|
-
token_from_amount: uint256.bnToUint256(0),
|
|
965
|
-
token_to_address: tokenToSell.address,
|
|
966
|
-
token_to_amount: uint256.bnToUint256(0),
|
|
967
|
-
token_to_min_amount: uint256.bnToUint256(0),
|
|
968
|
-
beneficiary: this.address.address,
|
|
969
|
-
integrator_fee_amount_bps: 0,
|
|
970
|
-
integrator_fee_recipient: this.address.address,
|
|
971
|
-
routes: [],
|
|
972
|
-
};
|
|
1044
|
+
return AvnuWrapper.buildZeroSwap(tokenToSell, this.address.address);
|
|
973
1045
|
}
|
|
1046
|
+
|
|
1047
|
+
// Get a quote for swapping the calculated amount
|
|
974
1048
|
const quote = await this.avnu.getQuotes(
|
|
975
1049
|
tokenToSell.address,
|
|
976
1050
|
tokenToBuy.address,
|
|
977
1051
|
amountToSell.toWei(),
|
|
978
1052
|
this.address.address
|
|
979
1053
|
);
|
|
1054
|
+
|
|
1055
|
+
// If all of the token is to be swapped, return the swap info directly
|
|
980
1056
|
if (remainingSellAmount.eq(0)) {
|
|
981
1057
|
const minAmountOut = Web3Number.fromWei(
|
|
982
1058
|
quote.buyAmount.toString(),
|
|
@@ -991,36 +1067,34 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
991
1067
|
);
|
|
992
1068
|
}
|
|
993
1069
|
|
|
1070
|
+
// Calculate the actual output and price from the quote
|
|
994
1071
|
const amountOut = Web3Number.fromWei(
|
|
995
1072
|
quote.buyAmount.toString(),
|
|
996
1073
|
tokenToBuyInfo.decimals
|
|
997
1074
|
);
|
|
1075
|
+
// Calculate the swap price depending on which token is being sold
|
|
998
1076
|
const swapPrice =
|
|
999
1077
|
tokenToSell == poolKey.token0
|
|
1000
1078
|
? amountOut.dividedBy(amountToSell)
|
|
1001
1079
|
: amountToSell.dividedBy(amountOut);
|
|
1080
|
+
// Calculate the new ratio after the swap
|
|
1002
1081
|
const newRatio =
|
|
1003
1082
|
tokenToSell == poolKey.token0
|
|
1004
1083
|
? remainingSellAmount.dividedBy(token1Bal.plus(amountOut))
|
|
1005
1084
|
: token0Bal.plus(amountOut).dividedBy(remainingSellAmount);
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
`${
|
|
1013
|
-
EkuboCLVault.name
|
|
1014
|
-
}: getSwapInfoToHandleUnused => swapPrice: ${swapPrice.toString()}`
|
|
1015
|
-
);
|
|
1016
|
-
logger.verbose(
|
|
1017
|
-
`${
|
|
1018
|
-
EkuboCLVault.name
|
|
1019
|
-
}: getSwapInfoToHandleUnused => newRatio: ${newRatio.toString()}`
|
|
1085
|
+
|
|
1086
|
+
logger.verbose(`${EkuboCLVault.name} getSwapInfoToHandleUnused => iter post calc: ${JSON.stringify({
|
|
1087
|
+
amountOut: amountOut.toString(),
|
|
1088
|
+
swapPrice: swapPrice.toString(),
|
|
1089
|
+
newRatio: newRatio.toString(),
|
|
1090
|
+
})}`
|
|
1020
1091
|
);
|
|
1092
|
+
|
|
1093
|
+
// If the new ratio is not within tolerance, adjust expected amounts and retry
|
|
1094
|
+
const expectedPrecision = Math.min(7, tokenToBuyInfo.decimals - 2);
|
|
1021
1095
|
if (
|
|
1022
|
-
Number(newRatio.toString()) > expectedRatio * 1
|
|
1023
|
-
Number(newRatio.toString()) < expectedRatio *
|
|
1096
|
+
Number(newRatio.toString()) > expectedRatio * (1 + 1 / 10 ** expectedPrecision) ||
|
|
1097
|
+
Number(newRatio.toString()) < expectedRatio * (1 - 1 / 10 ** expectedPrecision)
|
|
1024
1098
|
) {
|
|
1025
1099
|
expectedAmounts = await this._solveExpectedAmountsEq(
|
|
1026
1100
|
token0Bal,
|
|
@@ -1034,6 +1108,7 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1034
1108
|
}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
|
|
1035
1109
|
);
|
|
1036
1110
|
} else {
|
|
1111
|
+
// Otherwise, return the swap info with a slippage buffer
|
|
1037
1112
|
const minAmountOut = Web3Number.fromWei(
|
|
1038
1113
|
quote.buyAmount.toString(),
|
|
1039
1114
|
tokenToBuyInfo.decimals
|
|
@@ -1046,10 +1121,10 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1046
1121
|
minAmountOut.toWei()
|
|
1047
1122
|
);
|
|
1048
1123
|
}
|
|
1049
|
-
|
|
1050
1124
|
retry++;
|
|
1051
1125
|
}
|
|
1052
1126
|
|
|
1127
|
+
// If no suitable swap found after max retries, throw error
|
|
1053
1128
|
throw new Error("Failed to get swap info");
|
|
1054
1129
|
}
|
|
1055
1130
|
|
|
@@ -1266,6 +1341,9 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1266
1341
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
1267
1342
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
1268
1343
|
const bounds = await this.getCurrentBounds();
|
|
1344
|
+
logger.verbose(
|
|
1345
|
+
`${EkuboCLVault.name}: harvest => unClaimedRewards: ${unClaimedRewards.length}`
|
|
1346
|
+
);
|
|
1269
1347
|
const calls: Call[] = [];
|
|
1270
1348
|
for (let claim of unClaimedRewards) {
|
|
1271
1349
|
const fee = claim.claim.amount
|
|
@@ -1279,6 +1357,8 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1279
1357
|
EkuboCLVault.name
|
|
1280
1358
|
}: harvest => Processing claim, isToken1: ${isToken1} amount: ${postFeeAmount.toWei()}`
|
|
1281
1359
|
);
|
|
1360
|
+
|
|
1361
|
+
// todo what if the claim token is neither token0 or token1
|
|
1282
1362
|
const token0Amt = isToken1
|
|
1283
1363
|
? new Web3Number(0, token0Info.decimals)
|
|
1284
1364
|
: postFeeAmount;
|
|
@@ -1308,14 +1388,30 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1308
1388
|
const harvestEstimateCall = async (swapInfo1: SwapInfo) => {
|
|
1309
1389
|
const swap1Amount = Web3Number.fromWei(
|
|
1310
1390
|
uint256.uint256ToBN(swapInfo1.token_from_amount).toString(),
|
|
1311
|
-
18
|
|
1391
|
+
18 // cause its always STRK?
|
|
1392
|
+
);
|
|
1393
|
+
logger.verbose(
|
|
1394
|
+
`${EkuboCLVault.name}: harvest => swap1Amount: ${swap1Amount}`
|
|
1312
1395
|
);
|
|
1313
1396
|
const remainingAmount = postFeeAmount.minus(swap1Amount);
|
|
1397
|
+
logger.verbose(
|
|
1398
|
+
`${EkuboCLVault.name}: harvest => remainingAmount: ${remainingAmount}`
|
|
1399
|
+
);
|
|
1314
1400
|
const swapInfo2 = {
|
|
1315
1401
|
...swapInfo,
|
|
1316
1402
|
token_from_amount: uint256.bnToUint256(remainingAmount.toWei()),
|
|
1317
1403
|
};
|
|
1318
1404
|
swapInfo2.token_to_address = token1Info.address.address;
|
|
1405
|
+
logger.verbose(
|
|
1406
|
+
`${EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(
|
|
1407
|
+
swapInfo
|
|
1408
|
+
)}`
|
|
1409
|
+
);
|
|
1410
|
+
logger.verbose(
|
|
1411
|
+
`${EkuboCLVault.name}: harvest => swapInfo2: ${JSON.stringify(
|
|
1412
|
+
swapInfo2
|
|
1413
|
+
)}`
|
|
1414
|
+
);
|
|
1319
1415
|
const calldata = [
|
|
1320
1416
|
claim.rewardsContract.address,
|
|
1321
1417
|
{
|
|
@@ -1440,9 +1536,20 @@ const faqs: FAQ[] = [
|
|
|
1440
1536
|
},
|
|
1441
1537
|
{
|
|
1442
1538
|
question: "Is the strategy audited?",
|
|
1443
|
-
answer:
|
|
1444
|
-
<div>
|
|
1445
|
-
|
|
1539
|
+
answer: (
|
|
1540
|
+
<div>
|
|
1541
|
+
Yes, the strategy has been audited. You can review the audit report in
|
|
1542
|
+
our docs{" "}
|
|
1543
|
+
<a
|
|
1544
|
+
href="https://docs.strkfarm.com/p/ekubo-cl-vaults#technical-details"
|
|
1545
|
+
style={{ textDecoration: "underline", marginLeft: "5px" }}
|
|
1546
|
+
>
|
|
1547
|
+
Here
|
|
1548
|
+
</a>
|
|
1549
|
+
.
|
|
1550
|
+
</div>
|
|
1551
|
+
),
|
|
1552
|
+
},
|
|
1446
1553
|
];
|
|
1447
1554
|
/**
|
|
1448
1555
|
* Represents the Vesu Rebalance Strategies.
|
|
@@ -1504,6 +1611,11 @@ export const EkuboCLVaultStrategies: IStrategyMetadata<CLVaultStrategySettings>[
|
|
|
1504
1611
|
"0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a"
|
|
1505
1612
|
),
|
|
1506
1613
|
feeBps: 1000,
|
|
1614
|
+
rebalanceConditions: {
|
|
1615
|
+
customShouldRebalance: async (currentPrice: number) => true,
|
|
1616
|
+
minWaitHours: 24,
|
|
1617
|
+
direction: "uponly",
|
|
1618
|
+
},
|
|
1507
1619
|
},
|
|
1508
1620
|
faqs: [
|
|
1509
1621
|
...faqs,
|
|
@@ -1512,7 +1624,7 @@ export const EkuboCLVaultStrategies: IStrategyMetadata<CLVaultStrategySettings>[
|
|
|
1512
1624
|
answer:
|
|
1513
1625
|
"A negative APY can occur when xSTRK's price drops on DEXes. This is usually temporary and tends to recover within a few days or a week.",
|
|
1514
1626
|
},
|
|
1515
|
-
]
|
|
1627
|
+
],
|
|
1516
1628
|
},
|
|
1517
1629
|
{
|
|
1518
1630
|
name: "Ekubo USDC/USDT",
|
|
@@ -1549,8 +1661,10 @@ export const EkuboCLVaultStrategies: IStrategyMetadata<CLVaultStrategySettings>[
|
|
|
1549
1661
|
risk: {
|
|
1550
1662
|
riskFactor: _riskFactorStable,
|
|
1551
1663
|
netRisk:
|
|
1552
|
-
|
|
1553
|
-
|
|
1664
|
+
_riskFactorStable.reduce(
|
|
1665
|
+
(acc, curr) => acc + curr.value * curr.weight,
|
|
1666
|
+
0
|
|
1667
|
+
) / _riskFactorStable.reduce((acc, curr) => acc + curr.weight, 0),
|
|
1554
1668
|
notARisks: getNoRiskTags(_riskFactorStable),
|
|
1555
1669
|
},
|
|
1556
1670
|
apyMethodology:
|
|
@@ -1562,9 +1676,13 @@ export const EkuboCLVaultStrategies: IStrategyMetadata<CLVaultStrategySettings>[
|
|
|
1562
1676
|
},
|
|
1563
1677
|
truePrice: 1,
|
|
1564
1678
|
feeBps: 1000,
|
|
1679
|
+
rebalanceConditions: {
|
|
1680
|
+
customShouldRebalance: async (currentPrice: number) =>
|
|
1681
|
+
currentPrice > 0.99 && currentPrice < 1.01,
|
|
1682
|
+
minWaitHours: 6,
|
|
1683
|
+
direction: "any",
|
|
1684
|
+
},
|
|
1565
1685
|
},
|
|
1566
|
-
faqs: [
|
|
1567
|
-
...faqs,
|
|
1568
|
-
]
|
|
1686
|
+
faqs: [...faqs],
|
|
1569
1687
|
},
|
|
1570
1688
|
];
|