@strkfarm/sdk 1.2.0 → 2.0.0-dca.0.3

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.
Files changed (42) hide show
  1. package/dist/cli.js +9 -5
  2. package/dist/cli.mjs +9 -5
  3. package/dist/index.browser.global.js +67052 -40450
  4. package/dist/index.browser.mjs +5241 -1906
  5. package/dist/index.d.ts +483 -33
  6. package/dist/index.js +5523 -2154
  7. package/dist/index.mjs +5460 -2123
  8. package/package.json +4 -1
  9. package/src/data/ekubo-price-fethcer.abi.json +265 -0
  10. package/src/data/yoloVault.abi.json +777 -0
  11. package/src/dataTypes/_bignumber.ts +5 -0
  12. package/src/dataTypes/bignumber.browser.ts +5 -0
  13. package/src/dataTypes/bignumber.node.ts +5 -0
  14. package/src/dataTypes/index.ts +3 -2
  15. package/src/dataTypes/mynumber.ts +141 -0
  16. package/src/global.ts +42 -0
  17. package/src/index.browser.ts +2 -1
  18. package/src/interfaces/common.tsx +168 -2
  19. package/src/modules/apollo-client-config.ts +28 -0
  20. package/src/modules/avnu.ts +1 -1
  21. package/src/modules/ekubo-pricer.ts +79 -0
  22. package/src/modules/erc20.ts +18 -2
  23. package/src/modules/pragma.ts +23 -8
  24. package/src/modules/pricer-from-api.ts +150 -14
  25. package/src/modules/pricer.ts +2 -1
  26. package/src/modules/pricerBase.ts +2 -1
  27. package/src/node/pricer-redis.ts +2 -1
  28. package/src/strategies/base-strategy.ts +81 -2
  29. package/src/strategies/ekubo-cl-vault.tsx +684 -316
  30. package/src/strategies/factory.ts +179 -0
  31. package/src/strategies/index.ts +5 -1
  32. package/src/strategies/registry.ts +240 -0
  33. package/src/strategies/sensei.ts +335 -7
  34. package/src/strategies/types.ts +4 -0
  35. package/src/strategies/universal-adapters/vesu-adapter.ts +48 -27
  36. package/src/strategies/universal-lst-muliplier-strategy.tsx +1405 -462
  37. package/src/strategies/universal-strategy.tsx +287 -129
  38. package/src/strategies/vesu-rebalance.tsx +242 -146
  39. package/src/strategies/yoloVault.ts +499 -0
  40. package/src/utils/index.ts +1 -1
  41. package/src/utils/logger.node.ts +11 -4
  42. package/src/utils/strategy-utils.ts +61 -0
@@ -1,45 +1,105 @@
1
- import { FAQ, getNoRiskTags, highlightTextWithLinks, IConfig, IStrategyMetadata, Protocols, RiskFactor, RiskType, TokenInfo } from "@/interfaces";
2
- import { AUMTypes, getContractDetails, UNIVERSAL_ADAPTERS, UNIVERSAL_MANAGE_IDS, UniversalManageCall, UniversalStrategy, UniversalStrategySettings } from "./universal-strategy";
1
+ import {
2
+ FAQ,
3
+ getNoRiskTags,
4
+ highlightTextWithLinks,
5
+ IConfig,
6
+ IStrategyMetadata,
7
+ Protocols,
8
+ RiskFactor,
9
+ RiskType,
10
+ StrategyTag,
11
+ TokenInfo,
12
+ AuditStatus,
13
+ SourceCodeType,
14
+ AccessControlType,
15
+ InstantWithdrawalVault,
16
+ StrategyLiveStatus,
17
+ StrategySettings,
18
+ VaultType,
19
+ RedemptionInfo,
20
+ } from "@/interfaces";
21
+ import {
22
+ AUMTypes,
23
+ getContractDetails,
24
+ UNIVERSAL_ADAPTERS,
25
+ UNIVERSAL_MANAGE_IDS,
26
+ UniversalManageCall,
27
+ UniversalStrategy,
28
+ UniversalStrategySettings,
29
+ } from "./universal-strategy";
3
30
  import { PricerBase } from "@/modules/pricerBase";
4
31
  import { ContractAddr, Web3Number } from "@/dataTypes";
5
32
  import { Global } from "@/global";
6
- import { ApproveCallParams, AvnuSwapCallParams, CommonAdapter, getVesuSingletonAddress, ManageCall, Swap, VesuAdapter, VesuModifyDelegationCallParams, VesuModifyPositionCallParams, VesuMultiplyCallParams, VesuPools } from "./universal-adapters";
33
+ import {
34
+ ApproveCallParams,
35
+ AvnuSwapCallParams,
36
+ CommonAdapter,
37
+ getVesuSingletonAddress,
38
+ ManageCall,
39
+ Swap,
40
+ VesuAdapter,
41
+ VesuModifyDelegationCallParams,
42
+ VesuModifyPositionCallParams,
43
+ VesuMultiplyCallParams,
44
+ VesuPools,
45
+ } from "./universal-adapters";
7
46
  import { AVNU_EXCHANGE } from "./universal-adapters/adapter-utils";
8
- import { DepegRiskLevel, LiquidationRiskLevel, SmartContractRiskLevel, TechnicalRiskLevel } from "@/interfaces/risks";
9
- import { AvnuWrapper, EkuboQuoter, ERC20, LSTAPRService, PricerLST } from "@/modules";
47
+ import {
48
+ DepegRiskLevel,
49
+ LiquidationRiskLevel,
50
+ SmartContractRiskLevel,
51
+ TechnicalRiskLevel,
52
+ } from "@/interfaces/risks";
53
+ import {
54
+ AvnuWrapper,
55
+ EkuboQuoter,
56
+ ERC20,
57
+ LSTAPRService,
58
+ PricerLST,
59
+ } from "@/modules";
10
60
  import { assert, logger } from "@/utils";
11
61
  import { SingleTokenInfo } from "./base-strategy";
12
62
  import { Call, Contract, uint256 } from "starknet";
13
63
  import ERC4626Abi from "@/data/erc4626.abi.json";
14
64
  import { HealthFactorMath } from "@/utils/health-factor-math";
15
65
  import { findMaxInputWithSlippage } from "@/utils/math-utils";
66
+ import { LSTPriceType } from "./types";
16
67
 
17
68
  export interface HyperLSTStrategySettings extends UniversalStrategySettings {
18
- borrowable_assets: TokenInfo[];
69
+ borrowable_assets: { token: TokenInfo; poolId: ContractAddr }[];
19
70
  underlyingToken: TokenInfo;
20
71
  defaultPoolId: ContractAddr;
21
- altSupportedPoolIds: ContractAddr[];
22
72
  }
23
73
 
24
74
  export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTStrategySettings> {
25
-
26
75
  private quoteAmountToFetchPrice = new Web3Number(1, 18);
27
-
28
- constructor(config: IConfig, pricer: PricerBase, metadata: IStrategyMetadata<HyperLSTStrategySettings>) {
76
+
77
+ constructor(
78
+ config: IConfig,
79
+ pricer: PricerBase,
80
+ metadata: IStrategyMetadata<HyperLSTStrategySettings>,
81
+ ) {
29
82
  super(config, pricer, metadata);
30
83
 
31
- const STRKToken = Global.getDefaultTokens().find(token => token.symbol === 'STRK')!;
84
+ const STRKToken = Global.getDefaultTokens().find(
85
+ (token) => token.symbol === "STRK",
86
+ )!;
32
87
  const underlyingToken = this.getLSTUnderlyingTokenInfo();
33
88
  if (underlyingToken.address.eq(STRKToken.address)) {
34
89
  this.quoteAmountToFetchPrice = new Web3Number(100, 18);
35
90
  } else {
36
91
  // else this BTC
37
- this.quoteAmountToFetchPrice = new Web3Number(0.01, this.asset().decimals);
92
+ this.quoteAmountToFetchPrice = new Web3Number(
93
+ 0.01,
94
+ this.asset().decimals,
95
+ );
38
96
  }
39
97
  }
40
98
 
41
99
  asset() {
42
- return this.getVesuSameTokenAdapter(this.metadata.additionalInfo.defaultPoolId).config.collateral;
100
+ return this.getVesuSameTokenAdapter(
101
+ this.metadata.additionalInfo.defaultPoolId,
102
+ ).config.collateral;
43
103
  }
44
104
 
45
105
  getTag() {
@@ -48,7 +108,13 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
48
108
 
49
109
  // Vesu adapter with LST and base token match
50
110
  getVesuSameTokenAdapter(poolId: ContractAddr) {
51
- const baseAdapter = this.getAdapter(getVesuLegId(UNIVERSAL_MANAGE_IDS.VESU_LEG1, this.metadata.additionalInfo.underlyingToken.symbol, poolId.toString())) as VesuAdapter;
111
+ const baseAdapter = this.getAdapter(
112
+ getVesuLegId(
113
+ UNIVERSAL_MANAGE_IDS.VESU_LEG1,
114
+ this.metadata.additionalInfo.underlyingToken.symbol,
115
+ poolId.toString(),
116
+ ),
117
+ ) as VesuAdapter;
52
118
  baseAdapter.networkConfig = this.config;
53
119
  baseAdapter.pricer = this.pricer;
54
120
  return baseAdapter;
@@ -58,20 +124,21 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
58
124
  // todo support lending assets of underlying as well (like if xSTRK looping is not viable, simply supply STRK)
59
125
  getVesuAdapters() {
60
126
  const adapters: VesuAdapter[] = [];
61
- for (const poolId of [this.metadata.additionalInfo.defaultPoolId, ...this.metadata.additionalInfo.altSupportedPoolIds]) {
62
- const baseAdapter = this.getVesuSameTokenAdapter(poolId);
63
- for (const asset of this.metadata.additionalInfo.borrowable_assets) {
64
- const vesuAdapter1 = new VesuAdapter({
65
- poolId: baseAdapter.config.poolId,
66
- collateral: this.asset(),
67
- debt: asset,
68
- vaultAllocator: this.metadata.additionalInfo.vaultAllocator,
69
- id: ''
70
- })
71
- vesuAdapter1.pricer = this.pricer;
72
- vesuAdapter1.networkConfig = this.config;
73
- adapters.push(vesuAdapter1);
74
- }
127
+ for (const borrowableInfo of this.metadata.additionalInfo
128
+ .borrowable_assets) {
129
+ // do not add adapter if the pool id does not match
130
+ const asset = borrowableInfo.token;
131
+ const poolId = borrowableInfo.poolId;
132
+ const vesuAdapter1 = new VesuAdapter({
133
+ poolId: poolId,
134
+ collateral: this.asset(),
135
+ debt: asset,
136
+ vaultAllocator: this.metadata.additionalInfo.vaultAllocator,
137
+ id: "",
138
+ });
139
+ vesuAdapter1.pricer = this.pricer;
140
+ vesuAdapter1.networkConfig = this.config;
141
+ adapters.push(vesuAdapter1);
75
142
  }
76
143
  return adapters;
77
144
  }
@@ -80,7 +147,7 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
80
147
  // No rewards on collateral or borrowing of LST assets
81
148
  protected async getRewardsAUM(prevAum: Web3Number): Promise<Web3Number> {
82
149
  const lstToken = this.asset();
83
- if (lstToken.symbol === 'xSTRK') {
150
+ if (lstToken.symbol === "xSTRK") {
84
151
  return super.getRewardsAUM(prevAum);
85
152
  }
86
153
  return Web3Number.fromWei("0", lstToken.decimals);
@@ -93,110 +160,163 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
93
160
  const quote = await ekuboQuoter.getQuote(
94
161
  lstTokenInfo.address.address,
95
162
  lstUnderlyingTokenInfo.address.address,
96
- this.quoteAmountToFetchPrice
163
+ this.quoteAmountToFetchPrice,
97
164
  );
98
165
 
99
166
  // in Underlying
100
- const outputAmount = Web3Number.fromWei(quote.total_calculated, lstUnderlyingTokenInfo.decimals);
101
- const price = outputAmount.toNumber() / this.quoteAmountToFetchPrice.toNumber();
167
+ const outputAmount = Web3Number.fromWei(
168
+ quote.total_calculated,
169
+ lstUnderlyingTokenInfo.decimals,
170
+ );
171
+ const price =
172
+ outputAmount.toNumber() / this.quoteAmountToFetchPrice.toNumber();
102
173
  logger.verbose(`${this.getTag()}:: LST Dex Price: ${price}`);
103
174
  return price;
104
175
  }
105
176
 
106
-
107
177
  async getAvnuSwapMultiplyCall(params: {
108
- isDeposit: boolean,
109
- leg1DepositAmount: Web3Number
178
+ isDeposit: boolean;
179
+ leg1DepositAmount: Web3Number;
110
180
  }) {
111
- assert(params.isDeposit, 'Only deposit is supported in getAvnuSwapMultiplyCall')
181
+ assert(
182
+ params.isDeposit,
183
+ "Only deposit is supported in getAvnuSwapMultiplyCall",
184
+ );
112
185
  // TODO use a varibale for 1.02
113
- const maxBorrowableAmounts = await this.getMaxBorrowableAmount({ isAPYComputation: false });
186
+ const maxBorrowableAmounts = await this.getMaxBorrowableAmount({
187
+ isAPYComputation: false,
188
+ });
114
189
  const allVesuAdapters = this.getVesuAdapters();
115
190
  let remainingAmount = params.leg1DepositAmount;
116
191
  const lstExRate = await this.getLSTExchangeRate();
117
- const baseAssetPrice = await this.pricer.getPrice(this.getLSTUnderlyingTokenInfo().symbol);
192
+ const baseAssetPrice = await this.pricer.getPrice(
193
+ this.getLSTUnderlyingTokenInfo().symbol,
194
+ );
118
195
  const lstPrice = baseAssetPrice.price * lstExRate;
119
196
  for (let i = 0; i < maxBorrowableAmounts.maxBorrowables.length; i++) {
120
197
  const maxBorrowable = maxBorrowableAmounts.maxBorrowables[i];
121
- const vesuAdapter = allVesuAdapters.find(adapter => adapter.config.debt.address.eq(maxBorrowable.borrowableAsset.address));
198
+ const vesuAdapter = allVesuAdapters.find((adapter) =>
199
+ adapter.config.debt.address.eq(maxBorrowable.borrowableAsset.address),
200
+ );
122
201
  if (!vesuAdapter) {
123
- throw new Error(`${this.getTag()}::getAvnuSwapMultiplyCall: vesuAdapter not found for borrowable asset: ${maxBorrowable.borrowableAsset.symbol}`);
202
+ throw new Error(
203
+ `${this.getTag()}::getAvnuSwapMultiplyCall: vesuAdapter not found for borrowable asset: ${maxBorrowable.borrowableAsset.symbol}`,
204
+ );
124
205
  }
125
206
  const maxLTV = await vesuAdapter.getLTVConfig(this.config);
126
- const debtPrice = await this.pricer.getPrice(maxBorrowable.borrowableAsset.symbol);
127
- const maxAmountToDeposit = HealthFactorMath.getMinCollateralRequiredOnLooping(
128
- maxBorrowable.amount,
129
- debtPrice.price,
130
- this.metadata.additionalInfo.targetHealthFactor,
131
- maxLTV,
132
- lstPrice,
133
- this.asset()
134
- )
207
+ const debtPrice = await this.pricer.getPrice(
208
+ maxBorrowable.borrowableAsset.symbol,
209
+ );
210
+ const maxAmountToDeposit =
211
+ HealthFactorMath.getMinCollateralRequiredOnLooping(
212
+ maxBorrowable.amount,
213
+ debtPrice.price,
214
+ this.metadata.additionalInfo.targetHealthFactor,
215
+ maxLTV,
216
+ lstPrice,
217
+ this.asset(),
218
+ );
135
219
  const amountToDeposit = remainingAmount.minimum(maxAmountToDeposit);
136
- logger.verbose(`${this.getTag()}::getAvnuSwapMultiplyCall::${vesuAdapter.config.debt.symbol}:: remainingAmount: ${remainingAmount}, amountToDeposit: ${amountToDeposit}, depositAmount: ${amountToDeposit}, maxBorrowable: ${maxBorrowable.amount}`);
220
+ logger.verbose(
221
+ `${this.getTag()}::getAvnuSwapMultiplyCall::${vesuAdapter.config.debt.symbol}:: remainingAmount: ${remainingAmount}, amountToDeposit: ${amountToDeposit}, depositAmount: ${amountToDeposit}, maxBorrowable: ${maxBorrowable.amount}`,
222
+ );
137
223
  const call = await this._getAvnuDepositSwapLegCall({
138
224
  isDeposit: params.isDeposit,
139
225
  // adjust decimals of debt asset
140
226
  leg1DepositAmount: amountToDeposit,
141
227
  minHF: 1.1, // undo
142
- vesuAdapter: vesuAdapter
228
+ vesuAdapter: vesuAdapter,
143
229
  });
144
230
  remainingAmount = remainingAmount.minus(amountToDeposit);
145
231
 
146
232
  // return the first possible call because computing all calls at a time
147
233
  // is not efficinet for swaps
148
- return {call, vesuAdapter};
234
+ return { call, vesuAdapter };
149
235
  }
150
- throw new Error(`${this.getTag()}::getAvnuSwapMultiplyCall: no calls found`);
236
+ throw new Error(
237
+ `${this.getTag()}::getAvnuSwapMultiplyCall: no calls found`,
238
+ );
151
239
  }
152
240
 
153
241
  async _getAvnuDepositSwapLegCall(params: {
154
- isDeposit: boolean,
155
- leg1DepositAmount: Web3Number,
156
- minHF: number, // e.g. 1.01
157
- vesuAdapter: VesuAdapter
242
+ isDeposit: boolean;
243
+ leg1DepositAmount: Web3Number;
244
+ minHF: number; // e.g. 1.01
245
+ vesuAdapter: VesuAdapter;
158
246
  }) {
159
247
  const { vesuAdapter } = params;
160
- logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall params: ${JSON.stringify(params)}`);
161
- assert(params.isDeposit, 'Only deposit is supported in _getAvnuDepositSwapLegCall')
248
+ logger.verbose(
249
+ `${this.getTag()}::_getAvnuDepositSwapLegCall params: ${JSON.stringify(params)}`,
250
+ );
251
+ assert(
252
+ params.isDeposit,
253
+ "Only deposit is supported in _getAvnuDepositSwapLegCall",
254
+ );
162
255
  // add collateral
163
256
  // borrow STRK (e.g.)
164
257
  // approve and swap strk
165
258
  // add collateral again
166
259
 
167
-
168
260
  const legLTV = await vesuAdapter.getLTVConfig(this.config);
169
- logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall legLTV: ${legLTV}`);
261
+ logger.verbose(
262
+ `${this.getTag()}::_getAvnuDepositSwapLegCall legLTV: ${legLTV}`,
263
+ );
170
264
  const existingPositions = await vesuAdapter.getPositions(this.config);
171
- const collateralisation = await vesuAdapter.getCollateralization(this.config);
265
+ const collateralisation = await vesuAdapter.getCollateralization(
266
+ this.config,
267
+ );
172
268
  const existingCollateralInfo = existingPositions[0];
173
269
  const existingDebtInfo = existingPositions[1];
174
270
  logger.debug(`${this.getTag()}::_getAvnuDepositSwapLegCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
175
271
  existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
176
272
 
177
273
  // - Prices as seen by Vesu contracts, ideal for HF math
178
- // Price 1 is ok as fallback bcz that would relatively price the
274
+ // Price 1 is ok as fallback bcz that would relatively price the
179
275
  // collateral and debt as equal.
180
- const collateralPrice = collateralisation[0].usdValue > 0 ? collateralisation[0].usdValue / existingCollateralInfo.amount.toNumber() : 1;
181
- const debtPrice = collateralisation[1].usdValue > 0 ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber() : 1;
182
- logger.debug(`${this.getTag()}::_getAvnuDepositSwapLegCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
183
-
276
+ const collateralPrice =
277
+ collateralisation[0].usdValue > 0
278
+ ? collateralisation[0].usdValue /
279
+ existingCollateralInfo.amount.toNumber()
280
+ : 1;
281
+ const debtPrice =
282
+ collateralisation[1].usdValue > 0
283
+ ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber()
284
+ : 1;
285
+ logger.debug(
286
+ `${this.getTag()}::_getAvnuDepositSwapLegCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`,
287
+ );
184
288
 
185
289
  const debtTokenInfo = vesuAdapter.config.debt;
186
290
  let newDepositAmount = params.leg1DepositAmount;
187
- const totalCollateral = existingCollateralInfo.amount.plus(params.leg1DepositAmount);
188
- logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall totalCollateral: ${totalCollateral}`);
291
+ const totalCollateral = existingCollateralInfo.amount.plus(
292
+ params.leg1DepositAmount,
293
+ );
294
+ logger.verbose(
295
+ `${this.getTag()}::_getAvnuDepositSwapLegCall totalCollateral: ${totalCollateral}`,
296
+ );
189
297
  const totalDebtAmount = new Web3Number(
190
- totalCollateral.multipliedBy(collateralPrice).multipliedBy(legLTV).dividedBy(debtPrice).dividedBy(params.minHF).toString(),
191
- debtTokenInfo.decimals
298
+ totalCollateral
299
+ .multipliedBy(collateralPrice)
300
+ .multipliedBy(legLTV)
301
+ .dividedBy(debtPrice)
302
+ .dividedBy(params.minHF)
303
+ .toString(),
304
+ debtTokenInfo.decimals,
192
305
  );
193
306
  let debtAmount = totalDebtAmount.minus(existingDebtInfo.amount);
194
- logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall totalDebtAmount: ${totalDebtAmount}, initial computed debt: ${debtAmount}`);
195
- const maxBorrowable = await this.getMaxBorrowableAmountByVesuAdapter(vesuAdapter, false);
307
+ logger.verbose(
308
+ `${this.getTag()}::_getAvnuDepositSwapLegCall totalDebtAmount: ${totalDebtAmount}, initial computed debt: ${debtAmount}`,
309
+ );
310
+ const maxBorrowable = await this.getMaxBorrowableAmountByVesuAdapter(
311
+ vesuAdapter,
312
+ false,
313
+ );
196
314
 
197
315
  // if the debt amount is greater than 0 and the max borrowable amount is 0, skip
198
316
  if (debtAmount.gt(0) && maxBorrowable.amount.eq(0)) {
199
- logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall maxBorrowable is 0, skipping`);
317
+ logger.verbose(
318
+ `${this.getTag()}::_getAvnuDepositSwapLegCall maxBorrowable is 0, skipping`,
319
+ );
200
320
  return undefined;
201
321
  } else if (debtAmount.gt(0) && maxBorrowable.amount.gt(0)) {
202
322
  debtAmount = maxBorrowable.amount.minimum(debtAmount);
@@ -207,58 +327,78 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
207
327
  params.minHF,
208
328
  legLTV,
209
329
  collateralPrice,
210
- this.asset()
330
+ this.asset(),
331
+ );
332
+ newDepositAmount = totalCollateralRequired.minus(
333
+ existingCollateralInfo.amount,
211
334
  );
212
- newDepositAmount = totalCollateralRequired.minus(existingCollateralInfo.amount);
213
335
  if (newDepositAmount.lt(0)) {
214
- throw new Error(`${this.getTag()}::_getAvnuDepositSwapLegCall newDepositAmount is less than 0, newDepositAmount: ${newDepositAmount}, totalCollateralRequired: ${totalCollateralRequired}, existingCollateralInfo.amount: ${existingCollateralInfo.amount}`);
336
+ throw new Error(
337
+ `${this.getTag()}::_getAvnuDepositSwapLegCall newDepositAmount is less than 0, newDepositAmount: ${newDepositAmount}, totalCollateralRequired: ${totalCollateralRequired}, existingCollateralInfo.amount: ${existingCollateralInfo.amount}`,
338
+ );
215
339
  }
216
340
  if (newDebtUSDValue.toNumber() < 100) {
217
341
  // too less debt, skip
218
- logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall newDebtUSDValue is less than 100, skipping`);
342
+ logger.verbose(
343
+ `${this.getTag()}::_getAvnuDepositSwapLegCall newDebtUSDValue is less than 100, skipping`,
344
+ );
219
345
  return undefined;
220
346
  }
221
347
  }
222
348
 
223
- logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall debtAmount: ${debtAmount}`);
349
+ logger.verbose(
350
+ `${this.getTag()}::_getAvnuDepositSwapLegCall debtAmount: ${debtAmount}`,
351
+ );
224
352
  if (debtAmount.lt(0)) {
225
- // this is to unwind the position to optimal HF.
353
+ // this is to unwind the position to optimal HF.
226
354
  const lstDEXPrice = await this.getLSTDexPrice();
227
355
  const debtAmountInLST = debtAmount.abs().dividedBy(lstDEXPrice);
228
356
  const calls = await this.getVesuMultiplyCall({
229
357
  isDeposit: false,
230
358
  leg1DepositAmount: debtAmountInLST,
231
- poolId: vesuAdapter.config.poolId
232
- })
233
- assert(calls.length == 1, `Expected 1 call for unwind, got ${calls.length}`);
359
+ poolId: vesuAdapter.config.poolId,
360
+ });
361
+ assert(
362
+ calls.length == 1,
363
+ `Expected 1 call for unwind, got ${calls.length}`,
364
+ );
234
365
  return calls[0];
235
366
  }
236
- console.log(`debtAmount`, debtAmount.toWei(), params.leg1DepositAmount.toWei());
367
+ // console.log(`debtAmount`, debtAmount.toWei(), params.leg1DepositAmount.toWei());
237
368
  const STEP0 = UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1;
238
369
  const manage0Info = this.getProofs<ApproveCallParams>(STEP0);
239
370
  const manageCall0 = manage0Info.callConstructor({
240
- amount: newDepositAmount
371
+ amount: newDepositAmount,
241
372
  });
242
- const STEP1 = getVesuLegId(UNIVERSAL_MANAGE_IDS.VESU_LEG1, vesuAdapter.config.debt.symbol, vesuAdapter.config.poolId.toString());
373
+ const STEP1 = getVesuLegId(
374
+ UNIVERSAL_MANAGE_IDS.VESU_LEG1,
375
+ vesuAdapter.config.debt.symbol,
376
+ vesuAdapter.config.poolId.toString(),
377
+ );
243
378
  const manage1Info = this.getProofs<VesuModifyPositionCallParams>(STEP1);
244
- const manageCall1 = manage1Info.callConstructor(VesuAdapter.getDefaultModifyPositionCallParams({
379
+ const manageCall1 = manage1Info.callConstructor(
380
+ VesuAdapter.getDefaultModifyPositionCallParams({
245
381
  collateralAmount: newDepositAmount,
246
382
  isAddCollateral: params.isDeposit,
247
383
  debtAmount: debtAmount,
248
- isBorrow: params.isDeposit
249
- }));
384
+ isBorrow: params.isDeposit,
385
+ }),
386
+ );
387
+
388
+ // console.log(`manageCall1`, manageCall1.call, debtAmount.toWei(), newDepositAmount.toWei());
250
389
 
251
- console.log(`manageCall1`, manageCall1.call, debtAmount.toWei(), newDepositAmount.toWei());
252
-
253
390
  const proofIds: string[] = [STEP0, STEP1];
254
391
  const manageCalls: ManageCall[] = [manageCall0, manageCall1];
255
392
 
256
393
  // approve and swap to LST using avnu
257
394
  if (debtAmount.gt(0)) {
258
- const STEP2 = getAvnuManageIDs(LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_APPROVE_DEPOSIT, vesuAdapter.config.debt.symbol);
395
+ const STEP2 = getAvnuManageIDs(
396
+ LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_APPROVE_DEPOSIT,
397
+ vesuAdapter.config.debt.symbol,
398
+ );
259
399
  const manage2Info = this.getProofs<ApproveCallParams>(STEP2);
260
400
  const manageCall2 = manage2Info.callConstructor({
261
- amount: debtAmount
401
+ amount: debtAmount,
262
402
  });
263
403
 
264
404
  const debtTokenInfo = vesuAdapter.config.debt;
@@ -268,51 +408,75 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
268
408
  debtTokenInfo.address.address,
269
409
  lstTokenInfo.address.address,
270
410
  debtAmount.toWei(),
271
- this.metadata.additionalInfo.vaultAllocator.address
411
+ this.metadata.additionalInfo.vaultAllocator.address,
272
412
  );
273
413
  const minAmount = await this._getMinOutputAmountLSTBuy(debtAmount);
274
- const minAmountWei = (minAmount).toWei();
275
- logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall minAmount: ${minAmount}`);
414
+ const minAmountWei = minAmount.toWei();
415
+ logger.verbose(
416
+ `${this.getTag()}::_getAvnuDepositSwapLegCall minAmount: ${minAmount}`,
417
+ );
276
418
  const swapInfo = await avnuModule.getSwapInfo(
277
- quote,
278
- this.metadata.additionalInfo.vaultAllocator.address,
279
- 0,
419
+ quote,
420
+ this.metadata.additionalInfo.vaultAllocator.address,
421
+ 0,
280
422
  this.address.address,
281
- minAmountWei
423
+ minAmountWei,
424
+ );
425
+ logger.verbose(
426
+ `${this.getTag()}::_getAvnuDepositSwapLegCall swapInfo: ${JSON.stringify(swapInfo)}`,
427
+ );
428
+ const STEP3 = getAvnuManageIDs(
429
+ LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_SWAP_DEPOSIT,
430
+ vesuAdapter.config.debt.symbol,
282
431
  );
283
- logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall swapInfo: ${JSON.stringify(swapInfo)}`);
284
- const STEP3 = getAvnuManageIDs(LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_SWAP_DEPOSIT, vesuAdapter.config.debt.symbol);
285
432
  const manage3Info = this.getProofs<AvnuSwapCallParams>(STEP3);
286
433
  const manageCall3 = manage3Info.callConstructor({
287
- props: swapInfo
434
+ props: swapInfo,
288
435
  });
289
436
  proofIds.push(STEP2);
290
437
  proofIds.push(STEP3);
291
438
  manageCalls.push(manageCall2, manageCall3);
292
439
 
293
-
294
440
  // if the created debt, when added is collateral will put the total HF above min, but below (target + 0.05),
295
- // then lets close the looping cycle by adding this as collateral.
441
+ // then lets close the looping cycle by adding this as collateral.
296
442
  const newCollateral = minAmount.plus(totalCollateral);
297
- const newHF = newCollateral.multipliedBy(collateralPrice).multipliedBy(legLTV).dividedBy(totalDebtAmount).dividedBy(debtPrice).toNumber();
298
- logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall newHF: ${newHF}`);
299
- if (newHF > this.metadata.additionalInfo.minHealthFactor && newHF < this.metadata.additionalInfo.targetHealthFactor + 0.05) {
300
- logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall newHF is above min and below target + 0.05, adding collateral on vesu`);
443
+ const newHF = newCollateral
444
+ .multipliedBy(collateralPrice)
445
+ .multipliedBy(legLTV)
446
+ .dividedBy(totalDebtAmount)
447
+ .dividedBy(debtPrice)
448
+ .toNumber();
449
+ logger.verbose(
450
+ `${this.getTag()}::_getAvnuDepositSwapLegCall newHF: ${newHF}`,
451
+ );
452
+ if (
453
+ newHF > this.metadata.additionalInfo.minHealthFactor &&
454
+ newHF < this.metadata.additionalInfo.targetHealthFactor + 0.05
455
+ ) {
456
+ logger.verbose(
457
+ `${this.getTag()}::_getAvnuDepositSwapLegCall newHF is above min and below target + 0.05, adding collateral on vesu`,
458
+ );
301
459
  // approve and add collateral on vesu (modify position)
302
- const STEP4 = UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1;
460
+ const STEP4 = UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1
303
461
  const manage4Info = this.getProofs<ApproveCallParams>(STEP4);
304
462
  const manageCall4 = manage4Info.callConstructor({
305
- amount: minAmount
463
+ amount: minAmount,
306
464
  });
307
-
308
- const STEP5 = getVesuLegId(UNIVERSAL_MANAGE_IDS.VESU_LEG1, vesuAdapter.config.debt.symbol, vesuAdapter.config.poolId.toString());
465
+
466
+ const STEP5 = getVesuLegId(
467
+ UNIVERSAL_MANAGE_IDS.VESU_LEG1,
468
+ vesuAdapter.config.debt.symbol,
469
+ vesuAdapter.config.poolId.toString(),
470
+ );
309
471
  const manage5Info = this.getProofs<VesuModifyPositionCallParams>(STEP5);
310
- const manageCall5 = manage5Info.callConstructor(VesuAdapter.getDefaultModifyPositionCallParams({
311
- collateralAmount: minAmount,
312
- isAddCollateral: true,
313
- debtAmount: Web3Number.fromWei('0', this.asset().decimals),
314
- isBorrow: params.isDeposit
315
- }));
472
+ const manageCall5 = manage5Info.callConstructor(
473
+ VesuAdapter.getDefaultModifyPositionCallParams({
474
+ collateralAmount: minAmount,
475
+ isAddCollateral: true,
476
+ debtAmount: Web3Number.fromWei("0", this.asset().decimals),
477
+ isBorrow: params.isDeposit,
478
+ }),
479
+ );
316
480
  proofIds.push(STEP4, STEP5);
317
481
  manageCalls.push(manageCall4, manageCall5);
318
482
  }
@@ -322,53 +486,78 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
322
486
  return manageCall;
323
487
  }
324
488
 
325
- // todo unwind or not deposit when the yield is bad.
489
+ // todo unwind or not deposit when the yield is bad.
326
490
 
327
- async getLSTMultiplierRebalanceCall(): Promise<{ shouldRebalance: boolean, manageCalls: {vesuAdapter: VesuAdapter, manageCall: Call}[] }> {
491
+ async getLSTMultiplierRebalanceCall(): Promise<{
492
+ shouldRebalance: boolean;
493
+ manageCalls: { vesuAdapter: VesuAdapter; manageCall: Call }[];
494
+ }> {
328
495
  let shouldRebalance = false;
329
- const calls: {vesuAdapter: VesuAdapter, manageCall: Call}[] = [];
496
+ const calls: { vesuAdapter: VesuAdapter; manageCall: Call }[] = [];
330
497
  // todo undo
331
- const allVesuAdapters = this.getVesuAdapters().filter(vesuAdapter => vesuAdapter.config.debt.symbol === 'LBTC');
498
+ const allVesuAdapters = this.getVesuAdapters().filter(
499
+ (vesuAdapter) => vesuAdapter.config.debt.symbol === "LBTC",
500
+ );
332
501
  for (const vesuAdapter of allVesuAdapters) {
333
502
  const call = await this._getLSTMultiplierRebalanceCall(vesuAdapter);
334
503
  if (call.shouldRebalance && call.manageCall) {
335
504
  shouldRebalance = true;
336
- calls.push({vesuAdapter, manageCall: call.manageCall});
505
+ calls.push({ vesuAdapter, manageCall: call.manageCall });
337
506
  }
338
507
  }
339
508
  return { shouldRebalance, manageCalls: calls };
340
509
  }
341
510
 
342
- async _getLSTMultiplierRebalanceCall(vesuAdapter: VesuAdapter): Promise<{ shouldRebalance: boolean, manageCall: Call | undefined }> {
511
+ async _getLSTMultiplierRebalanceCall(
512
+ vesuAdapter: VesuAdapter,
513
+ ): Promise<{ shouldRebalance: boolean; manageCall: Call | undefined }> {
343
514
  const positions = await vesuAdapter.getPositions(this.config);
344
- assert(positions.length == 2, 'Rebalance call is only supported for 2 positions');
515
+ assert(
516
+ positions.length == 2,
517
+ "Rebalance call is only supported for 2 positions",
518
+ );
345
519
  const existingCollateralInfo = positions[0];
346
520
  const existingDebtInfo = positions[1];
347
521
  const unusedBalance = await this.getUnusedBalance();
348
522
  const healthFactor = await vesuAdapter.getHealthFactor();
349
523
 
350
- const collateralisation = await vesuAdapter.getCollateralization(this.config);
524
+ const collateralisation = await vesuAdapter.getCollateralization(
525
+ this.config,
526
+ );
351
527
  logger.debug(`${this.getTag()}::getVesuMultiplyCall::${vesuAdapter.config.debt.symbol} existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
352
528
  existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
353
529
 
354
530
  // - Prices as seen by Vesu contracts, ideal for HF math
355
- // Price 1 is ok as fallback bcz that would relatively price the
531
+ // Price 1 is ok as fallback bcz that would relatively price the
356
532
  // collateral and debt as equal.
357
- const collateralPrice = collateralisation[0].usdValue > 0 ? collateralisation[0].usdValue / existingCollateralInfo.amount.toNumber() : 1;
358
- const debtPrice = collateralisation[1].usdValue > 0 ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber() : 1;
359
- logger.debug(`${this.getTag()}::getVesuMultiplyCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
360
- logger.debug(`${this.getTag()}::getVesuMultiplyCall healthFactor: ${healthFactor}`);
533
+ const collateralPrice =
534
+ collateralisation[0].usdValue > 0
535
+ ? collateralisation[0].usdValue /
536
+ existingCollateralInfo.amount.toNumber()
537
+ : 1;
538
+ const debtPrice =
539
+ collateralisation[1].usdValue > 0
540
+ ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber()
541
+ : 1;
542
+ logger.debug(
543
+ `${this.getTag()}::getVesuMultiplyCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`,
544
+ );
545
+ logger.debug(
546
+ `${this.getTag()}::getVesuMultiplyCall healthFactor: ${healthFactor}`,
547
+ );
361
548
 
362
- const isHFTooLow = healthFactor < this.metadata.additionalInfo.minHealthFactor;
363
- const isHFTooHigh = healthFactor > this.metadata.additionalInfo.targetHealthFactor + 0.05;
549
+ const isHFTooLow =
550
+ healthFactor < this.metadata.additionalInfo.minHealthFactor;
551
+ const isHFTooHigh =
552
+ healthFactor > this.metadata.additionalInfo.targetHealthFactor + 0.05;
364
553
  if (isHFTooLow || isHFTooHigh || 1) {
365
- // use unused collateral to target more.
554
+ // use unused collateral to target more.
366
555
  const manageCall = await this._getAvnuDepositSwapLegCall({
367
556
  isDeposit: true,
368
557
  leg1DepositAmount: unusedBalance.amount,
369
- minHF: 1.02, // todo, shouldnt use this 1.02 HF, if there isn;t more looping left.
370
- vesuAdapter
371
- })
558
+ minHF: 1.02, // todo, shouldnt use this 1.02 HF, if there isn;t more looping left.
559
+ vesuAdapter,
560
+ });
372
561
  return { shouldRebalance: true, manageCall };
373
562
  } else {
374
563
  // do nothing
@@ -376,19 +565,61 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
376
565
  }
377
566
  }
378
567
 
379
- protected async getVesuAUM(adapter: VesuAdapter) {
568
+ protected async getVesuAUM(
569
+ adapter: VesuAdapter,
570
+ priceType: LSTPriceType = LSTPriceType.AVNU_PRICE,
571
+ ) {
380
572
  const legAUM = await adapter.getPositions(this.config);
381
573
  const underlying = this.asset();
382
574
  // assert its an LST of Endur
383
- assert(underlying.symbol.startsWith('x'), 'Underlying is not an LST of Endur');
575
+ assert(
576
+ underlying.symbol.startsWith("x"),
577
+ "Underlying is not an LST of Endur",
578
+ );
384
579
 
385
580
  let vesuAum = Web3Number.fromWei("0", underlying.decimals);
386
-
387
- let tokenUnderlyingPrice = await this.getLSTExchangeRate();
388
- // to offset for usual DEX lag, we multiply by 0.998 (i.e. 0.2% loss)
389
- tokenUnderlyingPrice = tokenUnderlyingPrice * 0.998;
390
- logger.verbose(`${this.getTag()} tokenUnderlyingPrice: ${tokenUnderlyingPrice}`);
391
-
581
+
582
+ // Get the price based on the price type
583
+ let tokenUnderlyingPrice: number;
584
+ if (priceType === LSTPriceType.ENDUR_PRICE) {
585
+ tokenUnderlyingPrice = await this.getLSTExchangeRate();
586
+ if (tokenUnderlyingPrice === 0) {
587
+ throw new Error(
588
+ `${this.getTag()}::getVesuAUM: tokenUnderlyingPrice (Endur) is 0`,
589
+ );
590
+ }
591
+
592
+ // For Endur price, also check against Avnu rate to ensure they're within 2%
593
+ const avnuRate = await this.getLSTAvnuRate();
594
+ if (avnuRate === 0) {
595
+ throw new Error(
596
+ `${this.getTag()}::getVesuAUM: tokenUnderlyingPrice (Avnu) is 0`,
597
+ );
598
+ }
599
+
600
+ const diff =
601
+ Math.abs(tokenUnderlyingPrice - avnuRate) / tokenUnderlyingPrice;
602
+ if (diff > 0.02) {
603
+ throw new Error(
604
+ `${this.getTag()}::getVesuAUM: Endur and Avnu prices differ by more than 2% (Endur: ${tokenUnderlyingPrice}, Avnu: ${avnuRate})`,
605
+ );
606
+ }
607
+
608
+ logger.verbose(
609
+ `${this.getTag()} tokenUnderlyingPrice (Endur): ${tokenUnderlyingPrice}, avnuRate: ${avnuRate}, diff: ${diff}`,
610
+ );
611
+ } else {
612
+ tokenUnderlyingPrice = await this.getLSTAvnuRate();
613
+ if (tokenUnderlyingPrice === 0) {
614
+ throw new Error(
615
+ `${this.getTag()}::getVesuAUM: tokenUnderlyingPrice (Avnu) is 0`,
616
+ );
617
+ }
618
+ logger.verbose(
619
+ `${this.getTag()} tokenUnderlyingPrice (Avnu): ${tokenUnderlyingPrice}`,
620
+ );
621
+ }
622
+
392
623
  // handle collateral
393
624
  if (legAUM[0].token.address.eq(underlying.address)) {
394
625
  vesuAum = vesuAum.plus(legAUM[0].amount);
@@ -401,40 +632,82 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
401
632
  vesuAum = vesuAum.minus(legAUM[1].amount);
402
633
  } else {
403
634
  vesuAum = vesuAum.minus(legAUM[1].amount.dividedBy(tokenUnderlyingPrice));
404
- };
635
+ }
405
636
 
406
- logger.verbose(`${this.getTag()} Vesu AUM: ${vesuAum}, legCollateral: ${legAUM[0].amount.toNumber()}, legDebt: ${legAUM[1].amount.toNumber()}`);
637
+ const priceTypeLabel =
638
+ priceType === LSTPriceType.ENDUR_PRICE ? "Endur Price" : "Avnu Price";
639
+ logger.verbose(
640
+ `${this.getTag()} Vesu AUM (${priceTypeLabel}): ${vesuAum}, legCollateral: ${legAUM[0].amount.toNumber()}, legDebt: ${legAUM[1].amount.toNumber()}`,
641
+ );
407
642
  return vesuAum;
408
643
  }
409
644
 
645
+ async getTVLUnrealized(): Promise<{
646
+ net: SingleTokenInfo;
647
+ prevAum: Web3Number;
648
+ splits: { id: string; aum: Web3Number }[];
649
+ }> {
650
+ return await this.getAUM(true);
651
+ }
652
+
653
+ async getUserUnrealizedGains(user: ContractAddr) {
654
+ // Get total TVL (realized)
655
+ const tvl = await this.getTVL();
656
+
657
+ // Get unrealized TVL (using Endur prices)
658
+ const unrealizedTVL = await this.getTVLUnrealized();
659
+
660
+ // Calculate the difference between unrealized and realized TVL
661
+ const unrealizedDiff = unrealizedTVL.net.amount.minus(tvl.amount);
662
+
663
+ // Get user's TVL
664
+ const userTVL = await this.getUserTVL(user);
665
+
666
+ // Calculate user's share of the total TVL
667
+ const userShare = userTVL.amount.dividedBy(tvl.amount);
668
+
669
+ // Calculate user's unrealized gains (in token amount)
670
+ const unrealizedGains = unrealizedDiff.multipliedBy(userShare);
671
+
672
+ return {
673
+ unrealizedGains,
674
+ userShare: userShare.toNumber(),
675
+ tokenInfo: this.asset(),
676
+ };
677
+ }
678
+
410
679
  //
411
680
  private async _getMinOutputAmountLSTBuy(amountInUnderlying: Web3Number) {
412
681
  const lstTruePrice = await this.getLSTExchangeRate();
413
- // during buy, the purchase should always be <= true LST price.
414
- const minOutputAmount = amountInUnderlying.dividedBy(lstTruePrice).multipliedBy(0.99979); // minus 0.021% to account for avnu fees
682
+ // during buy, the purchase should always be <= true LST price.
683
+ const minOutputAmount = amountInUnderlying
684
+ .dividedBy(lstTruePrice)
685
+ .multipliedBy(0.99979); // minus 0.021% to account for avnu fees
415
686
  return new Web3Number(minOutputAmount.toString(), this.asset().decimals);
416
687
  }
417
688
 
418
689
  private async _getMinOutputAmountLSTSell(amountInLST: Web3Number) {
419
690
  const lstTruePrice = await this.getLSTExchangeRate();
420
- // during sell, the purchase should always be > 0.995 * true LST price.
421
- const minOutputAmount = amountInLST.multipliedBy(lstTruePrice).multipliedBy(0.995);
691
+ // during sell, the purchase should always be > 0.995 * true LST price.
692
+ const minOutputAmount = amountInLST
693
+ .multipliedBy(lstTruePrice)
694
+ .multipliedBy(0.995);
422
695
  return minOutputAmount;
423
696
  }
424
697
 
425
698
  // todo add a function to findout max borrowable amount without fucking yield
426
- // if the current net yield < LST yield, add a function to calculate how much to unwind.
699
+ // if the current net yield < LST yield, add a function to calculate how much to unwind.
427
700
 
428
701
  /**
429
702
  * Uses vesu's multiple call to create leverage on LST
430
703
  * Deposit amount is in LST
431
- * @param params
704
+ * @param params
432
705
  */
433
706
  async getVesuMultiplyCall(params: {
434
- isDeposit: boolean,
435
- leg1DepositAmount: Web3Number,
436
- maxEkuboPriceImpact?: number,
437
- poolId: ContractAddr
707
+ isDeposit: boolean;
708
+ leg1DepositAmount: Web3Number;
709
+ maxEkuboPriceImpact?: number;
710
+ poolId: ContractAddr;
438
711
  }) {
439
712
  const maxEkuboPriceImpact = params.maxEkuboPriceImpact || 0.01;
440
713
  const vesuAdapter1 = this.getVesuSameTokenAdapter(params.poolId);
@@ -442,10 +715,12 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
442
715
  logger.verbose(`${this.getTag()}::getVesuMultiplyCall legLTV: ${legLTV}`);
443
716
 
444
717
  if (!params.isDeposit) {
445
- // try using unused balance to unwind.
446
- // no need to unwind.
718
+ // try using unused balance to unwind.
719
+ // no need to unwind.
447
720
  const unusedBalance = await this.getUnusedBalance();
448
- logger.verbose(`${this.getTag()}::getVesuMultiplyCall unusedBalance: ${unusedBalance.amount.toString()}, required: ${params.leg1DepositAmount.toString()}`);
721
+ logger.verbose(
722
+ `${this.getTag()}::getVesuMultiplyCall unusedBalance: ${unusedBalance.amount.toString()}, required: ${params.leg1DepositAmount.toString()}`,
723
+ );
449
724
  // undo
450
725
  // if (unusedBalance.amount.gte(params.leg1DepositAmount)) {
451
726
  // return [];
@@ -454,51 +729,85 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
454
729
  }
455
730
 
456
731
  const existingPositions = await vesuAdapter1.getPositions(this.config);
457
- const collateralisation = await vesuAdapter1.getCollateralization(this.config);
732
+ const collateralisation = await vesuAdapter1.getCollateralization(
733
+ this.config,
734
+ );
458
735
  const existingCollateralInfo = existingPositions[0];
459
736
  const existingDebtInfo = existingPositions[1];
460
737
  logger.debug(`${this.getTag()}::getVesuMultiplyCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
461
738
  existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
462
739
 
463
740
  // - Prices as seen by Vesu contracts, ideal for HF math
464
- // Price 1 is ok as fallback bcz that would relatively price the
741
+ // Price 1 is ok as fallback bcz that would relatively price the
465
742
  // collateral and debt as equal.
466
- const collateralPrice = collateralisation[0].usdValue > 0 ? collateralisation[0].usdValue / existingCollateralInfo.amount.toNumber() : 1;
467
- const debtPrice = collateralisation[1].usdValue > 0 ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber() : 1;
468
- logger.debug(`${this.getTag()}::getVesuMultiplyCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
743
+ const collateralPrice =
744
+ collateralisation[0].usdValue > 0
745
+ ? collateralisation[0].usdValue /
746
+ existingCollateralInfo.amount.toNumber()
747
+ : 1;
748
+ const debtPrice =
749
+ collateralisation[1].usdValue > 0
750
+ ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber()
751
+ : 1;
752
+ logger.debug(
753
+ `${this.getTag()}::getVesuMultiplyCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`,
754
+ );
469
755
 
470
756
  // - Prices as seen by actual swap price
471
757
  const dexPrice = await this.getLSTDexPrice();
472
758
 
473
759
  // compute optimal amount of collateral and debt post addition/removal
474
760
  // target hf = collateral * collateralPrice * ltv / debt * debtPrice
475
- // assuming X to be the usd amount of debt borrowed or repaied (negative).
761
+ // assuming X to be the usd amount of debt borrowed or repaied (negative).
476
762
  // target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv / (debt * debtPrice + X)
477
763
  // => X * target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv - (debt * debtPrice * target hf)
478
764
  // => X * (target hf - ltv)= ((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)
479
765
  // => X = (((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)) / (target hf - ltv)
480
- const addedCollateral = params.leg1DepositAmount
481
- .multipliedBy(params.isDeposit ? 1 : -1)
482
- logger.verbose(`${this.getTag()}::getVesuMultiplyCall addedCollateral: ${addedCollateral}`);
483
- const numeratorPart1 = (existingCollateralInfo.amount.plus((addedCollateral))).multipliedBy(collateralPrice).multipliedBy(legLTV);
484
- logger.verbose(`${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}`);
485
- const numeratorPart2 = existingDebtInfo.amount.multipliedBy(debtPrice).multipliedBy(this.metadata.additionalInfo.targetHealthFactor);
486
- logger.verbose(`${this.getTag()}::getVesuMultiplyCall numeratorPart2: ${numeratorPart2}`);
487
- const denominatorPart = this.metadata.additionalInfo.targetHealthFactor - (legLTV / dexPrice);
488
- logger.verbose(`${this.getTag()}::getVesuMultiplyCall denominatorPart: ${denominatorPart}`);
489
- const x_debt_usd = numeratorPart1.minus(numeratorPart2).dividedBy(denominatorPart);
490
- logger.verbose(`${this.getTag()}::getVesuMultiplyCall x_debt_usd: ${x_debt_usd}`);
491
- logger.debug(`${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}, numeratorPart2: ${numeratorPart2}, denominatorPart: ${denominatorPart}`);
766
+ const addedCollateral = params.leg1DepositAmount.multipliedBy(
767
+ params.isDeposit ? 1 : -1,
768
+ );
769
+ logger.verbose(
770
+ `${this.getTag()}::getVesuMultiplyCall addedCollateral: ${addedCollateral}`,
771
+ );
772
+ const numeratorPart1 = existingCollateralInfo.amount
773
+ .plus(addedCollateral)
774
+ .multipliedBy(collateralPrice)
775
+ .multipliedBy(legLTV);
776
+ logger.verbose(
777
+ `${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}`,
778
+ );
779
+ const numeratorPart2 = existingDebtInfo.amount
780
+ .multipliedBy(debtPrice)
781
+ .multipliedBy(this.metadata.additionalInfo.targetHealthFactor);
782
+ logger.verbose(
783
+ `${this.getTag()}::getVesuMultiplyCall numeratorPart2: ${numeratorPart2}`,
784
+ );
785
+ const denominatorPart =
786
+ this.metadata.additionalInfo.targetHealthFactor - legLTV / dexPrice;
787
+ logger.verbose(
788
+ `${this.getTag()}::getVesuMultiplyCall denominatorPart: ${denominatorPart}`,
789
+ );
790
+ const x_debt_usd = numeratorPart1
791
+ .minus(numeratorPart2)
792
+ .dividedBy(denominatorPart);
793
+ logger.verbose(
794
+ `${this.getTag()}::getVesuMultiplyCall x_debt_usd: ${x_debt_usd}`,
795
+ );
796
+ logger.debug(
797
+ `${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}, numeratorPart2: ${numeratorPart2}, denominatorPart: ${denominatorPart}`,
798
+ );
492
799
 
493
800
  // both in underlying
494
801
  let debtAmount = x_debt_usd.dividedBy(debtPrice);
495
802
  const marginAmount = addedCollateral;
496
- logger.verbose(`${this.getTag()}::getVesuMultiplyCall debtAmount: ${debtAmount}, marginAmount: ${marginAmount}`);
803
+ logger.verbose(
804
+ `${this.getTag()}::getVesuMultiplyCall debtAmount: ${debtAmount}, marginAmount: ${marginAmount}`,
805
+ );
497
806
 
498
807
  if (marginAmount.lt(0) && debtAmount.gt(0)) {
499
808
  // if we want to withdraw, but debt can go high, its conflicting between
500
- // increasing and reducing lever. and in this case, the HF will go high,
501
- // which is ok.
809
+ // increasing and reducing lever. and in this case, the HF will go high,
810
+ // which is ok.
502
811
  debtAmount = Web3Number.fromWei(0, this.asset().decimals);
503
812
  }
504
813
 
@@ -515,38 +824,60 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
515
824
  lstDexPriceInUnderlying: dexPrice,
516
825
  isIncrease: debtAmount.greaterThan(0),
517
826
  maxEkuboPriceImpact,
518
- poolId: params.poolId
827
+ poolId: params.poolId,
519
828
  });
520
829
  }
521
830
 
522
831
  getLSTUnderlyingTokenInfo() {
523
- const vesuAdapter1 = this.getVesuSameTokenAdapter(this.metadata.additionalInfo.defaultPoolId);
832
+ const vesuAdapter1 = this.getVesuSameTokenAdapter(
833
+ this.metadata.additionalInfo.defaultPoolId,
834
+ );
524
835
  return vesuAdapter1.config.debt;
525
836
  }
526
837
 
527
- async getMaxBorrowableAmount(params: { isAPYComputation: boolean } = { isAPYComputation: false }) {
838
+ async getMaxBorrowableAmount(
839
+ params: { isAPYComputation: boolean } = { isAPYComputation: false },
840
+ ) {
528
841
  const vesuAdapters = this.getVesuAdapters();
529
842
  let netMaxBorrowableAmount = Web3Number.fromWei("0", this.getLSTUnderlyingTokenInfo().decimals);
530
- const maxBorrowables: {amount: Web3Number, dexSwappableAmount: Web3Number, maxBorrowableAmount: Web3Number, borrowableAsset: TokenInfo, ltv: number}[] = [];
843
+ const maxBorrowables: {amount: Web3Number, dexSwappableAmount: Web3Number, maxBorrowableAmount: Web3Number, borrowableAsset: TokenInfo, ltv: number, poolId: ContractAddr}[] = [];
531
844
  for (const vesuAdapter of vesuAdapters) {
532
- const output = await this.getMaxBorrowableAmountByVesuAdapter(vesuAdapter, params.isAPYComputation);
845
+ const output = await this.getMaxBorrowableAmountByVesuAdapter(
846
+ vesuAdapter,
847
+ params.isAPYComputation,
848
+ );
533
849
  const ltv = await vesuAdapter.getLTVConfig(this.config);
534
- maxBorrowables.push({...output, ltv});
850
+ maxBorrowables.push({...output, ltv,poolId: vesuAdapter.config.poolId});
535
851
  }
536
852
  maxBorrowables.sort((a, b) => b.amount.toNumber() - a.amount.toNumber());
537
- netMaxBorrowableAmount = maxBorrowables.reduce((acc, curr) => acc.plus(curr.amount), Web3Number.fromWei("0", this.getLSTUnderlyingTokenInfo().decimals));
538
- return {netMaxBorrowableAmount, maxBorrowables};
853
+ netMaxBorrowableAmount = maxBorrowables.reduce(
854
+ (acc, curr) => acc.plus(curr.amount),
855
+ Web3Number.fromWei("0", this.getLSTUnderlyingTokenInfo().decimals),
856
+ );
857
+ return { netMaxBorrowableAmount, maxBorrowables };
539
858
  }
540
859
 
541
- // recursively, using binary search computes max swappable.
860
+ // recursively, using binary search computes max swappable.
542
861
  // @dev assumes 1 token of from == 1 token of to
543
- async getMaxSwappableWithMaxSlippage(fromToken: TokenInfo, toToken: TokenInfo, maxSlippage: number, maxAmount: Web3Number) {
862
+ async getMaxSwappableWithMaxSlippage(
863
+ fromToken: TokenInfo,
864
+ toToken: TokenInfo,
865
+ maxSlippage: number,
866
+ maxAmount: Web3Number,
867
+ ) {
544
868
  const output = await findMaxInputWithSlippage({
545
869
  apiGetOutput: async (inputAmount: number): Promise<number> => {
546
870
  const ekuboQuoter = new EkuboQuoter(this.config);
547
- await new Promise(resolve => setTimeout(resolve, 1000)); // artificial delay, to avoid rate limit
548
- const quote = await ekuboQuoter.getQuote(fromToken.address.address, toToken.address.address, new Web3Number(inputAmount.toFixed(9), fromToken.decimals));
549
- return Web3Number.fromWei(quote.total_calculated.toString(), toToken.decimals).toNumber();
871
+ await new Promise((resolve) => setTimeout(resolve, 1000)); // artificial delay, to avoid rate limit
872
+ const quote = await ekuboQuoter.getQuote(
873
+ fromToken.address.address,
874
+ toToken.address.address,
875
+ new Web3Number(inputAmount.toFixed(9), fromToken.decimals),
876
+ );
877
+ return Web3Number.fromWei(
878
+ quote.total_calculated.toString(),
879
+ toToken.decimals,
880
+ ).toNumber();
550
881
  },
551
882
  maxInput: maxAmount.toNumber(),
552
883
  maxSlippagePercent: maxSlippage,
@@ -556,48 +887,103 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
556
887
  return new Web3Number(output.optimalInput, fromToken.decimals);
557
888
  }
558
889
 
559
- async getMaxBorrowableAmountByVesuAdapter(vesuAdapter: VesuAdapter, isAPYComputation: boolean) {
560
- const lstAPY = await this.getLSTAPR(this.getLSTUnderlyingTokenInfo().address);
890
+ async getMaxBorrowableAmountByVesuAdapter(
891
+ vesuAdapter: VesuAdapter,
892
+ isAPYComputation: boolean,
893
+ ) {
894
+ const lstAPY = await this.getLSTAPR(
895
+ this.getLSTUnderlyingTokenInfo().address,
896
+ );
561
897
  const maxInterestRate = lstAPY * 0.8;
562
- const {maxDebtToHave: maxBorrowableAmount, currentDebt} = await vesuAdapter.getMaxBorrowableByInterestRate(this.config, vesuAdapter.config.debt, maxInterestRate);
898
+ const { maxDebtToHave: maxBorrowableAmount, currentDebt } =
899
+ await vesuAdapter.getMaxBorrowableByInterestRate(
900
+ this.config,
901
+ vesuAdapter.config.debt,
902
+ maxInterestRate,
903
+ );
563
904
  const debtCap = await vesuAdapter.getDebtCap(this.config);
564
905
 
565
906
  if (currentDebt.gte(debtCap)) {
566
- return {amount: Web3Number.fromWei("0", vesuAdapter.config.debt.decimals), dexSwappableAmount: Web3Number.fromWei("0", vesuAdapter.config.debt.decimals), maxBorrowableAmount: Web3Number.fromWei("0", vesuAdapter.config.debt.decimals), borrowableAsset: vesuAdapter.config.debt};
907
+ return {
908
+ amount: Web3Number.fromWei("0", vesuAdapter.config.debt.decimals),
909
+ dexSwappableAmount: Web3Number.fromWei(
910
+ "0",
911
+ vesuAdapter.config.debt.decimals,
912
+ ),
913
+ maxBorrowableAmount: Web3Number.fromWei(
914
+ "0",
915
+ vesuAdapter.config.debt.decimals,
916
+ ),
917
+ borrowableAsset: vesuAdapter.config.debt,
918
+ };
567
919
  }
568
920
 
569
921
  const availableToBorrow = debtCap.minus(currentDebt);
570
922
 
571
- const maxBorrowable = maxBorrowableAmount.minimum(availableToBorrow).multipliedBy(0.999);
923
+ const maxBorrowable = maxBorrowableAmount
924
+ .minimum(availableToBorrow)
925
+ .multipliedBy(0.999);
572
926
  // Dont compute precise max swappable for APY computation
573
- if (vesuAdapter.config.debt.address.eq(this.getLSTUnderlyingTokenInfo().address) || isAPYComputation) {
574
- return {amount: maxBorrowable, dexSwappableAmount: maxBorrowable, maxBorrowableAmount: maxBorrowable, borrowableAsset: vesuAdapter.config.debt};
927
+ if (
928
+ vesuAdapter.config.debt.address.eq(
929
+ this.getLSTUnderlyingTokenInfo().address,
930
+ ) ||
931
+ isAPYComputation
932
+ ) {
933
+ return {
934
+ amount: maxBorrowable,
935
+ dexSwappableAmount: maxBorrowable,
936
+ maxBorrowableAmount: maxBorrowable,
937
+ borrowableAsset: vesuAdapter.config.debt,
938
+ };
575
939
  }
576
940
  // Want < 0.02% slippage
577
941
  try {
578
- const maxSwappable = await this.getMaxSwappableWithMaxSlippage(vesuAdapter.config.debt, this.getLSTUnderlyingTokenInfo(), 0.0002, maxBorrowable);
579
- return {amount: maxBorrowable.minimum(maxSwappable), dexSwappableAmount: maxSwappable, maxBorrowableAmount: maxBorrowable, borrowableAsset: vesuAdapter.config.debt};
942
+ const maxSwappable = await this.getMaxSwappableWithMaxSlippage(
943
+ vesuAdapter.config.debt,
944
+ this.getLSTUnderlyingTokenInfo(),
945
+ 0.0002,
946
+ maxBorrowable,
947
+ );
948
+ return {
949
+ amount: maxBorrowable.minimum(maxSwappable),
950
+ dexSwappableAmount: maxSwappable,
951
+ maxBorrowableAmount: maxBorrowable,
952
+ borrowableAsset: vesuAdapter.config.debt,
953
+ };
580
954
  } catch (error) {
581
955
  logger.warn(`${this.getTag()}: Failed to get max swappable: ${error}`);
582
- const maxSwappable = Web3Number.fromWei("0", vesuAdapter.config.debt.decimals);
583
- return {amount: maxBorrowable.minimum(maxSwappable), dexSwappableAmount: maxSwappable, maxBorrowableAmount: maxBorrowable, borrowableAsset: vesuAdapter.config.debt};
584
- }
956
+ const maxSwappable = Web3Number.fromWei(
957
+ "0",
958
+ vesuAdapter.config.debt.decimals,
959
+ );
960
+ return {
961
+ amount: maxBorrowable.minimum(maxSwappable),
962
+ dexSwappableAmount: maxSwappable,
963
+ maxBorrowableAmount: maxBorrowable,
964
+ borrowableAsset: vesuAdapter.config.debt,
965
+ };
966
+ }
585
967
  }
586
968
 
587
969
  // todo how much to unwind to get back healthy APY zone again
588
970
  // if net APY < LST APR + 0.5%, we need to unwind to get back to LST APR + 1% atleast or 0 vesu position
589
- // For xSTRK, simply deposit in Vesu if looping is not viable
590
-
971
+ // For xSTRK, simply deposit in Vesu if looping is not viable
972
+
591
973
  /**
592
974
  * Gets LST APR for the strategy's underlying asset from Endur API
593
975
  * @returns Promise<number> The LST APR (not divided by 1e18)
594
976
  */
595
977
  async getLSTAPR(_address: ContractAddr): Promise<number> {
596
978
  try {
597
- const vesuAdapter1 = this.getVesuSameTokenAdapter(this.metadata.additionalInfo.defaultPoolId);
598
- const apr = await LSTAPRService.getLSTAPR(vesuAdapter1.config.debt.address);
979
+ const vesuAdapter1 = this.getVesuSameTokenAdapter(
980
+ this.metadata.additionalInfo.defaultPoolId,
981
+ );
982
+ const apr = await LSTAPRService.getLSTAPR(
983
+ vesuAdapter1.config.debt.address,
984
+ );
599
985
  if (!apr) {
600
- throw new Error('Failed to get LST APR');
986
+ throw new Error("Failed to get LST APR");
601
987
  }
602
988
  return apr;
603
989
  } catch (error) {
@@ -607,88 +993,170 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
607
993
  }
608
994
 
609
995
  // todo undo this
610
- async netAPY(): Promise<{ net: number; splits: { apy: number; id: string; }[]; }> {
996
+ async netAPY(): Promise<{
997
+ net: number;
998
+ splits: { apy: number; id: string }[];
999
+ }> {
611
1000
  const unusedBalance = await this.getUnusedBalance();
612
- const maxNewDeposits = await this.maxNewDeposits({ isAPYComputation: true });
613
- const lstAPY = await this.getLSTAPR(this.getLSTUnderlyingTokenInfo().address);
1001
+ const maxNewDeposits = await this.maxNewDeposits({
1002
+ isAPYComputation: true,
1003
+ });
1004
+ const lstAPY = await this.getLSTAPR(
1005
+ this.getLSTUnderlyingTokenInfo().address,
1006
+ );
614
1007
 
615
1008
  // if unused balance is > max servicable from loan, we are limited by the max borrowing we can do
616
1009
  // we also allow accepting little higher deposits (1.5x) to have room for future looping when theres more liquidity or debt cap rises
617
1010
  if (maxNewDeposits * 1.5 < unusedBalance.amount.toNumber()) {
618
1011
  // we have excess, just use real APY
619
- logger.verbose(`${this.getTag()}::netAPY: unused balance is > max servicable from loan, lstAPY: ${lstAPY}`);
1012
+ logger.verbose(
1013
+ `${this.getTag()}::netAPY: unused balance is > max servicable from loan, lstAPY: ${lstAPY}`,
1014
+ );
620
1015
  const output = await super.netAPY();
621
- output.splits.push({apy: lstAPY, id: 'lst_apy'});
1016
+ output.splits.push({ apy: lstAPY, id: "lst_apy" });
622
1017
  return output;
623
1018
  } else {
624
1019
  // we have little bit room to accept more deposits, we use theoretical max APY
625
- logger.verbose(`${this.getTag()}::netAPY: we can take more deposits, use theoretical max APY`);
1020
+ logger.verbose(
1021
+ `${this.getTag()}::netAPY: we can take more deposits, use theoretical max APY`,
1022
+ );
626
1023
  const { positions, baseAPYs, rewardAPYs } = await this.getVesuAPYs();
627
- const weights = positions.map((p, index) => p.usdValue * (index % 2 == 0 ? 1 : -1));
1024
+ const weights = positions.map(
1025
+ (p, index) => p.usdValue * (index % 2 == 0 ? 1 : -1),
1026
+ );
628
1027
  const aum = weights.reduce((acc, curr) => acc + curr, 0);
629
- const output = await this.returnNetAPY(baseAPYs, rewardAPYs, weights, new Web3Number(aum.toFixed(9), this.getLSTUnderlyingTokenInfo().decimals));
630
- output.splits.push({apy: lstAPY, id: 'lst_apy'});
1028
+ const output = await this.returnNetAPY(
1029
+ baseAPYs,
1030
+ rewardAPYs,
1031
+ weights,
1032
+ new Web3Number(
1033
+ aum.toFixed(9),
1034
+ this.getLSTUnderlyingTokenInfo().decimals,
1035
+ ),
1036
+ );
1037
+ output.splits.push({ apy: lstAPY, id: "lst_apy" });
631
1038
  return output;
632
1039
  }
633
1040
  }
634
1041
 
635
- async maxNewDeposits(params: { isAPYComputation: boolean } = { isAPYComputation: false }) {
1042
+ async maxNewDeposits(
1043
+ params: { isAPYComputation: boolean } = { isAPYComputation: false },
1044
+ ) {
636
1045
  const maxBorrowableAmounts = await this.getMaxBorrowableAmount(params);
637
1046
  let numerator = 0;
638
1047
 
639
1048
  let ltv: number | undefined = undefined;
640
1049
  for (let adapter of this.getVesuAdapters()) {
641
- const maxBorrowableAmountInfo = maxBorrowableAmounts.maxBorrowables.find(b => b.borrowableAsset.address.eq(adapter.config.debt.address));
1050
+ const maxBorrowableAmountInfo = maxBorrowableAmounts.maxBorrowables.find(
1051
+ (b) => b.borrowableAsset.address.eq(adapter.config.debt.address),
1052
+ );
642
1053
  if (!maxBorrowableAmountInfo || !maxBorrowableAmountInfo?.amount) {
643
- throw new Error(`Max borrowable amount not found for adapter: ${adapter.config.debt.symbol}`);
1054
+ throw new Error(
1055
+ `Max borrowable amount not found for adapter: ${adapter.config.debt.symbol}`,
1056
+ );
644
1057
  }
645
1058
 
646
- numerator += this.metadata.additionalInfo.targetHealthFactor * maxBorrowableAmountInfo.amount.toNumber() / maxBorrowableAmountInfo.ltv;
1059
+ numerator +=
1060
+ (this.metadata.additionalInfo.targetHealthFactor *
1061
+ maxBorrowableAmountInfo.amount.toNumber()) /
1062
+ maxBorrowableAmountInfo.ltv;
647
1063
  }
648
1064
 
649
- return numerator - maxBorrowableAmounts.netMaxBorrowableAmount.toNumber();
1065
+ return numerator - maxBorrowableAmounts.netMaxBorrowableAmount.toNumber();
650
1066
  }
651
1067
 
652
1068
  // todo revisit cases where 0th adapters is used
653
1069
  protected async getUnusedBalanceAPY() {
654
1070
  const unusedBalance = await this.getUnusedBalance();
655
- const vesuAdapter = this.getVesuSameTokenAdapter(this.metadata.additionalInfo.defaultPoolId);
1071
+ const vesuAdapter = this.getVesuSameTokenAdapter(
1072
+ this.metadata.additionalInfo.defaultPoolId,
1073
+ );
656
1074
  const underlying = vesuAdapter.config.debt;
657
1075
  const lstAPY = await this.getLSTAPR(underlying.address);
658
1076
  return {
659
- apy: lstAPY, weight: unusedBalance.usdValue
660
- }
1077
+ apy: lstAPY,
1078
+ weight: unusedBalance.usdValue,
1079
+ };
1080
+ }
1081
+
1082
+ async getLSTAvnuRate() {
1083
+ const vesuAdapter1 = this.getVesuSameTokenAdapter(
1084
+ this.metadata.additionalInfo.defaultPoolId,
1085
+ );
1086
+ const lstTokenInfo = vesuAdapter1.config.collateral;
1087
+ const underlyingTokenInfo = vesuAdapter1.config.debt;
1088
+
1089
+ const avnuModule = new AvnuWrapper();
1090
+
1091
+ const sellAmount = lstTokenInfo.priceCheckAmount
1092
+ ? new Web3Number(
1093
+ lstTokenInfo.priceCheckAmount,
1094
+ underlyingTokenInfo.decimals,
1095
+ )
1096
+ : new Web3Number(1, underlyingTokenInfo.decimals);
1097
+
1098
+ const quote = await avnuModule.getQuotes(
1099
+ underlyingTokenInfo.address.address,
1100
+ lstTokenInfo.address.address,
1101
+ sellAmount.toWei(),
1102
+ this.metadata.additionalInfo.vaultAllocator.address,
1103
+ );
1104
+
1105
+ const underlyingAmountNumber = sellAmount.toNumber();
1106
+ const lstAmountNumber = Web3Number.fromWei(
1107
+ quote.buyAmount.toString(),
1108
+ lstTokenInfo.decimals,
1109
+ ).toNumber();
1110
+
1111
+ assert(lstAmountNumber > 0, "Avnu LST amount is zero");
1112
+
1113
+ const exchangeRate = underlyingAmountNumber / lstAmountNumber;
1114
+ logger.verbose(
1115
+ `${this.getTag()}:: LST Avnu Exchange Rate: ${exchangeRate}`,
1116
+ );
1117
+ return exchangeRate;
661
1118
  }
662
1119
 
663
1120
  async getLSTExchangeRate() {
664
- const vesuAdapter1 = this.getVesuSameTokenAdapter(this.metadata.additionalInfo.defaultPoolId);
1121
+ const vesuAdapter1 = this.getVesuSameTokenAdapter(
1122
+ this.metadata.additionalInfo.defaultPoolId,
1123
+ );
665
1124
  const lstTokenInfo = vesuAdapter1.config.collateral;
666
1125
  const lstABI = new Contract({
667
1126
  abi: ERC4626Abi,
668
1127
  address: lstTokenInfo.address.address,
669
- providerOrAccount: this.config.provider
1128
+ providerOrAccount: this.config.provider,
670
1129
  });
671
1130
 
672
- const price: any = await lstABI.call('convert_to_assets', [uint256.bnToUint256((new Web3Number(1, lstTokenInfo.decimals)).toWei())]);
673
- const exchangeRate = Number(uint256.uint256ToBN(price).toString()) / Math.pow(10, lstTokenInfo.decimals);
1131
+ const price: any = await lstABI.call("convert_to_assets", [
1132
+ uint256.bnToUint256(new Web3Number(1, lstTokenInfo.decimals).toWei()),
1133
+ ]);
1134
+ const exchangeRate =
1135
+ Number(uint256.uint256ToBN(price).toString()) /
1136
+ Math.pow(10, lstTokenInfo.decimals);
674
1137
  logger.verbose(`${this.getTag()}:: LST Exchange Rate: ${exchangeRate}`);
675
1138
  return exchangeRate;
676
- }
1139
+ }
677
1140
 
678
1141
  /**
679
- *
1142
+ *
680
1143
  * @param params marginAmount is in LST, debtAmount is in underlying
681
1144
  */
682
1145
  async getModifyLeverCall(params: {
683
- marginAmount: Web3Number, // >0 during deposit
684
- debtAmount: Web3Number,
685
- lstDexPriceInUnderlying: number,
686
- isIncrease: boolean,
687
- maxEkuboPriceImpact: number,
688
- poolId: ContractAddr
1146
+ marginAmount: Web3Number; // >0 during deposit
1147
+ debtAmount: Web3Number;
1148
+ lstDexPriceInUnderlying: number;
1149
+ isIncrease: boolean;
1150
+ maxEkuboPriceImpact: number;
1151
+ poolId: ContractAddr;
689
1152
  }): Promise<Call[]> {
690
- logger.verbose(`${this.getTag()}::getModifyLeverCall marginAmount: ${params.marginAmount}, debtAmount: ${params.debtAmount}, lstDexPriceInUnderlying: ${params.lstDexPriceInUnderlying}, isIncrease: ${params.isIncrease}`);
691
- assert(!params.marginAmount.isZero() || !params.debtAmount.isZero(), 'Deposit/debt must be non-0');
1153
+ logger.verbose(
1154
+ `${this.getTag()}::getModifyLeverCall marginAmount: ${params.marginAmount}, debtAmount: ${params.debtAmount}, lstDexPriceInUnderlying: ${params.lstDexPriceInUnderlying}, isIncrease: ${params.isIncrease}`,
1155
+ );
1156
+ assert(
1157
+ !params.marginAmount.isZero() || !params.debtAmount.isZero(),
1158
+ "Deposit/debt must be non-0",
1159
+ );
692
1160
 
693
1161
  const vesuAdapter1 = this.getVesuSameTokenAdapter(params.poolId);
694
1162
  const lstTokenInfo = this.asset();
@@ -696,9 +1164,11 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
696
1164
 
697
1165
  // todo make it more general
698
1166
  // 500k STRK (~75k$) or 0.5 BTC (~60k$)
699
- const maxAmounts = lstTokenInfo.symbol == 'xSTRK' ? 500000 : 0.5;
1167
+ const maxAmounts = lstTokenInfo.symbol == "xSTRK" ? 500000 : 0.5;
700
1168
  if (params.marginAmount.greaterThan(maxAmounts)) {
701
- throw new Error(`Margin amount is greater than max amount: ${params.marginAmount.toNumber()} > ${maxAmounts}`);
1169
+ throw new Error(
1170
+ `Margin amount is greater than max amount: ${params.marginAmount.toNumber()} > ${maxAmounts}`,
1171
+ );
702
1172
  }
703
1173
 
704
1174
  const proofsIDs: string[] = [];
@@ -706,12 +1176,15 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
706
1176
 
707
1177
  // approve token
708
1178
  if (params.marginAmount.greaterThan(0)) {
709
- const STEP1_ID = getVesuGenericLegId(params.poolId.toString(), LST_MULTIPLIER_MANAGE_IDS.MULTIPLE_APPROVE);
1179
+ const STEP1_ID = getVesuGenericLegId(
1180
+ params.poolId.toString(),
1181
+ LST_MULTIPLIER_MANAGE_IDS.MULTIPLE_APPROVE,
1182
+ );
710
1183
  const manage1Info = this.getProofs<ApproveCallParams>(STEP1_ID);
711
1184
  const depositAmount = params.marginAmount;
712
1185
  const manageCall1 = manage1Info.callConstructor({
713
- amount: depositAmount
714
- })
1186
+ amount: depositAmount,
1187
+ });
715
1188
  proofsIDs.push(STEP1_ID);
716
1189
  manageCalls.push(manageCall1);
717
1190
  }
@@ -728,75 +1201,119 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
728
1201
  const fromToken = params.isIncrease ? lstUnderlyingTokenInfo : lstTokenInfo;
729
1202
  const toToken = params.isIncrease ? lstTokenInfo : lstUnderlyingTokenInfo;
730
1203
  const leverSwapQuote = await ekuboQuoter.getQuote(
731
- fromToken.address.address,
732
- toToken.address.address,
733
- params.debtAmount // negative for exact amount out
1204
+ fromToken.address.address,
1205
+ toToken.address.address,
1206
+ params.debtAmount, // negative for exact amount out
1207
+ );
1208
+ logger.verbose(
1209
+ `${this.getTag()}::getModifyLeverCall leverSwapQuote: ${JSON.stringify(leverSwapQuote)}`,
1210
+ );
1211
+ // Ekubo's price impact can randomly show high numbers sometimes.
1212
+ assert(
1213
+ leverSwapQuote.price_impact <= params.maxEkuboPriceImpact,
1214
+ "getIncreaseLeverCall: Price impact is too high [Debt swap]",
1215
+ );
1216
+ const leverSwap = ekuboQuoter.getVesuMultiplyQuote(
1217
+ leverSwapQuote,
1218
+ fromToken,
1219
+ toToken,
1220
+ );
1221
+ logger.verbose(
1222
+ `${this.getTag()}::getModifyLeverCall leverSwap: ${JSON.stringify(leverSwap)}`,
734
1223
  );
735
- logger.verbose(`${this.getTag()}::getModifyLeverCall leverSwapQuote: ${JSON.stringify(leverSwapQuote)}`);
736
- // Ekubo's price impact can randomly show high numbers sometimes.
737
- assert(leverSwapQuote.price_impact <= params.maxEkuboPriceImpact, 'getIncreaseLeverCall: Price impact is too high [Debt swap]');
738
- const leverSwap = ekuboQuoter.getVesuMultiplyQuote(leverSwapQuote, fromToken, toToken);
739
- logger.verbose(`${this.getTag()}::getModifyLeverCall leverSwap: ${JSON.stringify(leverSwap)}`);
740
1224
 
741
1225
  // todo double check this logic
742
1226
  // is Deposit
743
- let minLSTReceived = params.debtAmount.dividedBy(lstDexPriceInUnderlying).multipliedBy(1 - MAX_SLIPPAGE); // used for increase
744
- const minLSTReceivedAsPerTruePrice = params.debtAmount.dividedBy(lstTrueExchangeRate); // execution output to be <= True LST price
1227
+ let minLSTReceived = params.debtAmount
1228
+ .dividedBy(lstDexPriceInUnderlying)
1229
+ .multipliedBy(1 - MAX_SLIPPAGE); // used for increase
1230
+ const minLSTReceivedAsPerTruePrice =
1231
+ params.debtAmount.dividedBy(lstTrueExchangeRate); // execution output to be <= True LST price
745
1232
  // if (minLSTReceived < minLSTReceivedAsPerTruePrice) {
746
1233
  // minLSTReceived = minLSTReceivedAsPerTruePrice; // the execution shouldn't be bad than True price logi
747
1234
  // }
748
1235
  minLSTReceived = minLSTReceivedAsPerTruePrice; // in any case, we are ok with this, bcz the BTC LST spread shouldnt be high
749
- logger.verbose(`${this.getTag()}::getModifyLeverCall minLSTReceivedAsPerTruePrice: ${minLSTReceivedAsPerTruePrice}, minLSTReceived: ${minLSTReceived}`);
1236
+ logger.verbose(
1237
+ `${this.getTag()}::getModifyLeverCall minLSTReceivedAsPerTruePrice: ${minLSTReceivedAsPerTruePrice}, minLSTReceived: ${minLSTReceived}`,
1238
+ );
750
1239
 
751
1240
  // is withdraw
752
- let maxUsedCollateral = params.debtAmount.abs().dividedBy(lstDexPriceInUnderlying).multipliedBy(1 + MAX_SLIPPAGE); // +ve for exact amount out, used for decrease
753
- const maxUsedCollateralInLST = params.debtAmount.abs().dividedBy(lstTrueExchangeRate).multipliedBy(1.005); // 0.5% slippage, worst case based on true price
754
- logger.verbose(`${this.getTag()}::getModifyLeverCall maxUsedCollateralInLST: ${maxUsedCollateralInLST}, maxUsedCollateral: ${maxUsedCollateral}`);
1241
+ let maxUsedCollateral = params.debtAmount
1242
+ .abs()
1243
+ .dividedBy(lstDexPriceInUnderlying)
1244
+ .multipliedBy(1 + MAX_SLIPPAGE); // +ve for exact amount out, used for decrease
1245
+ const maxUsedCollateralInLST = params.debtAmount
1246
+ .abs()
1247
+ .dividedBy(lstTrueExchangeRate)
1248
+ .multipliedBy(1.005); // 0.5% slippage, worst case based on true price
1249
+ logger.verbose(
1250
+ `${this.getTag()}::getModifyLeverCall maxUsedCollateralInLST: ${maxUsedCollateralInLST}, maxUsedCollateral: ${maxUsedCollateral}`,
1251
+ );
755
1252
  // if (maxUsedCollateralInLST > maxUsedCollateral) {
756
1253
  // maxUsedCollateral = maxUsedCollateralInLST;
757
1254
  // }
758
1255
  maxUsedCollateral = maxUsedCollateralInLST; // in any case, we are ok with this, bcz the BTC LST spread shouldnt be high
759
1256
 
760
- const STEP2_ID = getVesuGenericLegId(params.poolId.toString(), LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_ON);
761
- const manage2Info = this.getProofs<VesuModifyDelegationCallParams>(STEP2_ID);
1257
+ const STEP2_ID = getVesuGenericLegId(
1258
+ params.poolId.toString(),
1259
+ LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_ON,
1260
+ );
1261
+ const manage2Info =
1262
+ this.getProofs<VesuModifyDelegationCallParams>(STEP2_ID);
762
1263
  const manageCall2 = manage2Info.callConstructor({
763
- delegation: true
1264
+ delegation: true,
764
1265
  });
765
1266
 
766
1267
  // deposit and borrow or repay and withdraw
767
- const STEP3_ID = getVesuLegId(LST_MULTIPLIER_MANAGE_IDS.MULTIPLY_VESU, vesuAdapter1.config.debt.symbol, vesuAdapter1.config.poolId.toString());
1268
+ const STEP3_ID = getVesuLegId(
1269
+ LST_MULTIPLIER_MANAGE_IDS.MULTIPLY_VESU,
1270
+ vesuAdapter1.config.debt.symbol,
1271
+ vesuAdapter1.config.poolId.toString(),
1272
+ );
768
1273
  const manage3Info = this.getProofs<VesuMultiplyCallParams>(STEP3_ID);
769
- const multiplyParams: VesuMultiplyCallParams = params.isIncrease ? {
770
- isIncrease: true,
771
- increaseParams: {
772
- add_margin: params.marginAmount,
773
- margin_swap: [],
774
- margin_swap_limit_amount: Web3Number.fromWei(0, this.asset().decimals),
775
- lever_swap: leverSwap,
776
- lever_swap_limit_amount: minLSTReceived
777
- }
778
- } : {
779
- isIncrease: false,
780
- decreaseParams: {
781
- sub_margin: params.marginAmount.multipliedBy(-1),
782
- lever_swap: leverSwap,
783
- lever_swap_limit_amount: maxUsedCollateral,
784
- // only required for close position
785
- lever_swap_weights: [],
786
- // no need to swap collateral to anything, and any residuals return our contract anyways.
787
- withdraw_swap: [],
788
- withdraw_swap_limit_amount: Web3Number.fromWei(0, this.asset().decimals),
789
- withdraw_swap_weights: [],
790
- close_position: false
791
- }
792
- }
1274
+ const multiplyParams: VesuMultiplyCallParams = params.isIncrease
1275
+ ? {
1276
+ isIncrease: true,
1277
+ increaseParams: {
1278
+ add_margin: params.marginAmount,
1279
+ margin_swap: [],
1280
+ margin_swap_limit_amount: Web3Number.fromWei(
1281
+ 0,
1282
+ this.asset().decimals,
1283
+ ),
1284
+ lever_swap: leverSwap,
1285
+ lever_swap_limit_amount: minLSTReceived,
1286
+ },
1287
+ }
1288
+ : {
1289
+ isIncrease: false,
1290
+ decreaseParams: {
1291
+ sub_margin: params.marginAmount.multipliedBy(-1),
1292
+ lever_swap: leverSwap,
1293
+ lever_swap_limit_amount: maxUsedCollateral,
1294
+ // only required for close position
1295
+ lever_swap_weights: [],
1296
+ // no need to swap collateral to anything, and any residuals return our contract anyways.
1297
+ withdraw_swap: [],
1298
+ withdraw_swap_limit_amount: Web3Number.fromWei(
1299
+ 0,
1300
+ this.asset().decimals,
1301
+ ),
1302
+ withdraw_swap_weights: [],
1303
+ close_position: false,
1304
+ },
1305
+ };
793
1306
  const manageCall3 = manage3Info.callConstructor(multiplyParams);
794
1307
 
795
1308
  // switch delegation off
796
- const STEP4_ID = getVesuGenericLegId(params.poolId.toString(), LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_OFF);
797
- const manage4Info = this.getProofs<VesuModifyDelegationCallParams>(STEP4_ID);
1309
+ const STEP4_ID = getVesuGenericLegId(
1310
+ params.poolId.toString(),
1311
+ LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_OFF,
1312
+ );
1313
+ const manage4Info =
1314
+ this.getProofs<VesuModifyDelegationCallParams>(STEP4_ID);
798
1315
  const manageCall4 = manage4Info.callConstructor({
799
- delegation: false
1316
+ delegation: false,
800
1317
  });
801
1318
 
802
1319
  proofsIDs.push(STEP2_ID, STEP3_ID, STEP4_ID);
@@ -808,12 +1325,11 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
808
1325
 
809
1326
  export default function VaultDescription(
810
1327
  lstSymbol: string,
811
- underlyingSymbol: string
1328
+ underlyingSymbol: string,
812
1329
  ) {
813
1330
  const containerStyle = {
814
1331
  maxWidth: "800px",
815
1332
  margin: "0 auto",
816
- backgroundColor: "#111",
817
1333
  color: "#eee",
818
1334
  fontFamily: "Arial, sans-serif",
819
1335
  borderRadius: "12px",
@@ -821,26 +1337,64 @@ export default function VaultDescription(
821
1337
 
822
1338
  return (
823
1339
  <div style={containerStyle}>
824
- <h1 style={{ fontSize: "18px", marginBottom: "10px" }}>Liquidation risk managed leverged {lstSymbol} Vault</h1>
1340
+ <h1 style={{ fontSize: "18px", marginBottom: "10px" }}>
1341
+ Liquidation risk managed leverged {lstSymbol} Vault
1342
+ </h1>
825
1343
  <p style={{ fontSize: "14px", lineHeight: "1.5", marginBottom: "16px" }}>
826
- This Levered Endur {lstSymbol} vault is a tokenized leveraged Vault, auto-compounding strategy that takes upto 5x leverage on {lstSymbol} by borrow {underlyingSymbol}. Borrowed amount
827
- is swapped to {lstSymbol} to create leverage. Depositors receive vault shares that
828
- represent a proportional claim on the underlying assets and accrued yield.
1344
+ This Levered Endur {lstSymbol} vault is a tokenized leveraged Vault,
1345
+ auto-compounding strategy that takes upto 5x leverage on {lstSymbol} by
1346
+ borrow {underlyingSymbol}. Borrowed amount is swapped to {lstSymbol} to
1347
+ create leverage. Depositors receive vault shares that represent a
1348
+ proportional claim on the underlying assets and accrued yield.
829
1349
  </p>
830
1350
 
831
1351
  <p style={{ fontSize: "14px", lineHeight: "1.5", marginBottom: "16px" }}>
832
- This vault uses Vesu for lending and borrowing. The oracle used by this pool is a {highlightTextWithLinks("conversion rate oracle", [{highlight: "conversion rate oracle", link: "https://docs.pragma.build/starknet/development#conversion-rate"}])}
833
- {" "}which is resilient to liquidity issues and price volatility, hence reducing the risk of liquidation. However, overtime, if left un-monitored, debt can increase enough to trigger a liquidation. But no worries, our continuous monitoring systems look for situations with reduced health factor and balance collateral/debt to bring it back to safe levels. With Troves, you can have a peaceful sleep.
1352
+ This vault uses Vesu for lending and borrowing. The oracle used by this
1353
+ pool is a{" "}
1354
+ {highlightTextWithLinks("conversion rate oracle", [
1355
+ {
1356
+ highlight: "conversion rate oracle",
1357
+ link: "https://docs.pragma.build/starknet/development#conversion-rate",
1358
+ },
1359
+ ])}{" "}
1360
+ which is resilient to liquidity issues and price volatility, hence
1361
+ reducing the risk of liquidation. However, overtime, if left
1362
+ un-monitored, debt can increase enough to trigger a liquidation. But no
1363
+ worries, our continuous monitoring systems look for situations with
1364
+ reduced health factor and balance collateral/debt to bring it back to
1365
+ safe levels. With Troves, you can have a peaceful sleep.
834
1366
  </p>
835
1367
 
836
- <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
1368
+ <div
1369
+ style={{
1370
+ backgroundColor: "#222",
1371
+ padding: "10px",
1372
+ borderRadius: "8px",
1373
+ marginBottom: "20px",
1374
+ border: "1px solid #444",
1375
+ }}
1376
+ >
837
1377
  <p style={{ fontSize: "13px", color: "#ccc" }}>
838
- <strong>Withdrawals:</strong> Requests can take up to <strong>1-2 hours</strong> to process as the vault unwinds and settles routing.
1378
+ <strong>Withdrawals:</strong> Requests can take up to{" "}
1379
+ <strong>1-2 hours</strong> to process as the vault unwinds and settles
1380
+ routing.
839
1381
  </p>
840
1382
  </div>
841
- <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
1383
+ <div
1384
+ style={{
1385
+ backgroundColor: "#222",
1386
+ padding: "10px",
1387
+ borderRadius: "8px",
1388
+ marginBottom: "20px",
1389
+ border: "1px solid #444",
1390
+ }}
1391
+ >
842
1392
  <p style={{ fontSize: "13px", color: "#ccc" }}>
843
- <strong>Debt limits:</strong> Pools on Vesu have debt caps that are gradually increased over time. Until caps are raised, deposited LSTs remain in the vault, generating a shared net return for all depositors. There is no additional fee taken by Troves on LST APY, its only on added gain.
1393
+ <strong>Debt limits:</strong> Pools on Vesu have debt caps that are
1394
+ gradually increased over time. Until caps are raised, deposited LSTs
1395
+ remain in the vault, generating a shared net return for all
1396
+ depositors. There is no additional fee taken by Troves on LST APY, its
1397
+ only on added gain.
844
1398
  </p>
845
1399
  </div>
846
1400
  {/* <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
@@ -852,24 +1406,25 @@ export default function VaultDescription(
852
1406
  );
853
1407
  }
854
1408
 
855
-
856
1409
  function getDescription(tokenSymbol: string, underlyingSymbol: string) {
857
1410
  return VaultDescription(tokenSymbol, underlyingSymbol);
858
1411
  }
859
1412
 
860
-
861
- enum LST_MULTIPLIER_MANAGE_IDS {
862
- MULTIPLE_APPROVE = 'multiple_approve',
863
- MULTIPLY_VESU = 'multiply_vesu',
864
- SWITCH_DELEGATION_ON = 'switch_delegation_on',
865
- SWITCH_DELEGATION_OFF = 'switch_delegation_off',
866
- AVNU_MULTIPLY_APPROVE_DEPOSIT = 'avnu_mul_approve_dep',
867
- AVNU_MULTIPLY_SWAP_DEPOSIT = 'avnu_mul_swap_dep',
868
- AVNU_MULTIPLY_APPROVE_WITHDRAW = 'avnu_mul_approve_withdr',
869
- AVNU_MULTIPLY_SWAP_WITHDRAW = 'avnu_mul_swap_withdr',
1413
+ enum LST_MULTIPLIER_MANAGE_IDS {
1414
+ MULTIPLE_APPROVE = "multiple_approve",
1415
+ MULTIPLY_VESU = "multiply_vesu",
1416
+ SWITCH_DELEGATION_ON = "switch_delegation_on",
1417
+ SWITCH_DELEGATION_OFF = "switch_delegation_off",
1418
+ AVNU_MULTIPLY_APPROVE_DEPOSIT = "avnu_mul_approve_dep",
1419
+ AVNU_MULTIPLY_SWAP_DEPOSIT = "avnu_mul_swap_dep",
1420
+ AVNU_MULTIPLY_APPROVE_WITHDRAW = "avnu_mul_approve_withdr",
1421
+ AVNU_MULTIPLY_SWAP_WITHDRAW = "avnu_mul_swap_withdr",
870
1422
  }
871
1423
 
872
- function getAvnuManageIDs(baseID: LST_MULTIPLIER_MANAGE_IDS, debtTokenSymbol: string) {
1424
+ function getAvnuManageIDs(
1425
+ baseID: LST_MULTIPLIER_MANAGE_IDS,
1426
+ debtTokenSymbol: string,
1427
+ ) {
873
1428
  return `${baseID}_${debtTokenSymbol.toLowerCase()}`;
874
1429
  }
875
1430
 
@@ -881,6 +1436,7 @@ function getVesuLegId(baseID: string, debtTokenSymbol: string, poolId: string) {
881
1436
  return `${baseID}_${debtTokenSymbol.toLowerCase()}_${poolId.slice(-4).toLowerCase()}`;
882
1437
  }
883
1438
 
1439
+
884
1440
  function addVesuLeaves(
885
1441
  poolId: ContractAddr,
886
1442
  lstSymbol: string,
@@ -888,42 +1444,100 @@ function addVesuLeaves(
888
1444
  vaultSettings: HyperLSTStrategySettings,
889
1445
  commonAdapter: CommonAdapter,
890
1446
  ) {
891
- const lstToken = Global.getDefaultTokens().find(token => token.symbol === lstSymbol)!;
892
- const underlyingToken = Global.getDefaultTokens().find(token => token.symbol === underlyingSymbol)!;
1447
+ const lstToken = Global.getDefaultTokens().find(
1448
+ (token) => token.symbol === lstSymbol,
1449
+ )!;
1450
+ const underlyingToken = Global.getDefaultTokens().find(
1451
+ (token) => token.symbol === underlyingSymbol,
1452
+ )!;
893
1453
 
894
1454
  const vesuAdapterLST = new VesuAdapter({
895
1455
  poolId: poolId,
896
1456
  collateral: lstToken,
897
1457
  debt: underlyingToken,
898
1458
  vaultAllocator: vaultSettings.vaultAllocator,
899
- id: getVesuLegId(UNIVERSAL_MANAGE_IDS.VESU_LEG1, underlyingToken.symbol, poolId.toString())
1459
+ id: getVesuLegId(
1460
+ UNIVERSAL_MANAGE_IDS.VESU_LEG1,
1461
+ underlyingToken.symbol,
1462
+ poolId.toString(),
1463
+ ),
900
1464
  });
901
1465
 
902
1466
  // Useful for returning adapter class objects that can compute
903
1467
  // certain things for us (e.g. positions, hfs)
904
- vaultSettings.adapters.push(...[{
905
- id: getVesuLegId(UNIVERSAL_MANAGE_IDS.VESU_LEG1, underlyingToken.symbol, poolId.toString()),
906
- adapter: vesuAdapterLST
907
- }]);
1468
+ vaultSettings.adapters.push(
1469
+ ...[
1470
+ {
1471
+ id: getVesuLegId(
1472
+ UNIVERSAL_MANAGE_IDS.VESU_LEG1,
1473
+ underlyingToken.symbol,
1474
+ poolId.toString(),
1475
+ ),
1476
+ adapter: vesuAdapterLST,
1477
+ },
1478
+ ],
1479
+ );
908
1480
 
909
1481
  // avnu multiply
910
- const { isV2, addr: _ } = getVesuSingletonAddress(poolId);
1482
+ const { isV2, addr: poolAddr } = getVesuSingletonAddress(poolId);
911
1483
 
912
1484
  // vesu multiply looping
913
- const VESU_MULTIPLY = isV2 ? vesuAdapterLST.VESU_MULTIPLY : vesuAdapterLST.VESU_MULTIPLY_V1;
914
-
915
- const leafIdApprove = getVesuGenericLegId(poolId.toString(), LST_MULTIPLIER_MANAGE_IDS.MULTIPLE_APPROVE);
916
- vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(lstToken.address, VESU_MULTIPLY, leafIdApprove).bind(commonAdapter));
917
-
918
- const leafIdDelegationOn = getVesuGenericLegId(poolId.toString(), LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_ON);
919
- vaultSettings.leafAdapters.push(vesuAdapterLST.getVesuModifyDelegationAdapter(leafIdDelegationOn).bind(vesuAdapterLST));
920
-
921
- const leafIdDelegationOff = getVesuGenericLegId(poolId.toString(), LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_OFF);
922
- vaultSettings.leafAdapters.push(vesuAdapterLST.getVesuModifyDelegationAdapter(leafIdDelegationOff).bind(vesuAdapterLST));
923
-
924
- const multiplID = getVesuLegId(LST_MULTIPLIER_MANAGE_IDS.MULTIPLY_VESU, underlyingToken.symbol, poolId.toString());
925
- vaultSettings.leafAdapters.push(vesuAdapterLST.getMultiplyAdapter(multiplID).bind(vesuAdapterLST));
926
-
1485
+ const VESU_MULTIPLY = isV2
1486
+ ? vesuAdapterLST.VESU_MULTIPLY
1487
+ : vesuAdapterLST.VESU_MULTIPLY_V1;
1488
+
1489
+ const leafIdApprove = getVesuGenericLegId(
1490
+ poolId.toString(),
1491
+ LST_MULTIPLIER_MANAGE_IDS.MULTIPLE_APPROVE,
1492
+ );
1493
+ vaultSettings.leafAdapters.push(
1494
+ commonAdapter
1495
+ .getApproveAdapter(lstToken.address, VESU_MULTIPLY, leafIdApprove)
1496
+ .bind(commonAdapter),
1497
+ );
1498
+
1499
+ const leafIdDelegationOn = getVesuGenericLegId(
1500
+ poolId.toString(),
1501
+ LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_ON,
1502
+ );
1503
+ vaultSettings.leafAdapters.push(
1504
+ vesuAdapterLST
1505
+ .getVesuModifyDelegationAdapter(leafIdDelegationOn, VESU_MULTIPLY)
1506
+ .bind(vesuAdapterLST),
1507
+ );
1508
+
1509
+ const leafIdDelegationOff = getVesuGenericLegId(
1510
+ poolId.toString(),
1511
+ LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_OFF,
1512
+ );
1513
+ vaultSettings.leafAdapters.push(
1514
+ vesuAdapterLST
1515
+ .getVesuModifyDelegationAdapter(leafIdDelegationOff, VESU_MULTIPLY)
1516
+ .bind(vesuAdapterLST),
1517
+ );
1518
+
1519
+ const multiplID = getVesuLegId(
1520
+ LST_MULTIPLIER_MANAGE_IDS.MULTIPLY_VESU,
1521
+ underlyingToken.symbol,
1522
+ poolId.toString(),
1523
+ );
1524
+ vaultSettings.leafAdapters.push(
1525
+ vesuAdapterLST.getMultiplyAdapter(multiplID).bind(vesuAdapterLST),
1526
+ );
1527
+
1528
+ // direct modify position stuff
1529
+ vaultSettings.leafAdapters.push(
1530
+ commonAdapter
1531
+ .getApproveAdapter(
1532
+ lstToken.address,
1533
+ poolAddr,
1534
+ UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1,
1535
+ )
1536
+ .bind(commonAdapter),
1537
+ );
1538
+ vaultSettings.leafAdapters.push(
1539
+ vesuAdapterLST.getModifyPosition.bind(vesuAdapterLST),
1540
+ );
927
1541
  return vesuAdapterLST;
928
1542
  }
929
1543
 
@@ -932,7 +1546,6 @@ function getLooperSettings(
932
1546
  underlyingSymbol: string,
933
1547
  vaultSettings: HyperLSTStrategySettings,
934
1548
  defaultPoolId: ContractAddr,
935
- altSupportedPoolIds: ContractAddr[] = []
936
1549
  ) {
937
1550
  vaultSettings.leafAdapters = [];
938
1551
  const pool1 = vaultSettings.defaultPoolId;
@@ -940,99 +1553,179 @@ function getLooperSettings(
940
1553
  throw new Error(`Dont include default pool id in supported pool ids`);
941
1554
  }
942
1555
 
943
- const lstToken = Global.getDefaultTokens().find(token => token.symbol === lstSymbol)!;
944
- const underlyingToken = Global.getDefaultTokens().find(token => token.symbol === underlyingSymbol)!;
1556
+ const lstToken = Global.getDefaultTokens().find(
1557
+ (token) => token.symbol === lstSymbol,
1558
+ )!;
1559
+ const underlyingToken = Global.getDefaultTokens().find(
1560
+ (token) => token.symbol === underlyingSymbol,
1561
+ )!;
945
1562
 
946
1563
  const commonAdapter = new CommonAdapter({
947
1564
  manager: vaultSettings.manager,
948
1565
  asset: lstToken.address,
949
- id: '',
1566
+ id: "",
950
1567
  vaultAddress: vaultSettings.vaultAddress,
951
1568
  vaultAllocator: vaultSettings.vaultAllocator,
952
1569
  });
953
1570
 
954
1571
  // add common adapter to adapters
955
- vaultSettings.adapters.push(...[{
956
- id: UNIVERSAL_ADAPTERS.COMMON,
957
- adapter: commonAdapter
958
- }]);
1572
+ vaultSettings.adapters.push(
1573
+ ...[
1574
+ {
1575
+ id: UNIVERSAL_ADAPTERS.COMMON,
1576
+ adapter: commonAdapter,
1577
+ },
1578
+ ],
1579
+ );
959
1580
 
960
- // add vesu leaves for default pool id
961
- const vesuAdapterLST = addVesuLeaves(defaultPoolId, lstSymbol, underlyingSymbol, vaultSettings, commonAdapter);
1581
+ // add vesu leaves for all supported pool ids
1582
+ vaultSettings.borrowable_assets.map((borrowableAsset) =>
1583
+ addVesuLeaves(
1584
+ borrowableAsset.poolId,
1585
+ lstSymbol,
1586
+ underlyingSymbol,
1587
+ vaultSettings,
1588
+ commonAdapter,
1589
+ ),
1590
+ );
962
1591
 
963
- // add vesu leaves for alt supported pool ids
964
- altSupportedPoolIds.map(poolId => addVesuLeaves(poolId, lstSymbol, underlyingSymbol, vaultSettings, commonAdapter));
965
-
966
1592
  // approve lst once to avnu
967
- vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(lstToken.address, AVNU_EXCHANGE, LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_APPROVE_WITHDRAW).bind(commonAdapter));
968
- for (let borrowableAsset of vaultSettings.borrowable_assets) {
1593
+ vaultSettings.leafAdapters.push(
1594
+ commonAdapter
1595
+ .getApproveAdapter(
1596
+ lstToken.address,
1597
+ AVNU_EXCHANGE,
1598
+ LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_APPROVE_WITHDRAW,
1599
+ )
1600
+ .bind(commonAdapter),
1601
+ );
1602
+
1603
+ const uniqueBorrowableAssets = [
1604
+ ...new Set(
1605
+ vaultSettings.borrowable_assets.map(
1606
+ (borrowableAsset) => borrowableAsset.token.symbol,
1607
+ ),
1608
+ ),
1609
+ ];
1610
+ for (let borrowableAssetSymbol of uniqueBorrowableAssets) {
1611
+ const borrowableAsset = Global.getDefaultTokens().find(
1612
+ (token) => token.symbol === borrowableAssetSymbol,
1613
+ )!;
969
1614
  // in-efficient avnu swap looping (but good with endur integration)
970
1615
  const debtAsset = borrowableAsset;
971
- const approve_debt_token_id = getAvnuManageIDs(LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_APPROVE_DEPOSIT, debtAsset.symbol);
972
- const swap_debt_token_id = getAvnuManageIDs(LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_SWAP_DEPOSIT, debtAsset.symbol);
973
- const swap_lst_token_id = getAvnuManageIDs(LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_SWAP_WITHDRAW, debtAsset.symbol);
974
- vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(debtAsset.address, AVNU_EXCHANGE, approve_debt_token_id).bind(commonAdapter));
975
- vaultSettings.leafAdapters.push(commonAdapter.getAvnuAdapter(debtAsset.address, lstToken.address, swap_debt_token_id, false).bind(commonAdapter));
976
- vaultSettings.leafAdapters.push(commonAdapter.getAvnuAdapter(lstToken.address, debtAsset.address, swap_lst_token_id, false).bind(commonAdapter));
977
-
978
- // approve LST to add collateral
979
- const vesuAdapter = new VesuAdapter({
980
- poolId: pool1,
981
- collateral: lstToken,
982
- debt: debtAsset,
983
- vaultAllocator: vaultSettings.vaultAllocator,
984
- id: getVesuLegId(UNIVERSAL_MANAGE_IDS.VESU_LEG1, debtAsset.symbol, pool1.toString())
985
- });
986
- const { isV2, addr:poolAddr } = getVesuSingletonAddress(pool1);
987
- vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(lstToken.address, poolAddr, UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1).bind(commonAdapter));
988
- vaultSettings.leafAdapters.push(vesuAdapter.getModifyPosition.bind(vesuAdapter));
989
-
990
- // Vesu multiply
991
- if (borrowableAsset.address.eq(underlyingToken.address)) {
992
- continue; // already added in addVesuLeaves
993
- }
1616
+ const approve_debt_token_id = getAvnuManageIDs(
1617
+ LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_APPROVE_DEPOSIT,
1618
+ debtAsset.symbol,
1619
+ );
1620
+ const swap_debt_token_id = getAvnuManageIDs(
1621
+ LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_SWAP_DEPOSIT,
1622
+ debtAsset.symbol,
1623
+ );
1624
+ const swap_lst_token_id = getAvnuManageIDs(
1625
+ LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_SWAP_WITHDRAW,
1626
+ debtAsset.symbol,
1627
+ );
1628
+ vaultSettings.leafAdapters.push(
1629
+ commonAdapter
1630
+ .getApproveAdapter(
1631
+ debtAsset.address,
1632
+ AVNU_EXCHANGE,
1633
+ approve_debt_token_id,
1634
+ )
1635
+ .bind(commonAdapter),
1636
+ );
1637
+ vaultSettings.leafAdapters.push(
1638
+ commonAdapter
1639
+ .getAvnuAdapter(
1640
+ debtAsset.address,
1641
+ lstToken.address,
1642
+ swap_debt_token_id,
1643
+ false,
1644
+ )
1645
+ .bind(commonAdapter),
1646
+ );
1647
+ vaultSettings.leafAdapters.push(
1648
+ commonAdapter
1649
+ .getAvnuAdapter(
1650
+ lstToken.address,
1651
+ debtAsset.address,
1652
+ swap_lst_token_id,
1653
+ false,
1654
+ )
1655
+ .bind(commonAdapter),
1656
+ );
994
1657
  }
995
1658
 
996
-
997
1659
  // to bridge liquidity back to vault (used by bring_liquidity)
998
- vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(lstToken.address, vaultSettings.vaultAddress, UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY).bind(commonAdapter));
999
- vaultSettings.leafAdapters.push(commonAdapter.getBringLiquidityAdapter(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY).bind(commonAdapter));
1000
-
1001
- // claim rewards
1002
- vaultSettings.leafAdapters.push(vesuAdapterLST.getDefispringRewardsAdapter(UNIVERSAL_MANAGE_IDS.DEFISPRING_REWARDS).bind(vesuAdapterLST));
1660
+ vaultSettings.leafAdapters.push(
1661
+ commonAdapter
1662
+ .getApproveAdapter(
1663
+ lstToken.address,
1664
+ vaultSettings.vaultAddress,
1665
+ UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY,
1666
+ )
1667
+ .bind(commonAdapter),
1668
+ );
1669
+ vaultSettings.leafAdapters.push(
1670
+ commonAdapter
1671
+ .getBringLiquidityAdapter(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY)
1672
+ .bind(commonAdapter),
1673
+ );
1674
+
1675
+ // claim rewards (defi spring ended)
1676
+ // vaultSettings.leafAdapters.push(vesuAdapterLST.getDefispringRewardsAdapter(UNIVERSAL_MANAGE_IDS.DEFISPRING_REWARDS).bind(vesuAdapterLST));
1003
1677
 
1004
1678
  // avnu swap for claims rewards
1005
- const STRKToken = Global.getDefaultTokens().find(token => token.symbol === 'STRK')!;
1006
- vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(STRKToken.address, AVNU_EXCHANGE, UNIVERSAL_MANAGE_IDS.APPROVE_SWAP_TOKEN1).bind(commonAdapter));
1007
- vaultSettings.leafAdapters.push(commonAdapter.getAvnuAdapter(STRKToken.address, lstToken.address, UNIVERSAL_MANAGE_IDS.AVNU_SWAP_REWARDS, false).bind(commonAdapter));
1679
+ const STRKToken = Global.getDefaultTokens().find(
1680
+ (token) => token.symbol === "STRK",
1681
+ )!;
1682
+ vaultSettings.leafAdapters.push(
1683
+ commonAdapter
1684
+ .getApproveAdapter(
1685
+ STRKToken.address,
1686
+ AVNU_EXCHANGE,
1687
+ UNIVERSAL_MANAGE_IDS.APPROVE_SWAP_TOKEN1,
1688
+ )
1689
+ .bind(commonAdapter),
1690
+ );
1691
+ vaultSettings.leafAdapters.push(
1692
+ commonAdapter
1693
+ .getAvnuAdapter(
1694
+ STRKToken.address,
1695
+ lstToken.address,
1696
+ UNIVERSAL_MANAGE_IDS.AVNU_SWAP_REWARDS,
1697
+ false,
1698
+ )
1699
+ .bind(commonAdapter),
1700
+ );
1008
1701
  return vaultSettings;
1009
1702
  }
1010
1703
 
1011
- const AUDIT_URL = 'https://docs.troves.fi/p/security#starknet-vault-kit'
1704
+ const AUDIT_URL = "https://docs.troves.fi/p/security#starknet-vault-kit";
1012
1705
 
1013
1706
  function getFAQs(lstSymbol: string, underlyingSymbol: string): FAQ[] {
1014
1707
  return [
1015
1708
  {
1016
1709
  question: `What is the Hyper ${lstSymbol} Vault?`,
1017
- answer:
1018
- `The Hyper ${lstSymbol} Vault is a tokenized strategy that automatically loops your ${lstSymbol} to create up to 5x leverage to hence yield in a very low risk manner.`,
1710
+ answer: `The Hyper ${lstSymbol} Vault is a tokenized strategy that automatically loops your ${lstSymbol} to create up to 5x leverage to hence yield in a very low risk manner.`,
1019
1711
  },
1020
1712
  {
1021
1713
  question: "How does yield allocation work?",
1022
- answer:
1023
- `The strategy uses deposited ${lstSymbol} to collateralize it on Vesu, borrow more ${underlyingSymbol} to loop further. Instead of manually doing this, using flash loan, this leverage is created in a single gas efficient step. Our continuous monitoring systems gauge current yield and available liquidity in real time to make sure yield is optimal. For instance, if the looping becomes in-efficient in future, the strategy will rebalance to reduce leverage or simply hold ${lstSymbol} to continue earning yield.`,
1714
+ answer: `The strategy uses deposited ${lstSymbol} to collateralize it on Vesu, borrow more ${underlyingSymbol} to loop further. Instead of manually doing this, using flash loan, this leverage is created in a single gas efficient step. Our continuous monitoring systems gauge current yield and available liquidity in real time to make sure yield is optimal. For instance, if the looping becomes in-efficient in future, the strategy will rebalance to reduce leverage or simply hold ${lstSymbol} to continue earning yield.`,
1024
1715
  },
1025
1716
  {
1026
1717
  question: "Which protocols/dApp are used??",
1027
1718
  answer: (
1028
1719
  <span>
1029
- Currently, the LST is from <strong>Endur</strong> while <strong>Vesu</strong> is used to collateralize the looped position.
1720
+ Currently, the LST is from <strong>Endur</strong> while{" "}
1721
+ <strong>Vesu</strong> is used to collateralize the looped position.
1030
1722
  </span>
1031
1723
  ),
1032
1724
  },
1033
1725
  {
1034
1726
  question: "Can I get liquidated?",
1035
- answer: "The strategy uses highly correlated assets which drastically reduces the risk of liquidation. However, overtime, if left un-monitored, debt can increase enough to trigger a liquidation. But no worries, our continuous monitoring systems look for situations with reduced health factor and balance collateral/debt to bring it back to safe levels. With Troves, you can have a peaceful sleep.",
1727
+ answer:
1728
+ "The strategy uses highly correlated assets which drastically reduces the risk of liquidation. However, overtime, if left un-monitored, debt can increase enough to trigger a liquidation. But no worries, our continuous monitoring systems look for situations with reduced health factor and balance collateral/debt to bring it back to safe levels. With Troves, you can have a peaceful sleep.",
1036
1729
  },
1037
1730
  {
1038
1731
  question: "What do I receive when I deposit?",
@@ -1063,95 +1756,207 @@ function getFAQs(lstSymbol: string, underlyingSymbol: string): FAQ[] {
1063
1756
  }
1064
1757
 
1065
1758
  const _riskFactor: RiskFactor[] = [
1066
- { type: RiskType.SMART_CONTRACT_RISK, value: SmartContractRiskLevel.WELL_AUDITED, weight: 25, reason: "Audited by Zellic" },
1067
- { type: RiskType.LIQUIDATION_RISK, value: LiquidationRiskLevel.VERY_LOW_PROBABILITY, weight: 25, reason: "The collateral and debt are highly correlated" },
1068
- { type: RiskType.TECHNICAL_RISK, value: TechnicalRiskLevel.STABLE_INFRASTRUCTURE, weight: 25, reason: "Liquidation can only happen if vault is left un-monitored for weeks, which is highly unlikely. We actively monitor all services on a daily basis." },
1069
- {type: RiskType.DEPEG_RISK, value: DepegRiskLevel.GENERALLY_STABLE, weight: 25, reason: "Generally stable pegged assets" },
1759
+ {
1760
+ type: RiskType.SMART_CONTRACT_RISK,
1761
+ value: SmartContractRiskLevel.WELL_AUDITED,
1762
+ weight: 25,
1763
+ reason: "Audited by Zellic",
1764
+ },
1765
+ {
1766
+ type: RiskType.LIQUIDATION_RISK,
1767
+ value: LiquidationRiskLevel.VERY_LOW_PROBABILITY,
1768
+ weight: 25,
1769
+ reason: "The collateral and debt are highly correlated",
1770
+ },
1771
+ {
1772
+ type: RiskType.TECHNICAL_RISK,
1773
+ value: TechnicalRiskLevel.STABLE_INFRASTRUCTURE,
1774
+ weight: 25,
1775
+ reason:
1776
+ "Liquidation can only happen if vault is left un-monitored for weeks, which is highly unlikely. We actively monitor all services on a daily basis.",
1777
+ },
1778
+ {
1779
+ type: RiskType.DEPEG_RISK,
1780
+ value: DepegRiskLevel.GENERALLY_STABLE,
1781
+ weight: 25,
1782
+ reason: "Generally stable pegged assets",
1783
+ },
1070
1784
  ];
1071
1785
 
1072
- const borrowableAssets = [
1073
- 'WBTC', 'tBTC', 'LBTC', 'solvBTC'
1074
- ]
1786
+ const btcBorrowableAssets = ["WBTC", "tBTC", "LBTC", "solvBTC"];
1075
1787
 
1076
1788
  const hyperxSTRK: HyperLSTStrategySettings = {
1077
- vaultAddress: ContractAddr.from('0x46c7a54c82b1fe374353859f554a40b8bd31d3e30f742901579e7b57b1b5960'),
1078
- manager: ContractAddr.from('0x5d499cd333757f461a0bedaca3dfc4d77320c773037e0aa299f22a6dbfdc03a'),
1079
- vaultAllocator: ContractAddr.from('0x511d07953a09bc7c505970891507c5a2486d2ea22752601a14db092186d7caa'),
1080
- redeemRequestNFT: ContractAddr.from('0x51e40b839dc0c2feca923f863072673b94abfa2483345be3b30b457a90d095'),
1081
- aumOracle: ContractAddr.from('0x48cf709870a1a0d453d37de108e0c41b8b89819ef54f95abc0e2e1f98bbe937'),
1789
+ vaultAddress: ContractAddr.from(
1790
+ "0x46c7a54c82b1fe374353859f554a40b8bd31d3e30f742901579e7b57b1b5960",
1791
+ ),
1792
+ manager: ContractAddr.from(
1793
+ "0x5d499cd333757f461a0bedaca3dfc4d77320c773037e0aa299f22a6dbfdc03a",
1794
+ ),
1795
+ vaultAllocator: ContractAddr.from(
1796
+ "0x511d07953a09bc7c505970891507c5a2486d2ea22752601a14db092186d7caa",
1797
+ ),
1798
+ redeemRequestNFT: ContractAddr.from(
1799
+ "0x51e40b839dc0c2feca923f863072673b94abfa2483345be3b30b457a90d095",
1800
+ ),
1801
+ aumOracle: ContractAddr.from(
1802
+ "0x48cf709870a1a0d453d37de108e0c41b8b89819ef54f95abc0e2e1f98bbe937",
1803
+ ),
1082
1804
  leafAdapters: [],
1083
1805
  adapters: [],
1084
1806
  targetHealthFactor: 1.1,
1085
1807
  minHealthFactor: 1.05,
1086
- borrowable_assets: Global.getDefaultTokens().filter(token => token.symbol === 'STRK'),
1808
+ borrowable_assets: [
1809
+ ...Global.getDefaultTokens()
1810
+ .filter((token) => token.symbol === "STRK")
1811
+ .map((token) => ({ token, poolId: VesuPools.Re7xSTRK })),
1812
+ ...Global.getDefaultTokens()
1813
+ .filter((token) => token.symbol === "STRK")
1814
+ .map((token) => ({ token, poolId: VesuPools.Prime })),
1815
+ ...Global.getDefaultTokens()
1816
+ .filter((token) => token.symbol === "STRK")
1817
+ .map((token) => ({ token, poolId: VesuPools.Re7STRK })),
1818
+ ],
1087
1819
  underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'STRK')!,
1088
- defaultPoolId: VesuPools.Re7xSTRK,
1089
- altSupportedPoolIds: [VesuPools.Prime],
1820
+ defaultPoolId: VesuPools.Re7STRK,
1090
1821
  }
1091
1822
 
1092
1823
  const hyperxWBTC: HyperLSTStrategySettings = {
1093
- vaultAddress: ContractAddr.from('0x2da9d0f96a46b453f55604313785dc866424240b1c6811d13bef594343db818'),
1094
- manager: ContractAddr.from('0x75866db44c81e6986f06035206ee9c7d15833ddb22d6a22c016cfb5c866a491'),
1095
- vaultAllocator: ContractAddr.from('0x57b5c1bb457b5e840a2714ae53ada87d77be2f3fd33a59b4fe709ef20c020c1'),
1096
- redeemRequestNFT: ContractAddr.from('0x7a5dc288325456f05e70e9616e16bc02ffbe448f4b89f80b47c0970b989c7c'),
1097
- aumOracle: ContractAddr.from('0x258f8a0ca0d21f542e48ad89d00e92dc4d9db4999084f50ef9c22dfb1e83023'),
1824
+ vaultAddress: ContractAddr.from(
1825
+ "0x2da9d0f96a46b453f55604313785dc866424240b1c6811d13bef594343db818",
1826
+ ),
1827
+ manager: ContractAddr.from(
1828
+ "0x75866db44c81e6986f06035206ee9c7d15833ddb22d6a22c016cfb5c866a491",
1829
+ ),
1830
+ vaultAllocator: ContractAddr.from(
1831
+ "0x57b5c1bb457b5e840a2714ae53ada87d77be2f3fd33a59b4fe709ef20c020c1",
1832
+ ),
1833
+ redeemRequestNFT: ContractAddr.from(
1834
+ "0x7a5dc288325456f05e70e9616e16bc02ffbe448f4b89f80b47c0970b989c7c",
1835
+ ),
1836
+ aumOracle: ContractAddr.from(
1837
+ "0x258f8a0ca0d21f542e48ad89d00e92dc4d9db4999084f50ef9c22dfb1e83023",
1838
+ ),
1098
1839
  leafAdapters: [],
1099
1840
  adapters: [],
1100
1841
  targetHealthFactor: 1.1,
1101
1842
  minHealthFactor: 1.05,
1102
- borrowable_assets: borrowableAssets.map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
1103
- underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!,
1843
+ borrowable_assets: [
1844
+ // allow all BTC flavours borrowing on Re7xBTC pool
1845
+ ...btcBorrowableAssets
1846
+ .map(
1847
+ (asset) =>
1848
+ Global.getDefaultTokens().find((token) => token.symbol === asset)!,
1849
+ )
1850
+ .map((token) => ({ token, poolId: VesuPools.Re7xBTC })),
1851
+ // allow only WBTC borrowing on Prime pool
1852
+ ...Global.getDefaultTokens()
1853
+ .filter((token) => token.symbol === "WBTC")
1854
+ .map((token) => ({ token, poolId: VesuPools.Prime })),
1855
+ ],
1856
+ underlyingToken: Global.getDefaultTokens().find(
1857
+ (token) => token.symbol === "WBTC",
1858
+ )!,
1104
1859
  defaultPoolId: VesuPools.Re7xBTC,
1105
- altSupportedPoolIds: [VesuPools.Prime],
1106
- }
1860
+ redemptionRouter: ContractAddr.from(
1861
+ "0x6ea649f402898f69baf775c1afdd08522c071c640b9c4460192070ec2b96417",
1862
+ ),
1863
+ };
1107
1864
 
1108
1865
  const hyperxtBTC: HyperLSTStrategySettings = {
1109
- vaultAddress: ContractAddr.from('0x47d5f68477e5637ce0e56436c6b5eee5a354e6828995dae106b11a48679328'),
1110
- manager: ContractAddr.from('0xc4cc3e08029a0ae076f5fdfca70575abb78d23c5cd1c49a957f7e697885401'),
1111
- vaultAllocator: ContractAddr.from('0x50bbd4fe69f841ecb13b2619fe50ebfa4e8944671b5d0ebf7868fd80c61b31e'),
1112
- redeemRequestNFT: ContractAddr.from('0xeac9032f02057779816e38a6cb9185d12d86b3aacc9949b96b36de359c1e3'),
1113
- aumOracle: ContractAddr.from('0x7e0d05cb7ba3f7db77a36c21c21583b5a524c2e685c08c24b3554911fb4a039'),
1866
+ vaultAddress: ContractAddr.from(
1867
+ "0x47d5f68477e5637ce0e56436c6b5eee5a354e6828995dae106b11a48679328",
1868
+ ),
1869
+ manager: ContractAddr.from(
1870
+ "0xc4cc3e08029a0ae076f5fdfca70575abb78d23c5cd1c49a957f7e697885401",
1871
+ ),
1872
+ vaultAllocator: ContractAddr.from(
1873
+ "0x50bbd4fe69f841ecb13b2619fe50ebfa4e8944671b5d0ebf7868fd80c61b31e",
1874
+ ),
1875
+ redeemRequestNFT: ContractAddr.from(
1876
+ "0xeac9032f02057779816e38a6cb9185d12d86b3aacc9949b96b36de359c1e3",
1877
+ ),
1878
+ aumOracle: ContractAddr.from(
1879
+ "0x7e0d05cb7ba3f7db77a36c21c21583b5a524c2e685c08c24b3554911fb4a039",
1880
+ ),
1114
1881
  leafAdapters: [],
1115
1882
  adapters: [],
1116
1883
  targetHealthFactor: 1.1,
1117
1884
  minHealthFactor: 1.05,
1118
- borrowable_assets: Global.getDefaultTokens().filter(token => token.symbol === 'tBTC' || token.symbol === 'WBTC'),
1119
- underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'tBTC')!,
1885
+ borrowable_assets: [
1886
+ ...Global.getDefaultTokens()
1887
+ .filter((token) => token.symbol === "tBTC" || token.symbol === "WBTC")
1888
+ .map((token) => ({ token, poolId: VesuPools.Re7xBTC })),
1889
+ ],
1890
+ underlyingToken: Global.getDefaultTokens().find(
1891
+ (token) => token.symbol === "tBTC",
1892
+ )!,
1120
1893
  defaultPoolId: VesuPools.Re7xBTC,
1121
- altSupportedPoolIds: [],
1122
- }
1894
+ redemptionRouter: ContractAddr.from(
1895
+ "0x3de9c409d1e357e25778fb7a3e2e2393666956846a5c2caa607296fa8e76b5d",
1896
+ ),
1897
+ };
1123
1898
 
1124
1899
  const hyperxsBTC: HyperLSTStrategySettings = {
1125
- vaultAddress: ContractAddr.from('0x437ef1e7d0f100b2e070b7a65cafec0b2be31b0290776da8b4112f5473d8d9'),
1126
- manager: ContractAddr.from('0xc9ac023090625b0be3f6532ca353f086746f9c09f939dbc1b2613f09e5f821'),
1127
- vaultAllocator: ContractAddr.from('0x60c2d856936b975459a5b4eb28b8672d91f757bd76cebb6241f8d670185dc01'),
1128
- redeemRequestNFT: ContractAddr.from('0x429e8ee8bc7ecd1ade72630d350a2e0f10f9a2507c45f188ba17fe8f2ab4cf3'),
1129
- aumOracle: ContractAddr.from('0x149298ade3e79ec6cbdac6cfad289c57504eaf54e590939136ed1ceca60c345'),
1900
+ vaultAddress: ContractAddr.from(
1901
+ "0x437ef1e7d0f100b2e070b7a65cafec0b2be31b0290776da8b4112f5473d8d9",
1902
+ ),
1903
+ manager: ContractAddr.from(
1904
+ "0xc9ac023090625b0be3f6532ca353f086746f9c09f939dbc1b2613f09e5f821",
1905
+ ),
1906
+ vaultAllocator: ContractAddr.from(
1907
+ "0x60c2d856936b975459a5b4eb28b8672d91f757bd76cebb6241f8d670185dc01",
1908
+ ),
1909
+ redeemRequestNFT: ContractAddr.from(
1910
+ "0x429e8ee8bc7ecd1ade72630d350a2e0f10f9a2507c45f188ba17fe8f2ab4cf3",
1911
+ ),
1912
+ aumOracle: ContractAddr.from(
1913
+ "0x149298ade3e79ec6cbdac6cfad289c57504eaf54e590939136ed1ceca60c345",
1914
+ ),
1130
1915
  leafAdapters: [],
1131
1916
  adapters: [],
1132
1917
  targetHealthFactor: 1.1,
1133
1918
  minHealthFactor: 1.05,
1134
- borrowable_assets: Global.getDefaultTokens().filter(token => token.symbol === 'solvBTC'),
1135
- underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'solvBTC')!,
1919
+ borrowable_assets: [
1920
+ ...Global.getDefaultTokens()
1921
+ .filter((token) => token.symbol === "solvBTC")
1922
+ .map((token) => ({ token, poolId: VesuPools.Re7xBTC })),
1923
+ ],
1924
+ underlyingToken: Global.getDefaultTokens().find(
1925
+ (token) => token.symbol === "solvBTC",
1926
+ )!,
1136
1927
  defaultPoolId: VesuPools.Re7xBTC,
1137
- altSupportedPoolIds: [],
1138
- }
1928
+ };
1139
1929
 
1140
1930
  const hyperxLBTC: HyperLSTStrategySettings = {
1141
- vaultAddress: ContractAddr.from('0x64cf24d4883fe569926419a0569ab34497c6956a1a308fa883257f7486d7030'),
1142
- manager: ContractAddr.from('0x203530a4022a99b8f4b406aaf33b0849d43ad7422c1d5cc14ff8c667abec6c0'),
1143
- vaultAllocator: ContractAddr.from('0x7dbc8ccd4eabce6ea6c19e0e5c9ccca3a93bd510303b9e071cbe25fc508546e'),
1144
- redeemRequestNFT: ContractAddr.from('0x5ee66a39af9aef3d0d48982b4a63e8bd2a5bad021916bd87fb0eae3a26800b8'),
1145
- aumOracle: ContractAddr.from('0x23d69e4391fa72d10e625e7575d8bddbb4aff96f04503f83fdde23123bf41d0'),
1931
+ vaultAddress: ContractAddr.from(
1932
+ "0x64cf24d4883fe569926419a0569ab34497c6956a1a308fa883257f7486d7030",
1933
+ ),
1934
+ manager: ContractAddr.from(
1935
+ "0x203530a4022a99b8f4b406aaf33b0849d43ad7422c1d5cc14ff8c667abec6c0",
1936
+ ),
1937
+ vaultAllocator: ContractAddr.from(
1938
+ "0x7dbc8ccd4eabce6ea6c19e0e5c9ccca3a93bd510303b9e071cbe25fc508546e",
1939
+ ),
1940
+ redeemRequestNFT: ContractAddr.from(
1941
+ "0x5ee66a39af9aef3d0d48982b4a63e8bd2a5bad021916bd87fb0eae3a26800b8",
1942
+ ),
1943
+ aumOracle: ContractAddr.from(
1944
+ "0x23d69e4391fa72d10e625e7575d8bddbb4aff96f04503f83fdde23123bf41d0",
1945
+ ),
1146
1946
  leafAdapters: [],
1147
1947
  adapters: [],
1148
1948
  targetHealthFactor: 1.1,
1149
1949
  minHealthFactor: 1.05,
1150
- borrowable_assets: Global.getDefaultTokens().filter(token => token.symbol === 'LBTC'),
1151
- underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'LBTC')!,
1950
+ borrowable_assets: [
1951
+ ...Global.getDefaultTokens()
1952
+ .filter((token) => token.symbol === "LBTC")
1953
+ .map((token) => ({ token, poolId: VesuPools.Re7xBTC })),
1954
+ ],
1955
+ underlyingToken: Global.getDefaultTokens().find(
1956
+ (token) => token.symbol === "LBTC",
1957
+ )!,
1152
1958
  defaultPoolId: VesuPools.Re7xBTC,
1153
- altSupportedPoolIds: [],
1154
- }
1959
+ };
1155
1960
 
1156
1961
  function getInvestmentSteps(lstSymbol: string, underlyingSymbol: string) {
1157
1962
  return [
@@ -1159,42 +1964,180 @@ function getInvestmentSteps(lstSymbol: string, underlyingSymbol: string) {
1159
1964
  `The vault manager loops the ${underlyingSymbol} to buy ${lstSymbol}`,
1160
1965
  `The vault manager collateralizes the ${lstSymbol} on Vesu`,
1161
1966
  `The vault manager borrows more ${underlyingSymbol} to loop further`,
1162
- `If required, adjust leverage or re-allocate assets within LST pool on Vesu to optimize yield`
1163
- ]
1967
+ `If required, adjust leverage or re-allocate assets within LST pool on Vesu to optimize yield`,
1968
+ ];
1164
1969
  }
1165
1970
 
1166
- function getStrategySettings(lstSymbol: string, underlyingSymbol: string, addresses: HyperLSTStrategySettings, isPreview: boolean = false): IStrategyMetadata<HyperLSTStrategySettings> {
1971
+ // Helper to get maxTVL based on LST symbol (matching client values)
1972
+ function getMaxTVL(lstSymbol: string): Web3Number {
1973
+ const lstMaxTVLs: Record<string, number> = {
1974
+ xWBTC: 5,
1975
+ xLBTC: 5,
1976
+ xtBTC: 5,
1977
+ xsBTC: 5,
1978
+ xSTRK: 7000000,
1979
+ };
1980
+
1981
+ const maxTVLValue = lstMaxTVLs[lstSymbol] || 0;
1982
+ const token = Global.getDefaultTokens().find(
1983
+ (token) => token.symbol === lstSymbol,
1984
+ );
1985
+ if (!token) {
1986
+ return Web3Number.fromWei(0, 18);
1987
+ }
1988
+ return Web3Number.fromWei(maxTVLValue, token.decimals);
1989
+ }
1990
+
1991
+ // Helper to create Hyper LST strategy settings
1992
+ function createHyperLSTSettings(
1993
+ lstSymbol: string,
1994
+ underlyingSymbol: string,
1995
+ ): StrategySettings {
1996
+ const depositToken = Global.getDefaultTokens().find(
1997
+ (token) => token.symbol === lstSymbol,
1998
+ )!;
1999
+ return {
2000
+ maxTVL: getMaxTVL(lstSymbol),
2001
+ isPaused: false,
2002
+ liveStatus: StrategyLiveStatus.HOT,
2003
+ isAudited: true,
2004
+ isInstantWithdrawal: false,
2005
+ hideHarvestInfo: true,
2006
+ quoteToken: depositToken,
2007
+ showWithdrawalWarningModal: false,
2008
+ alerts: [
2009
+ {
2010
+ tab: "withdraw" as const,
2011
+ text: "On withdrawal, you will receive an NFT representing your withdrawal request. The funds will be automatically sent to your wallet (NFT owner) in 24 hours (In this initial phase of Launch). You can monitor the status in transactions tab.",
2012
+ type: "info" as const,
2013
+ },
2014
+ {
2015
+ tab: "deposit" as const,
2016
+ text: (
2017
+ <>
2018
+ To acquire the LST, please visit{" "}
2019
+ <a
2020
+ href="https://app.endur.fi"
2021
+ target="_blank"
2022
+ rel="noopener noreferrer"
2023
+ >
2024
+ endur.fi
2025
+ </a>
2026
+ </>
2027
+ ),
2028
+ type: "info" as const,
2029
+ },
2030
+ {
2031
+ tab: "deposit" as const,
2032
+ text: "It may take up to one week for your deposit to appreciate in value. This delay occurs because the LST price is sourced from DEXes and liquidity is usually rebased once a week.",
2033
+ type: "info" as const,
2034
+ },
2035
+ ],
2036
+ };
2037
+ }
2038
+
2039
+ const HYPER_LST_SECURITY = {
2040
+ auditStatus: AuditStatus.AUDITED,
2041
+ sourceCode: {
2042
+ type: SourceCodeType.CLOSED_SOURCE,
2043
+ contractLink: "https://github.com/trovesfi/troves-contracts",
2044
+ },
2045
+ accessControl: {
2046
+ type: AccessControlType.STANDARD_ACCOUNT,
2047
+ addresses: [ContractAddr.from("0x0")],
2048
+ timeLock: "2 Days",
2049
+ },
2050
+ };
2051
+
2052
+ const HYPER_LST_REDEMPTION_INFO: RedemptionInfo = {
2053
+ instantWithdrawalVault: InstantWithdrawalVault.NO,
2054
+ redemptionsInfo: [
2055
+ {
2056
+ title: "Typical Duration",
2057
+ description: "1-2 hours",
2058
+ },
2059
+ ],
2060
+ alerts: [
2061
+ {
2062
+ type: "info",
2063
+ text: "In cases of low liquidity, high slippages, the redemptions can take longer time. Redemption times are estimates and may vary based on network conditions and liquidity requirements.",
2064
+ tab: "withdraw",
2065
+ },
2066
+ ],
2067
+ };
2068
+
2069
+ function getStrategySettings(
2070
+ lstSymbol: string,
2071
+ underlyingSymbol: string,
2072
+ settings: HyperLSTStrategySettings,
2073
+ isPreview: boolean = false,
2074
+ ): IStrategyMetadata<HyperLSTStrategySettings> {
1167
2075
  return {
2076
+ id: `hyper_${lstSymbol.toLowerCase()}`,
1168
2077
  name: `Hyper ${lstSymbol}`,
1169
2078
  description: getDescription(lstSymbol, underlyingSymbol),
1170
- address: addresses.vaultAddress,
2079
+ address: settings.vaultAddress,
1171
2080
  launchBlock: 0,
1172
- type: 'Other',
1173
- depositTokens: [Global.getDefaultTokens().find(token => token.symbol === lstSymbol)!],
1174
- additionalInfo: getLooperSettings(lstSymbol, underlyingSymbol, addresses, addresses.defaultPoolId, lstSymbol === 'xSTRK' ? [VesuPools.Prime] : []),
2081
+ type: "Other",
2082
+ vaultType: {
2083
+ type: VaultType.LOOPING,
2084
+ description: `Creates leveraged looping position on ${lstSymbol} by borrowing ${underlyingSymbol} to increase yield`,
2085
+ },
2086
+ depositTokens: [
2087
+ Global.getDefaultTokens().find((token) => token.symbol === lstSymbol)!,
2088
+ ],
2089
+ additionalInfo: getLooperSettings(
2090
+ lstSymbol,
2091
+ underlyingSymbol,
2092
+ settings,
2093
+ settings.defaultPoolId,
2094
+ ),
1175
2095
  risk: {
1176
2096
  riskFactor: _riskFactor,
1177
2097
  netRisk:
1178
2098
  _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) /
1179
2099
  _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
1180
- notARisks: getNoRiskTags(_riskFactor)
2100
+ notARisks: getNoRiskTags(_riskFactor),
1181
2101
  },
1182
2102
  auditUrl: AUDIT_URL,
1183
2103
  protocols: [Protocols.ENDUR, Protocols.VESU],
1184
- maxTVL: Web3Number.fromWei(0, 18),
1185
- contractDetails: getContractDetails(addresses),
2104
+ curator: {
2105
+ name: "Unwrap Labs",
2106
+ logo: "https://assets.troves.fi/integrations/unwraplabs/white.png",
2107
+ },
2108
+ settings: createHyperLSTSettings(lstSymbol, underlyingSymbol),
2109
+ contractDetails: getContractDetails(settings),
1186
2110
  faqs: getFAQs(lstSymbol, underlyingSymbol),
1187
2111
  investmentSteps: getInvestmentSteps(lstSymbol, underlyingSymbol),
1188
2112
  isPreview: isPreview,
1189
- apyMethodology: 'Current annualized APY in terms of base asset of the LST. There is no additional fee taken by Troves on LST APY. We charge a 10% performance fee on the additional gain which is already accounted in the APY shown.'
1190
- }
2113
+ apyMethodology:
2114
+ "Current annualized APY in terms of base asset of the LST. There is no additional fee taken by Troves on LST APY. We charge a 10% performance fee on the additional gain which is already accounted in the APY shown.",
2115
+ realizedAPYMethodology:
2116
+ "The realizedAPY is based on past 14 days performance by the vault",
2117
+ tags: lstSymbol.includes("BTC")
2118
+ ? [StrategyTag.BTC, StrategyTag.LEVERED]
2119
+ : [StrategyTag.LEVERED],
2120
+ security: HYPER_LST_SECURITY,
2121
+ redemptionInfo: HYPER_LST_REDEMPTION_INFO,
2122
+ usualTimeToEarnings: "2 weeks",
2123
+ usualTimeToEarningsDescription:
2124
+ "Strategy returns depend on LST price on DEXes. Even though the true price of LST on Endur increases continuously, the DEX price may lag sometimes, and historically is seen to rebase at least once every 2 hours. This is when you realise your earnings.",
2125
+ points: [
2126
+ {
2127
+ multiplier: 4,
2128
+ logo: "https://endur.fi/favicon.ico",
2129
+ toolTip:
2130
+ "This strategy holds xSTRK. Earn 3-4x Endur points on your xSTRK due to the leverage. Points can be found on endur.fi.",
2131
+ },
2132
+ ],
2133
+ };
1191
2134
  }
1192
2135
 
1193
2136
  export const HyperLSTStrategies: IStrategyMetadata<HyperLSTStrategySettings>[] =
1194
2137
  [
1195
- getStrategySettings('xSTRK', 'STRK', hyperxSTRK, false),
1196
- getStrategySettings('xWBTC', 'WBTC', hyperxWBTC, false),
1197
- getStrategySettings('xtBTC', 'tBTC', hyperxtBTC, false),
1198
- getStrategySettings('xsBTC', 'solvBTC', hyperxsBTC, false),
1199
- getStrategySettings('xLBTC', 'LBTC', hyperxLBTC, false),
1200
- ]
2138
+ getStrategySettings("xSTRK", "STRK", hyperxSTRK, false),
2139
+ getStrategySettings("xWBTC", "WBTC", hyperxWBTC, false),
2140
+ getStrategySettings("xtBTC", "tBTC", hyperxtBTC, false),
2141
+ getStrategySettings("xsBTC", "solvBTC", hyperxsBTC, false),
2142
+ getStrategySettings("xLBTC", "LBTC", hyperxLBTC, false),
2143
+ ];