@strkfarm/sdk 2.0.0-staging.22 → 2.0.0-staging.25

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