@strkfarm/sdk 2.0.0-staging.4 → 2.0.0-staging.41

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