@strkfarm/sdk 2.0.0-staging.7 → 2.0.0-staging.70

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