@strkfarm/sdk 2.0.0-dev.27 → 2.0.0-dev.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/cli.js +190 -36
  2. package/dist/cli.mjs +188 -34
  3. package/dist/index.browser.global.js +79130 -49357
  4. package/dist/index.browser.mjs +18039 -11434
  5. package/dist/index.d.ts +2869 -898
  6. package/dist/index.js +19036 -12210
  7. package/dist/index.mjs +18942 -12161
  8. package/package.json +1 -1
  9. package/src/data/avnu.abi.json +840 -0
  10. package/src/data/ekubo-price-fethcer.abi.json +265 -0
  11. package/src/dataTypes/_bignumber.ts +13 -4
  12. package/src/dataTypes/index.ts +3 -2
  13. package/src/dataTypes/mynumber.ts +141 -0
  14. package/src/global.ts +76 -41
  15. package/src/index.browser.ts +2 -1
  16. package/src/interfaces/common.tsx +167 -2
  17. package/src/modules/ExtendedWrapperSDk/types.ts +26 -4
  18. package/src/modules/ExtendedWrapperSDk/wrapper.ts +110 -67
  19. package/src/modules/apollo-client-config.ts +28 -0
  20. package/src/modules/avnu.ts +4 -4
  21. package/src/modules/ekubo-pricer.ts +79 -0
  22. package/src/modules/ekubo-quoter.ts +46 -30
  23. package/src/modules/erc20.ts +17 -0
  24. package/src/modules/harvests.ts +43 -29
  25. package/src/modules/pragma.ts +23 -8
  26. package/src/modules/pricer-from-api.ts +156 -15
  27. package/src/modules/pricer-lst.ts +1 -1
  28. package/src/modules/pricer.ts +40 -4
  29. package/src/modules/pricerBase.ts +2 -1
  30. package/src/node/deployer.ts +36 -1
  31. package/src/node/pricer-redis.ts +2 -1
  32. package/src/strategies/base-strategy.ts +78 -10
  33. package/src/strategies/ekubo-cl-vault.tsx +906 -347
  34. package/src/strategies/factory.ts +159 -0
  35. package/src/strategies/index.ts +6 -1
  36. package/src/strategies/registry.ts +239 -0
  37. package/src/strategies/sensei.ts +335 -7
  38. package/src/strategies/svk-strategy.ts +97 -27
  39. package/src/strategies/types.ts +4 -0
  40. package/src/strategies/universal-adapters/adapter-utils.ts +2 -1
  41. package/src/strategies/universal-adapters/avnu-adapter.ts +177 -268
  42. package/src/strategies/universal-adapters/baseAdapter.ts +263 -251
  43. package/src/strategies/universal-adapters/common-adapter.ts +206 -203
  44. package/src/strategies/universal-adapters/extended-adapter.ts +155 -336
  45. package/src/strategies/universal-adapters/index.ts +9 -8
  46. package/src/strategies/universal-adapters/token-transfer-adapter.ts +200 -0
  47. package/src/strategies/universal-adapters/usdc<>usdce-adapter.ts +200 -0
  48. package/src/strategies/universal-adapters/vesu-adapter.ts +110 -75
  49. package/src/strategies/universal-adapters/vesu-modify-position-adapter.ts +476 -0
  50. package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +762 -844
  51. package/src/strategies/universal-adapters/vesu-position-common.ts +251 -0
  52. package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +18 -3
  53. package/src/strategies/universal-lst-muliplier-strategy.tsx +396 -204
  54. package/src/strategies/universal-strategy.tsx +1426 -1178
  55. package/src/strategies/vesu-extended-strategy/services/executionService.ts +2251 -0
  56. package/src/strategies/vesu-extended-strategy/services/extended-vesu-state-manager.ts +2941 -0
  57. package/src/strategies/vesu-extended-strategy/services/operationService.ts +12 -1
  58. package/src/strategies/vesu-extended-strategy/types/transaction-metadata.ts +52 -0
  59. package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +1 -0
  60. package/src/strategies/vesu-extended-strategy/utils/constants.ts +2 -0
  61. package/src/strategies/vesu-extended-strategy/utils/helper.ts +158 -124
  62. package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +377 -1788
  63. package/src/strategies/vesu-rebalance.tsx +255 -152
  64. package/src/utils/health-factor-math.ts +4 -1
  65. package/src/utils/index.ts +2 -1
  66. package/src/utils/logger.browser.ts +22 -4
  67. package/src/utils/logger.node.ts +259 -24
  68. package/src/utils/starknet-call-parser.ts +1036 -0
  69. package/src/utils/strategy-utils.ts +61 -0
  70. package/src/strategies/universal-adapters/unused-balance-adapter.ts +0 -109
@@ -1,18 +1,19 @@
1
- import { FAQ, getMainnetConfig, getNoRiskTags, highlightTextWithLinks, IConfig, IStrategyMetadata, Protocols, RiskFactor, RiskType, TokenInfo, VaultPosition } from "@/interfaces";
1
+ import { FAQ, getMainnetConfig, getNoRiskTags, highlightTextWithLinks, IConfig, IStrategyMetadata, Protocols, RiskFactor, RiskType, TokenInfo, VaultPosition, StrategyTag, AuditStatus, SourceCodeType, AccessControlType, InstantWithdrawalVault, StrategyLiveStatus, StrategySettings, VaultType, RedemptionInfo } from "@/interfaces";
2
2
  import { AUMTypes, getContractDetails, UNIVERSAL_MANAGE_IDS, UniversalManageCall, UniversalStrategySettings } from "./universal-strategy";
3
3
  import { PricerBase } from "@/modules/pricerBase";
4
4
  import { ContractAddr, Web3Number } from "@/dataTypes";
5
5
  import { Global } from "@/global";
6
- import { ApproveCallParams, APYType, AvnuSwapCallParams, BaseAdapterConfig, CommonAdapter, ManageCall, PositionInfo, UnusedBalanceAdapter, VesuMultiplyAdapter, VesuPools, VesuSupplyOnlyAdapter } from "./universal-adapters";
6
+ import { ApproveCallParams, APYType, AvnuSwapCallParams, BaseAdapterConfig, CommonAdapter, ManageCall, PositionInfo, TokenTransferAdapter, VesuMultiplyAdapter, VesuPools, VesuSupplyOnlyAdapter } from "./universal-adapters";
7
7
  import { AVNU_EXCHANGE } from "./universal-adapters/adapter-utils";
8
8
  import { DepegRiskLevel, LiquidationRiskLevel, SmartContractRiskLevel, TechnicalRiskLevel } from "@/interfaces/risks";
9
- import { AvnuWrapper, EkuboQuoter, ERC20, PricerFromApi, PricerLST } from "@/modules";
9
+ import { AvnuWrapper, EkuboQuoter, ERC20, LSTAPRService, PricerFromApi, PricerLST } from "@/modules";
10
10
  import { assert, logger } from "@/utils";
11
11
  import { SingleActionAmount, SingleTokenInfo } from "./base-strategy";
12
12
  import { SVKStrategy } from "./svk-strategy";
13
- import { Call, uint256 } from "starknet";
13
+ import { Call, Contract, uint256 } from "starknet";
14
14
  import ERC4626Abi from "@/data/erc4626.abi.json";
15
15
  import { AdapterOptimizer } from "./universal-adapters/adapter-optimizer";
16
+ import { LSTPriceType } from "./types";
16
17
  import { MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP } from "./vesu-extended-strategy/utils/constants";
17
18
  import { VesuExtendedStrategySettings } from "./vesu-extended-strategy/vesu-extended-strategy";
18
19
 
@@ -28,7 +29,7 @@ export interface HyperLSTStrategySettings extends UniversalStrategySettings {
28
29
  export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings> extends SVKStrategy<S> {
29
30
 
30
31
  private quoteAmountToFetchPrice = new Web3Number(1, 18);
31
-
32
+
32
33
  constructor(config: IConfig, pricer: PricerBase, metadata: IStrategyMetadata<S>) {
33
34
  super(config, pricer, metadata);
34
35
  const STRKToken = Global.getDefaultTokens().find(token => token.symbol === 'STRK')!;
@@ -43,9 +44,9 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
43
44
  this.metadata.additionalInfo.adapters.forEach(adapter => {
44
45
  adapter.adapter.config.networkConfig = this.config;
45
46
  adapter.adapter.config.pricer = this.pricer;
46
- if ((adapter.adapter as VesuMultiplyAdapter).vesuAdapter) {
47
- (adapter.adapter as VesuMultiplyAdapter).vesuAdapter.networkConfig = this.config;
48
- (adapter.adapter as VesuMultiplyAdapter).vesuAdapter.pricer = this.pricer;
47
+ if ((adapter.adapter as any)._vesuAdapter) {
48
+ (adapter.adapter as any)._vesuAdapter.networkConfig = this.config;
49
+ (adapter.adapter as any)._vesuAdapter.pricer = this.pricer;
49
50
  }
50
51
  });
51
52
  }
@@ -83,6 +84,102 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
83
84
  return vesuMultipleAdapters;
84
85
  }
85
86
 
87
+ async getRewardsAUM(_prevAum: Web3Number) {
88
+ const lstToken = this.asset();
89
+ // Defi spring rewards handling; currently returns 0 as SVKStrategy has no built-in rewards logic
90
+ return Web3Number.fromWei("0", lstToken.decimals);
91
+ }
92
+
93
+ async getLSTDexPrice() {
94
+ const ekuboQuoter = new EkuboQuoter(this.config, this.pricer);
95
+ const lstTokenInfo = this.asset();
96
+ const lstUnderlyingTokenInfo = this.getLSTUnderlyingTokenInfo();
97
+ const quote = await ekuboQuoter.getQuoteExactInput(
98
+ lstTokenInfo.address.address,
99
+ lstUnderlyingTokenInfo.address.address,
100
+ this.quoteAmountToFetchPrice,
101
+ );
102
+
103
+ const outputAmount = Web3Number.fromWei(
104
+ quote.total_calculated,
105
+ lstUnderlyingTokenInfo.decimals,
106
+ );
107
+ const price =
108
+ outputAmount.toNumber() / this.quoteAmountToFetchPrice.toNumber();
109
+ logger.verbose(`${this.getTag()}:: LST Dex Price: ${price}`);
110
+ return price;
111
+ }
112
+
113
+ async getLSTAvnuRate() {
114
+ const vesuAdapter1 = this.getVesuSameTokenAdapter();
115
+ const lstTokenInfo = vesuAdapter1._vesuAdapter.config.collateral;
116
+ const underlyingTokenInfo = vesuAdapter1._vesuAdapter.config.debt;
117
+
118
+ const avnuModule = new AvnuWrapper();
119
+
120
+ const sellAmount = (lstTokenInfo as any).priceCheckAmount
121
+ ? new Web3Number(
122
+ (lstTokenInfo as any).priceCheckAmount,
123
+ underlyingTokenInfo.decimals,
124
+ )
125
+ : new Web3Number(1, underlyingTokenInfo.decimals);
126
+
127
+ const quote = await avnuModule.getQuotes(
128
+ underlyingTokenInfo.address.address,
129
+ lstTokenInfo.address.address,
130
+ sellAmount.toWei(),
131
+ this.metadata.additionalInfo.vaultAllocator.address,
132
+ );
133
+
134
+ const underlyingAmountNumber = sellAmount.toNumber();
135
+ const lstAmountNumber = Web3Number.fromWei(
136
+ quote.buyAmount.toString(),
137
+ lstTokenInfo.decimals,
138
+ ).toNumber();
139
+
140
+ assert(lstAmountNumber > 0, "Avnu LST amount is zero");
141
+
142
+ const exchangeRate = underlyingAmountNumber / lstAmountNumber;
143
+ logger.verbose(
144
+ `${this.getTag()}:: LST Avnu Exchange Rate: ${exchangeRate}`,
145
+ );
146
+ return exchangeRate;
147
+ }
148
+
149
+ async getLSTExchangeRate() {
150
+ const vesuAdapter1 = this.getVesuSameTokenAdapter();
151
+ const lstTokenInfo = vesuAdapter1._vesuAdapter.config.collateral;
152
+ const lstABI = new Contract({
153
+ abi: ERC4626Abi,
154
+ address: lstTokenInfo.address.address,
155
+ providerOrAccount: this.config.provider,
156
+ });
157
+
158
+ const price: any = await lstABI.call("convert_to_assets", [
159
+ uint256.bnToUint256(new Web3Number(1, lstTokenInfo.decimals).toWei()),
160
+ ]);
161
+ const exchangeRate =
162
+ Number(uint256.uint256ToBN(price).toString()) /
163
+ Math.pow(10, lstTokenInfo.decimals);
164
+ logger.verbose(`${this.getTag()}:: LST Exchange Rate: ${exchangeRate}`);
165
+ return exchangeRate;
166
+ }
167
+
168
+ private async _getMinOutputAmountLSTBuy(amountInUnderlying: Web3Number) {
169
+ const lstTruePrice = await this.getLSTExchangeRate();
170
+ const minOutputAmount = amountInUnderlying
171
+ .dividedBy(lstTruePrice)
172
+ .multipliedBy(0.99979);
173
+ return new Web3Number(minOutputAmount.toString(), this.asset().decimals);
174
+ }
175
+
176
+ private async _getMinOutputAmountLSTSell(amountInLST: Web3Number) {
177
+ const lstTruePrice = await this.getLSTExchangeRate();
178
+ const minOutputAmount = amountInLST
179
+ .multipliedBy(lstTruePrice)
180
+ .multipliedBy(0.995);
181
+ return minOutputAmount;
182
+ }
86
183
 
87
184
  // async getAvnuSwapMultiplyCall(params: {
88
185
  // isDeposit: boolean,
@@ -132,7 +229,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
132
229
 
133
230
  // async _getAvnuDepositSwapLegCall(params: {
134
231
  // isDeposit: boolean,
135
- // leg1DepositAmount: Web3Number,
232
+ // leg1DepositAmount: Web3Number,
136
233
  // minHF: number, // e.g. 1.01
137
234
  // vesuAdapter: VesuAdapter
138
235
  // }) {
@@ -144,18 +241,18 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
144
241
  // // approve and swap strk
145
242
  // // add collateral again
146
243
 
147
-
244
+
148
245
  // const legLTV = await vesuAdapter.getLTVConfig(this.config);
149
246
  // logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall legLTV: ${legLTV}`);
150
247
  // const existingPositions = await vesuAdapter.getPositions(this.config);
151
248
  // const collateralisation = await vesuAdapter.getCollateralization(this.config);
152
249
  // const existingCollateralInfo = existingPositions[0];
153
250
  // const existingDebtInfo = existingPositions[1];
154
- // logger.debug(`${this.getTag()}::_getAvnuDepositSwapLegCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
251
+ // logger.debug(`${this.getTag()}::_getAvnuDepositSwapLegCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
155
252
  // existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
156
253
 
157
254
  // // - Prices as seen by Vesu contracts, ideal for HF math
158
- // // Price 1 is ok as fallback bcz that would relatively price the
255
+ // // Price 1 is ok as fallback bcz that would relatively price the
159
256
  // // collateral and debt as equal.
160
257
  // const collateralPrice = collateralisation[0].usdValue > 0 ? collateralisation[0].usdValue / existingCollateralInfo.amount.toNumber() : 1;
161
258
  // const debtPrice = collateralisation[1].usdValue > 0 ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber() : 1;
@@ -202,7 +299,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
202
299
 
203
300
  // logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall debtAmount: ${debtAmount}`);
204
301
  // if (debtAmount.lt(0)) {
205
- // // this is to unwind the position to optimal HF.
302
+ // // this is to unwind the position to optimal HF.
206
303
  // const lstDEXPrice = await this.getLSTDexPrice();
207
304
  // const debtAmountInLST = debtAmount.abs().dividedBy(lstDEXPrice);
208
305
  // const calls = await this.getVesuMultiplyCall({
@@ -228,7 +325,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
228
325
  // }));
229
326
 
230
327
  // console.log(`manageCall1`, manageCall1.call, debtAmount.toWei(), newDepositAmount.toWei());
231
-
328
+
232
329
  // const proofIds: string[] = [STEP0, STEP1];
233
330
  // const manageCalls: ManageCall[] = [manageCall0, manageCall1];
234
331
 
@@ -253,9 +350,9 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
253
350
  // const minAmountWei = (minAmount).toWei();
254
351
  // logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall minAmount: ${minAmount}`);
255
352
  // const swapInfo = await avnuModule.getSwapInfo(
256
- // quote,
257
- // this.metadata.additionalInfo.vaultAllocator.address,
258
- // 0,
353
+ // quote,
354
+ // this.metadata.additionalInfo.vaultAllocator.address,
355
+ // 0,
259
356
  // this.address.address,
260
357
  // minAmountWei
261
358
  // );
@@ -271,7 +368,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
271
368
 
272
369
 
273
370
  // // if the created debt, when added is collateral will put the total HF above min, but below (target + 0.05),
274
- // // then lets close the looping cycle by adding this as collateral.
371
+ // // then lets close the looping cycle by adding this as collateral.
275
372
  // const newCollateral = minAmount.plus(totalCollateral);
276
373
  // const newHF = newCollateral.multipliedBy(collateralPrice).multipliedBy(legLTV).dividedBy(totalDebtAmount).dividedBy(debtPrice).toNumber();
277
374
  // logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall newHF: ${newHF}`);
@@ -283,7 +380,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
283
380
  // const manageCall4 = manage4Info.callConstructor({
284
381
  // amount: minAmount
285
382
  // });
286
-
383
+
287
384
  // const STEP5 = getVesuLegId(UNIVERSAL_MANAGE_IDS.VESU_LEG1, vesuAdapter.config.debt.symbol);
288
385
  // const manage5Info = this.getProofs<VesuModifyPositionCallParams>(STEP5);
289
386
  // const manageCall5 = manage5Info.callConstructor(VesuAdapter.getDefaultModifyPositionCallParams({
@@ -301,7 +398,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
301
398
  // return manageCall;
302
399
  // }
303
400
 
304
- // todo unwind or not deposit when the yield is bad.
401
+ // todo unwind or not deposit when the yield is bad.
305
402
 
306
403
  // async getLSTMultiplierRebalanceCall(): Promise<{ shouldRebalance: boolean, manageCalls: {vesuAdapter: VesuAdapter, manageCall: Call}[] }> {
307
404
  // let shouldRebalance = false;
@@ -327,11 +424,11 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
327
424
  // const healthFactor = await vesuAdapter.getHealthFactor();
328
425
 
329
426
  // const collateralisation = await vesuAdapter.getCollateralization(this.config);
330
- // logger.debug(`${this.getTag()}::getVesuMultiplyCall::${vesuAdapter.config.debt.symbol} existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
427
+ // logger.debug(`${this.getTag()}::getVesuMultiplyCall::${vesuAdapter.config.debt.symbol} existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
331
428
  // existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
332
429
 
333
430
  // // - Prices as seen by Vesu contracts, ideal for HF math
334
- // // Price 1 is ok as fallback bcz that would relatively price the
431
+ // // Price 1 is ok as fallback bcz that would relatively price the
335
432
  // // collateral and debt as equal.
336
433
  // const collateralPrice = collateralisation[0].usdValue > 0 ? collateralisation[0].usdValue / existingCollateralInfo.amount.toNumber() : 1;
337
434
  // const debtPrice = collateralisation[1].usdValue > 0 ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber() : 1;
@@ -341,11 +438,11 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
341
438
  // const isHFTooLow = healthFactor < this.metadata.additionalInfo.minHealthFactor;
342
439
  // const isHFTooHigh = healthFactor > this.metadata.additionalInfo.targetHealthFactor + 0.05;
343
440
  // if (isHFTooLow || isHFTooHigh || 1) {
344
- // // use unused collateral to target more.
441
+ // // use unused collateral to target more.
345
442
  // const manageCall = await this._getAvnuDepositSwapLegCall({
346
443
  // isDeposit: true,
347
444
  // leg1DepositAmount: unusedBalance.amount,
348
- // minHF: 1.02, // todo, shouldnt use this 1.02 HF, if there isn;t more looping left.
445
+ // minHF: 1.02, // todo, shouldnt use this 1.02 HF, if there isn;t more looping left.
349
446
  // vesuAdapter
350
447
  // })
351
448
  // return { shouldRebalance: true, manageCall };
@@ -355,61 +452,17 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
355
452
  // }
356
453
  // }
357
454
 
358
- // protected async getVesuAUM(adapter: VesuAdapter) {
359
- // const legAUM = await adapter.getPositions(this.config);
360
- // const underlying = this.asset();
361
- // // assert its an LST of Endur
362
- // assert(underlying.symbol.startsWith('x'), 'Underlying is not an LST of Endur');
363
-
364
- // let vesuAum = Web3Number.fromWei("0", underlying.decimals);
365
-
366
- // let tokenUnderlyingPrice = await this.getLSTExchangeRate();
367
- // // to offset for usual DEX lag, we multiply by 0.998 (i.e. 0.2% loss)
368
- // tokenUnderlyingPrice = tokenUnderlyingPrice * 0.998;
369
- // logger.verbose(`${this.getTag()} tokenUnderlyingPrice: ${tokenUnderlyingPrice}`);
370
-
371
- // // handle collateral
372
- // if (legAUM[0].token.address.eq(underlying.address)) {
373
- // vesuAum = vesuAum.plus(legAUM[0].amount);
374
- // } else {
375
- // vesuAum = vesuAum.plus(legAUM[0].amount.dividedBy(tokenUnderlyingPrice));
376
- // }
377
-
378
- // // handle debt
379
- // if (legAUM[1].token.address.eq(underlying.address)) {
380
- // vesuAum = vesuAum.minus(legAUM[1].amount);
381
- // } else {
382
- // vesuAum = vesuAum.minus(legAUM[1].amount.dividedBy(tokenUnderlyingPrice));
383
- // };
384
-
385
- // logger.verbose(`${this.getTag()} Vesu AUM: ${vesuAum}, legCollateral: ${legAUM[0].amount.toNumber()}, legDebt: ${legAUM[1].amount.toNumber()}`);
386
- // return vesuAum;
387
- // }
388
-
389
- //
390
- // private async _getMinOutputAmountLSTBuy(amountInUnderlying: Web3Number) {
391
- // const lstTruePrice = await this.getLSTExchangeRate();
392
- // // during buy, the purchase should always be <= true LST price.
393
- // const minOutputAmount = amountInUnderlying.dividedBy(lstTruePrice).multipliedBy(0.99979); // minus 0.021% to account for avnu fees
394
- // return new Web3Number(minOutputAmount.toString(), this.asset().decimals);
395
- // }
396
455
 
397
- // private async _getMinOutputAmountLSTSell(amountInLST: Web3Number) {
398
- // const lstTruePrice = await this.getLSTExchangeRate();
399
- // // during sell, the purchase should always be > 0.995 * true LST price.
400
- // const minOutputAmount = amountInLST.multipliedBy(lstTruePrice).multipliedBy(0.995);
401
- // return minOutputAmount;
402
- // }
403
456
 
404
457
 
405
458
 
406
459
  // todo add a function to findout max borrowable amount without fucking yield
407
- // if the current net yield < LST yield, add a function to calculate how much to unwind.
460
+ // if the current net yield < LST yield, add a function to calculate how much to unwind.
408
461
 
409
462
  /**
410
463
  * Uses vesu's multiple call to create leverage on LST
411
464
  * Deposit amount is in LST
412
- * @param params
465
+ * @param params
413
466
  */
414
467
  async getFundManagementCall(params: {
415
468
  isDeposit: boolean,
@@ -420,8 +473,8 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
420
473
  // logger.verbose(`${this.getTag()}::getVesuMultiplyCall legLTV: ${legLTV}`);
421
474
  const allAdapters = this.metadata.additionalInfo.adapters.map(adapter => adapter.adapter);
422
475
  if (!params.isDeposit) {
423
- // try using unused balance to unwind.
424
- // no need to unwind.
476
+ // try using unused balance to unwind.
477
+ // no need to unwind.
425
478
  const unusedBalance = await this.getUnusedBalance();
426
479
  logger.verbose(`${this.getTag()}::getVesuMultiplyCall unusedBalance: ${unusedBalance.amount.toString()}, required: ${params.leg1DepositAmount.toString()}`);
427
480
  if (unusedBalance.amount.gte(params.leg1DepositAmount)) {
@@ -432,8 +485,8 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
432
485
  const proofsInfo = adapters.map(adapter => adapter.getProofs(false, this.getMerkleTree()));
433
486
  const calls: Call[] = [];
434
487
  for (const info of proofsInfo) {
435
- const proofGroups = info.proofs;
436
- const call = this.getManageCall(proofGroups, await info.callConstructor({amount: params.leg1DepositAmount}));
488
+ const manageCalls = await info.callConstructor({ amount: params.leg1DepositAmount });
489
+ const call = this.getManageCall(this.getProofGroupsForManageCalls(manageCalls), manageCalls);
437
490
  calls.push(call);
438
491
  }
439
492
  return calls;
@@ -448,8 +501,8 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
448
501
  const proofsInfo = adapters.map(adapter => adapter.getProofs(true, this.getMerkleTree()));
449
502
  const calls: Call[] = [];
450
503
  for (const info of proofsInfo) {
451
- const proofGroups = info.proofs;
452
- const call = this.getManageCall(proofGroups, await info.callConstructor({amount: params.leg1DepositAmount}));
504
+ const manageCalls = await info.callConstructor({ amount: params.leg1DepositAmount });
505
+ const call = this.getManageCall(this.getProofGroupsForManageCalls(manageCalls), manageCalls);
453
506
  calls.push(call);
454
507
  }
455
508
  return calls;
@@ -461,11 +514,11 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
461
514
  // const collateralisation = await vesuAdapter1.getCollateralization(this.config);
462
515
  // const existingCollateralInfo = existingPositions[0];
463
516
  // const existingDebtInfo = existingPositions[1];
464
- // logger.debug(`${this.getTag()}::getVesuMultiplyCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
517
+ // logger.debug(`${this.getTag()}::getVesuMultiplyCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
465
518
  // existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
466
519
 
467
520
  // // - Prices as seen by Vesu contracts, ideal for HF math
468
- // // Price 1 is ok as fallback bcz that would relatively price the
521
+ // // Price 1 is ok as fallback bcz that would relatively price the
469
522
  // // collateral and debt as equal.
470
523
  // const collateralPrice = collateralisation[0].usdValue > 0 ? collateralisation[0].usdValue / existingCollateralInfo.amount.toNumber() : 1;
471
524
  // const debtPrice = collateralisation[1].usdValue > 0 ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber() : 1;
@@ -476,7 +529,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
476
529
 
477
530
  // // compute optimal amount of collateral and debt post addition/removal
478
531
  // // target hf = collateral * collateralPrice * ltv / debt * debtPrice
479
- // // assuming X to be the usd amount of debt borrowed or repaied (negative).
532
+ // // assuming X to be the usd amount of debt borrowed or repaied (negative).
480
533
  // // target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv / (debt * debtPrice + X)
481
534
  // // => X * target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv - (debt * debtPrice * target hf)
482
535
  // // => X * (target hf - ltv)= ((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)
@@ -519,149 +572,166 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
519
572
  return this.metadata.additionalInfo.underlyingToken;
520
573
  }
521
574
 
522
- // async getMaxBorrowableAmount(params: { isAPYComputation: boolean } = { isAPYComputation: false }) {
523
- // const vesuAdapters = this.getVesuAdapters();
524
- // let netMaxBorrowableAmount = Web3Number.fromWei("0", this.getLSTUnderlyingTokenInfo().decimals);
525
- // const maxBorrowables: {amount: Web3Number, dexSwappableAmount: Web3Number, maxBorrowableAmount: Web3Number, borrowableAsset: TokenInfo}[] = [];
526
- // for (const vesuAdapter of vesuAdapters) {
527
- // maxBorrowables.push(await this.getMaxBorrowableAmountByVesuAdapter(vesuAdapter, params.isAPYComputation));
528
- // }
529
- // maxBorrowables.sort((a, b) => b.amount.toNumber() - a.amount.toNumber());
530
- // netMaxBorrowableAmount = maxBorrowables.reduce((acc, curr) => acc.plus(curr.amount), Web3Number.fromWei("0", this.getLSTUnderlyingTokenInfo().decimals));
531
- // return {netMaxBorrowableAmount, maxBorrowables};
532
- // }
575
+ async getLSTAPR(_address: ContractAddr): Promise<number> {
576
+ try {
577
+ const vesuAdapter1 = this.getVesuSameTokenAdapter();
578
+ const apr = await LSTAPRService.getLSTAPR(vesuAdapter1._vesuAdapter.config.debt.address);
579
+ if (!apr) {
580
+ throw new Error('Failed to get LST APR');
581
+ }
582
+ return apr;
583
+ } catch (error) {
584
+ logger.warn(`${this.getTag()}: Failed to get LST APR: ${error}`);
585
+ return 0;
586
+ }
587
+ }
533
588
 
534
- // recursively, using binary search computes max swappable.
535
- // @dev assumes 1 token of from == 1 token of to
536
- // async getMaxSwappableWithMaxSlippage(fromToken: TokenInfo, toToken: TokenInfo, maxSlippage: number, maxAmount: Web3Number) {
537
- // const output = await findMaxInputWithSlippage({
538
- // apiGetOutput: async (inputAmount: number): Promise<number> => {
539
- // const ekuboQuoter = new EkuboQuoter(this.config);
540
- // await new Promise(resolve => setTimeout(resolve, 1000)); // artificial delay, to avoid rate limit
541
- // const quote = await ekuboQuoter.getQuote(fromToken.address.address, toToken.address.address, new Web3Number(inputAmount.toFixed(9), fromToken.decimals));
542
- // return Web3Number.fromWei(quote.total_calculated.toString(), toToken.decimals).toNumber();
543
- // },
544
- // maxInput: maxAmount.toNumber(),
545
- // maxSlippagePercent: maxSlippage,
546
- // tolerance: 0.001,
547
- // referenceRate: 1,
548
- // });
549
- // return new Web3Number(output.optimalInput, fromToken.decimals);
550
- // }
589
+ async netAPY(): Promise<{
590
+ net: number;
591
+ splits: { apy: number; id: string }[];
592
+ }> {
593
+ const unusedBalance = await this.getUnusedBalance();
594
+ const maxDeposits = await this.maxNewDeposits({ isAPYComputation: true });
595
+ const lstAPY = await this.getLSTAPR(this.getLSTUnderlyingTokenInfo().address);
596
+
597
+ if (maxDeposits * 1.5 < unusedBalance.amount.toNumber()) {
598
+ logger.verbose(
599
+ `${this.getTag()}::netAPY: unused balance is > max servicable from loan, lstAPY: ${lstAPY}`,
600
+ );
601
+ const output = await super.netAPY();
602
+ output.splits.push({ apy: lstAPY, id: "lst_apy" });
603
+ return output;
604
+ } else {
605
+ logger.verbose(
606
+ `${this.getTag()}::netAPY: we can take more deposits, use theoretical max APY`,
607
+ );
608
+ const output = await super.netAPY();
609
+ output.splits.push({ apy: lstAPY, id: "lst_apy" });
610
+ return output;
611
+ }
612
+ }
551
613
 
552
- // async getMaxBorrowableAmountByVesuAdapter(vesuAdapter: VesuAdapter, isAPYComputation: boolean) {
553
- // const lstAPY = await this.getLSTAPR(this.getLSTUnderlyingTokenInfo().address);
554
- // const maxInterestRate = lstAPY * 0.8;
555
- // const maxBorrowableAmount = await vesuAdapter.getMaxBorrowableByInterestRate(this.config, vesuAdapter.config.debt, maxInterestRate);
556
- // const debtCap = await vesuAdapter.getDebtCap(this.config);
614
+ async maxNewDeposits(
615
+ params: { isAPYComputation: boolean } = { isAPYComputation: false },
616
+ ) {
617
+ let numerator = 0;
618
+ for (let adapter of this.getVesuAdapters()) {
619
+ const maxDepositInfo = await adapter.maxDeposit();
620
+ const ltv = await adapter._vesuAdapter.getLTVConfig(this.config);
621
+ numerator +=
622
+ (this.metadata.additionalInfo.targetHealthFactor *
623
+ maxDepositInfo.amount.toNumber()) / ltv;
624
+ }
625
+ return numerator;
626
+ }
557
627
 
558
- // const maxBorrowable = maxBorrowableAmount.minimum(debtCap).multipliedBy(0.999);
559
- // // Dont compute precise max swappable for APY computation
560
- // if (vesuAdapter.config.debt.address.eq(this.getLSTUnderlyingTokenInfo().address) || isAPYComputation) {
561
- // return {amount: maxBorrowable, dexSwappableAmount: maxBorrowable, maxBorrowableAmount: maxBorrowable, borrowableAsset: vesuAdapter.config.debt};
562
- // }
563
- // // Want < 0.02% slippage
564
- // try {
565
- // const maxSwappable = await this.getMaxSwappableWithMaxSlippage(vesuAdapter.config.debt, this.getLSTUnderlyingTokenInfo(), 0.0002, maxBorrowable);
566
- // return {amount: maxBorrowable.minimum(maxSwappable), dexSwappableAmount: maxSwappable, maxBorrowableAmount: maxBorrowable, borrowableAsset: vesuAdapter.config.debt};
567
- // } catch (error) {
568
- // logger.warn(`${this.getTag()}: Failed to get max swappable: ${error}`);
569
- // const maxSwappable = Web3Number.fromWei("0", vesuAdapter.config.debt.decimals);
570
- // return {amount: maxBorrowable.minimum(maxSwappable), dexSwappableAmount: maxSwappable, maxBorrowableAmount: maxBorrowable, borrowableAsset: vesuAdapter.config.debt};
571
- // }
572
- // }
628
+ protected async getUnusedBalanceAPY() {
629
+ const unusedBalance = await this.getUnusedBalance();
630
+ const underlying = this.getLSTUnderlyingTokenInfo();
631
+ const lstAPY = await this.getLSTAPR(underlying.address);
632
+ return {
633
+ apy: lstAPY,
634
+ weight: unusedBalance.usdValue,
635
+ };
636
+ }
573
637
 
574
- // todo how much to unwind to get back healthy APY zone again
575
- // if net APY < LST APR + 0.5%, we need to unwind to get back to LST APR + 1% atleast or 0 vesu position
576
- // For xSTRK, simply deposit in Vesu if looping is not viable
577
-
578
- // /**
579
- // * Gets LST APR for the strategy's underlying asset from Endur API
580
- // * @returns Promise<number> The LST APR (not divided by 1e18)
581
- // */
582
- // async getLSTAPR(_address: ContractAddr): Promise<number> {
583
- // try {
584
- // const vesuAdapter1 = this.getVesuSameTokenAdapter();
585
- // const apr = await LSTAPRService.getLSTAPR(vesuAdapter1.config.debt.address);
586
- // if (!apr) {
587
- // throw new Error('Failed to get LST APR');
588
- // }
589
- // return apr;
590
- // } catch (error) {
591
- // logger.warn(`${this.getTag()}: Failed to get LST APR: ${error}`);
592
- // return 0;
593
- // }
594
- // }
638
+ async getAUM(unrealizedAUM: boolean = false): Promise<{net: SingleTokenInfo, prevAum: Web3Number, splits: PositionInfo[]}> {
639
+ const underlying = this.asset();
640
+ assert(underlying.symbol.startsWith('x'), 'Underlying is not an LST of Endur');
595
641
 
596
- // async maxNewDeposits(params: { isAPYComputation: boolean } = { isAPYComputation: false }) {
597
- // const maxBorrowableAmounts = await this.getMaxBorrowableAmount(params);
598
-
599
- // let ltv: number | undefined = undefined;
600
- // for (let adapter of this.getVesuAdapters()) {
601
- // const maxBorrowableAmount = maxBorrowableAmounts.maxBorrowables.find(b => b.borrowableAsset.address.eq(adapter.config.debt.address))?.amount;
602
- // if (!maxBorrowableAmount) {
603
- // throw new Error(`Max borrowable amount not found for adapter: ${adapter.config.debt.symbol}`);
604
- // }
605
- // const maxLTV = await adapter.getLTVConfig(this.config);
606
- // if (!ltv) {
607
- // ltv = maxLTV;
608
- // } else if (ltv != maxLTV) {
609
- // throw new Error(`LTV mismatch for adapter: ${adapter.config.debt.symbol}`);
610
- // }
611
- // }
612
- // if (!ltv) {
613
- // throw new Error('LTV not found');
614
- // }
615
- // // for simplicity, we assume 1 underlying = 1 LST
616
- // const numerator = this.metadata.additionalInfo.targetHealthFactor * maxBorrowableAmounts.netMaxBorrowableAmount.toNumber() / (ltv)
617
- // return numerator - maxBorrowableAmounts.netMaxBorrowableAmount.toNumber();
618
- // }
642
+ const priceType = unrealizedAUM ? LSTPriceType.ENDUR_PRICE : LSTPriceType.AVNU_PRICE;
643
+
644
+ let tokenUnderlyingPrice: number;
645
+ if (priceType === LSTPriceType.ENDUR_PRICE) {
646
+ tokenUnderlyingPrice = await this.getLSTExchangeRate();
647
+ if (tokenUnderlyingPrice === 0) {
648
+ throw new Error(`${this.getTag()}::getAUM: tokenUnderlyingPrice (Endur) is 0`);
649
+ }
650
+ const avnuRate = await this.getLSTAvnuRate();
651
+ if (avnuRate === 0) {
652
+ throw new Error(`${this.getTag()}::getAUM: tokenUnderlyingPrice (Avnu) is 0`);
653
+ }
654
+ const diff = Math.abs(tokenUnderlyingPrice - avnuRate) / tokenUnderlyingPrice;
655
+ if (diff > 0.02) {
656
+ throw new Error(
657
+ `${this.getTag()}::getAUM: Endur and Avnu prices differ by more than 2% (Endur: ${tokenUnderlyingPrice}, Avnu: ${avnuRate})`,
658
+ );
659
+ }
660
+ logger.verbose(`${this.getTag()} tokenUnderlyingPrice (Endur): ${tokenUnderlyingPrice}, avnuRate: ${avnuRate}, diff: ${diff}`);
661
+ } else {
662
+ tokenUnderlyingPrice = await this.getLSTAvnuRate();
663
+ if (tokenUnderlyingPrice === 0) {
664
+ throw new Error(`${this.getTag()}::getAUM: tokenUnderlyingPrice (Avnu) is 0`);
665
+ }
666
+ logger.verbose(`${this.getTag()} tokenUnderlyingPrice (Avnu): ${tokenUnderlyingPrice}`);
667
+ }
619
668
 
620
- async getAUM(): Promise<{net: SingleTokenInfo, prevAum: Web3Number, splits: PositionInfo[]}> {
621
669
  const allPositions: PositionInfo[] = [];
622
670
  for (let adapter of this.metadata.additionalInfo.adapters) {
623
671
  const positions = await adapter.adapter.getPositions();
624
672
  allPositions.push(...positions);
625
673
  }
626
674
 
627
- const assetPrice = await this.pricer.getPrice(this.asset().symbol);
628
- let netAUM = new Web3Number(0, this.asset().decimals);
675
+ let netAUM = new Web3Number(0, underlying.decimals);
629
676
  for (let position of allPositions) {
630
- if (position.tokenInfo.address.eq(this.asset().address)) {
677
+ if (position.tokenInfo.address.eq(underlying.address)) {
631
678
  netAUM = netAUM.plus(position.amount);
632
679
  } else {
633
- netAUM = netAUM.plus(position.usdValue / assetPrice.price);
680
+ netAUM = netAUM.plus(position.amount.dividedBy(tokenUnderlyingPrice));
634
681
  }
635
682
  }
636
683
 
684
+ const assetPrice = await this.pricer.getPrice(underlying.symbol);
637
685
  const prevAum = await this.getPrevAUM();
686
+ const priceTypeLabel = priceType === LSTPriceType.ENDUR_PRICE ? "Endur Price" : "Avnu Price";
687
+ logger.verbose(`${this.getTag()} AUM (${priceTypeLabel}): ${netAUM}`);
688
+
638
689
  const realAUM: PositionInfo = {
639
- tokenInfo: this.asset(),
690
+ tokenInfo: underlying,
640
691
  amount: netAUM,
641
692
  usdValue: netAUM.toNumber() * assetPrice.price,
642
693
  apy: {apy: netAUM.toNumber() * assetPrice.price, type: APYType.BASE},
643
694
  remarks: AUMTypes.FINALISED,
644
- protocol: Protocols.NONE // just placeholder
695
+ protocol: Protocols.NONE
645
696
  };
646
697
 
647
698
  const estimatedAUMDelta: PositionInfo = {
648
- tokenInfo: this.asset(),
649
- amount: Web3Number.fromWei('0', this.asset().decimals),
699
+ tokenInfo: underlying,
700
+ amount: Web3Number.fromWei('0', underlying.decimals),
650
701
  usdValue: 0,
651
702
  apy: {apy: 0, type: APYType.BASE},
652
703
  remarks: AUMTypes.DEFISPRING,
653
- protocol: Protocols.NONE // just placeholder
704
+ protocol: Protocols.NONE
654
705
  };
655
706
 
656
707
  return {net: {
657
- tokenInfo: this.asset(),
708
+ tokenInfo: underlying,
658
709
  amount: netAUM,
659
710
  usdValue: netAUM.toNumber() * assetPrice.price
660
711
  }, prevAum: prevAum, splits: [realAUM, estimatedAUMDelta]};
661
712
  }
662
713
 
714
+ async getTVLUnrealized() {
715
+ return await this.getAUM(true);
716
+ }
717
+
718
+ async getUserUnrealizedGains(user: ContractAddr) {
719
+ const tvl = await this.getTVL();
720
+ const unrealizedTVL = await this.getTVLUnrealized();
721
+ const unrealizedDiff = unrealizedTVL.net.amount.minus(tvl.amount);
722
+ const userTVL = await this.getUserTVL(user);
723
+ const userShare = userTVL.amount.dividedBy(tvl.amount);
724
+ const unrealizedGains = unrealizedDiff.multipliedBy(userShare);
725
+
726
+ return {
727
+ unrealizedGains,
728
+ userShare: userShare.toNumber(),
729
+ tokenInfo: this.asset(),
730
+ };
731
+ }
732
+
663
733
  /**
664
- *
734
+ *
665
735
  * @param params marginAmount is in LST, debtAmount is in underlying
666
736
  */
667
737
  // async getModifyLeverCall(params: {
@@ -671,7 +741,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
671
741
  // isIncrease: boolean
672
742
  // }): Promise<Call[]> {
673
743
  // logger.verbose(`${this.getTag()}::getModifyLeverCall marginAmount: ${params.marginAmount}, debtAmount: ${params.debtAmount}, lstDexPriceInUnderlying: ${params.lstDexPriceInUnderlying}, isIncrease: ${params.isIncrease}`);
674
-
744
+
675
745
  // const vesuAdapter = this.getVesuSameTokenAdapter();
676
746
  // const manage0Info = vesuAdapter.
677
747
  // const manageCall0 = manage0Info.callConstructor({
@@ -689,7 +759,6 @@ export default function VaultDescription(
689
759
  const containerStyle = {
690
760
  maxWidth: "800px",
691
761
  margin: "0 auto",
692
- backgroundColor: "#111",
693
762
  color: "#eee",
694
763
  fontFamily: "Arial, sans-serif",
695
764
  borderRadius: "12px",
@@ -716,7 +785,7 @@ export default function VaultDescription(
716
785
  </div>
717
786
  <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
718
787
  <p style={{ fontSize: "13px", color: "#ccc" }}>
719
- <strong>Debt limits:</strong> Pools on Vesu have debt caps that are gradually increased over time. Until caps are raised, deposited Tokens remain in the vault, generating a shared net return for all depositors. There is no additional fee taken by Troves on Yield token's APY, its only on added gain.
788
+ <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.
720
789
  </p>
721
790
  </div>
722
791
  {/* <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
@@ -782,21 +851,25 @@ function getLooperSettings(
782
851
  poolId: pool1,
783
852
  collateral: lstToken,
784
853
  debt: Global.getDefaultTokens().find(token => token.symbol === position)!,
854
+ marginToken: lstToken,
785
855
  targetHealthFactor: vaultSettings.targetHealthFactor,
786
856
  minHealthFactor: vaultSettings.minHealthFactor,
787
857
  quoteAmountToFetchPrice: vaultSettings.quoteAmountToFetchPrice,
788
858
  ...baseAdapterConfig,
789
859
  supportedPositions: [{asset: lstToken, isDebt: false}, {asset: Global.getDefaultTokens().find(token => token.symbol === position)!, isDebt: true}],
790
- minimumVesuMovementAmount: 0
860
+ minimumVesuMovementAmount: 0
791
861
  }));
792
862
 
793
- const unusedBalanceAdapter = new UnusedBalanceAdapter({
863
+ // Tracks LST balance in the vault allocator; fromAddress=vaultAllocator, toAddress=vaultAddress
864
+ const lstTransferAdapter = new TokenTransferAdapter({
794
865
  ...baseAdapterConfig,
866
+ fromAddress: vaultSettings.vaultAllocator,
867
+ toAddress: vaultSettings.vaultAddress,
795
868
  });
796
869
 
797
870
  vaultSettings.adapters.push({id: `${vesuAdapterLST.name}_${lstToken.symbol}_${underlyingToken.symbol}`, adapter: vesuAdapterLST});
798
871
  vesuMultiplyAdapters.map(adapter => vaultSettings.adapters.push({id: `${adapter.name}_${lstToken.symbol}_${underlyingToken.symbol}`, adapter: adapter}));
799
- vaultSettings.adapters.push({id: `${unusedBalanceAdapter.name}_${lstToken.symbol}`, adapter: unusedBalanceAdapter});
872
+ vaultSettings.adapters.push({id: `${lstTransferAdapter.name}_${lstToken.symbol}`, adapter: lstTransferAdapter});
800
873
 
801
874
  const commonAdapter = new CommonAdapter({
802
875
  id: UNIVERSAL_MANAGE_IDS.FLASH_LOAN,
@@ -813,11 +886,11 @@ function getLooperSettings(
813
886
  // push vesu multiply adapter to leaf adapters
814
887
  vesuMultiplyAdapters.map(adapter => vaultSettings.leafAdapters.push(() => adapter.getDepositLeaf()));
815
888
  vesuMultiplyAdapters.map(adapter => vaultSettings.leafAdapters.push(() => adapter.getWithdrawLeaf()));
816
-
889
+
817
890
  // to bridge liquidity back to vault (used by bring_liquidity)
818
891
  vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(lstToken.address, vaultSettings.vaultAddress, UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY).bind(commonAdapter));
819
892
  vaultSettings.leafAdapters.push(commonAdapter.getBringLiquidityAdapter(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY).bind(commonAdapter));
820
-
893
+
821
894
  // claim rewards
822
895
  // vaultSettings.leafAdapters.push(vesuAdapterLST.getDefispringRewardsAdapter(UNIVERSAL_MANAGE_IDS.DEFISPRING_REWARDS).bind(vesuAdapterLST));
823
896
 
@@ -922,9 +995,10 @@ const hyperxWBTC: HyperLSTStrategySettings = {
922
995
  adapters: [],
923
996
  targetHealthFactor: 1.1,
924
997
  minHealthFactor: 1.05,
925
- borrowable_assets: borrowableAssets.map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
998
+ borrowable_assets: Global.getDefaultTokens().filter(token => token.symbol === 'WBTC'),
926
999
  underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!,
927
1000
  quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!.decimals),
1001
+ redemptionRouter: ContractAddr.from('0x6ea649f402898f69baf775c1afdd08522c071c640b9c4460192070ec2b96417'),
928
1002
  }
929
1003
 
930
1004
  const hyperxtBTC: HyperLSTStrategySettings = {
@@ -937,9 +1011,10 @@ const hyperxtBTC: HyperLSTStrategySettings = {
937
1011
  adapters: [],
938
1012
  targetHealthFactor: 1.1,
939
1013
  minHealthFactor: 1.05,
940
- borrowable_assets: borrowableAssets.map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
1014
+ borrowable_assets: Global.getDefaultTokens().filter(token => token.symbol === 'tBTC' || token.symbol === 'WBTC'),
941
1015
  underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'tBTC')!,
942
1016
  quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'tBTC')!.decimals),
1017
+ redemptionRouter: ContractAddr.from('0x3de9c409d1e357e25778fb7a3e2e2393666956846a5c2caa607296fa8e76b5d'),
943
1018
  }
944
1019
 
945
1020
  const hyperxsBTC: HyperLSTStrategySettings = {
@@ -952,7 +1027,7 @@ const hyperxsBTC: HyperLSTStrategySettings = {
952
1027
  adapters: [],
953
1028
  targetHealthFactor: 1.1,
954
1029
  minHealthFactor: 1.05,
955
- borrowable_assets: borrowableAssets.map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
1030
+ borrowable_assets: Global.getDefaultTokens().filter(token => token.symbol === 'solvBTC'),
956
1031
  underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'solvBTC')!,
957
1032
  quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'solvBTC')!.decimals),
958
1033
  }
@@ -967,7 +1042,7 @@ const hyperxLBTC: HyperLSTStrategySettings = {
967
1042
  adapters: [],
968
1043
  targetHealthFactor: 1.1,
969
1044
  minHealthFactor: 1.05,
970
- borrowable_assets: borrowableAssets.map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
1045
+ borrowable_assets: Global.getDefaultTokens().filter(token => token.symbol === 'LBTC'),
971
1046
  underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'LBTC')!,
972
1047
  quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'LBTC')!.decimals),
973
1048
  }
@@ -1012,35 +1087,152 @@ export function getInvestmentSteps(lstSymbol: string, underlyingSymbol: string)
1012
1087
  `The vault manager loops the ${underlyingSymbol} to buy ${lstSymbol}`,
1013
1088
  `The vault manager collateralizes the ${lstSymbol} on Vesu`,
1014
1089
  `The vault manager borrows more ${underlyingSymbol} to loop further`,
1015
- `If required, adjust leverage or re-allocate assets within pool on Vesu to optimize yield`
1090
+ `If required, adjust leverage or re-allocate assets within LST pool on Vesu to optimize yield`
1016
1091
  ]
1017
1092
  }
1018
1093
 
1019
- function getStrategySettings(lstSymbol: string, underlyingSymbol: string, addresses: HyperLSTStrategySettings, isPreview: boolean = false, isLST: boolean): IStrategyMetadata<HyperLSTStrategySettings> {
1094
+ function getMaxTVL(lstSymbol: string): Web3Number {
1095
+ const lstMaxTVLs: Record<string, number> = {
1096
+ xWBTC: 5,
1097
+ xLBTC: 5,
1098
+ xtBTC: 5,
1099
+ xsBTC: 5,
1100
+ xSTRK: 7000000,
1101
+ };
1102
+ const maxTVLValue = lstMaxTVLs[lstSymbol] || 0;
1103
+ const token = Global.getDefaultTokens().find(
1104
+ (token) => token.symbol === lstSymbol,
1105
+ );
1106
+ if (!token) {
1107
+ return Web3Number.fromWei(0, 18);
1108
+ }
1109
+ return Web3Number.fromWei(maxTVLValue, token.decimals);
1110
+ }
1111
+
1112
+ function createHyperLSTSettings(
1113
+ lstSymbol: string,
1114
+ underlyingSymbol: string,
1115
+ ): StrategySettings {
1116
+ const depositToken = Global.getDefaultTokens().find(
1117
+ (token) => token.symbol === lstSymbol,
1118
+ )!;
1119
+ return {
1120
+ maxTVL: getMaxTVL(lstSymbol),
1121
+ isPaused: false,
1122
+ liveStatus: StrategyLiveStatus.HOT,
1123
+ isAudited: true,
1124
+ isInstantWithdrawal: false,
1125
+ hideHarvestInfo: true,
1126
+ quoteToken: depositToken,
1127
+ showWithdrawalWarningModal: false,
1128
+ alerts: [
1129
+ {
1130
+ tab: "withdraw" as const,
1131
+ 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.",
1132
+ type: "info" as const,
1133
+ },
1134
+ {
1135
+ tab: "deposit" as const,
1136
+ text: (
1137
+ <>
1138
+ To acquire the LST, please visit{" "}
1139
+ <a href="https://app.endur.fi" target="_blank" rel="noopener noreferrer">endur.fi</a>
1140
+ </>
1141
+ ),
1142
+ type: "info" as const,
1143
+ },
1144
+ {
1145
+ tab: "deposit" as const,
1146
+ 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.",
1147
+ type: "info" as const,
1148
+ },
1149
+ ],
1150
+ };
1151
+ }
1152
+
1153
+ const HYPER_LST_SECURITY = {
1154
+ auditStatus: AuditStatus.AUDITED,
1155
+ sourceCode: {
1156
+ type: SourceCodeType.CLOSED_SOURCE,
1157
+ contractLink: "https://github.com/trovesfi/troves-contracts",
1158
+ },
1159
+ accessControl: {
1160
+ type: AccessControlType.STANDARD_ACCOUNT,
1161
+ addresses: [ContractAddr.from("0x0")],
1162
+ timeLock: "2 Days",
1163
+ },
1164
+ };
1165
+
1166
+ const HYPER_LST_REDEMPTION_INFO: RedemptionInfo = {
1167
+ instantWithdrawalVault: InstantWithdrawalVault.NO,
1168
+ redemptionsInfo: [
1169
+ {
1170
+ title: "Typical Duration",
1171
+ description: "1-2 hours",
1172
+ },
1173
+ ],
1174
+ alerts: [
1175
+ {
1176
+ type: "info",
1177
+ 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.",
1178
+ tab: "withdraw",
1179
+ },
1180
+ ],
1181
+ };
1182
+
1183
+ function getStrategySettings(
1184
+ lstSymbol: string,
1185
+ underlyingSymbol: string,
1186
+ settings: HyperLSTStrategySettings,
1187
+ isPreview: boolean = false,
1188
+ isLST: boolean,
1189
+ ): IStrategyMetadata<HyperLSTStrategySettings> {
1020
1190
  return {
1191
+ id: `hyper_${lstSymbol.toLowerCase()}`,
1021
1192
  name: `Hyper ${lstSymbol}`,
1022
1193
  description: getDescription(lstSymbol, underlyingSymbol),
1023
- address: addresses.vaultAddress,
1194
+ address: settings.vaultAddress,
1024
1195
  launchBlock: 0,
1025
1196
  type: 'Other',
1197
+ vaultType: {
1198
+ type: VaultType.LOOPING,
1199
+ description: `Creates leveraged looping position on ${lstSymbol} by borrowing ${underlyingSymbol} to increase yield`,
1200
+ },
1026
1201
  depositTokens: [Global.getDefaultTokens().find(token => token.symbol === lstSymbol)!],
1027
- additionalInfo: getLooperSettings(lstSymbol, underlyingSymbol, addresses, lstSymbol === 'xSTRK' ? VesuPools.Re7xSTRK : VesuPools.Re7xBTC),
1202
+ additionalInfo: getLooperSettings(lstSymbol, underlyingSymbol, settings, lstSymbol === 'xSTRK' ? VesuPools.Re7xSTRK : VesuPools.Re7xBTC),
1028
1203
  risk: {
1029
1204
  riskFactor: _riskFactor,
1030
1205
  netRisk:
1031
1206
  _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) /
1032
1207
  _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
1033
- notARisks: getNoRiskTags(_riskFactor)
1208
+ notARisks: getNoRiskTags(_riskFactor),
1034
1209
  },
1035
1210
  auditUrl: AUDIT_URL,
1036
1211
  protocols: [Protocols.ENDUR, Protocols.VESU],
1037
- maxTVL: Web3Number.fromWei(0, 18),
1038
- contractDetails: getContractDetails(addresses),
1212
+ curator: {
1213
+ name: "Unwrap Labs",
1214
+ logo: "https://assets.troves.fi/integrations/unwraplabs/white.png",
1215
+ },
1216
+ settings: createHyperLSTSettings(lstSymbol, underlyingSymbol),
1217
+ contractDetails: getContractDetails(settings),
1039
1218
  faqs: getFAQs(lstSymbol, underlyingSymbol, isLST),
1040
1219
  investmentSteps: getInvestmentSteps(lstSymbol, underlyingSymbol),
1041
1220
  isPreview: isPreview,
1042
- apyMethodology: isLST ? '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.' : 'Current annualized APY in terms of base asset of the Yield Token. There is no additional fee taken by Troves on yield token APY. We charge a 10% performance fee on the additional gain which is already accounted in the APY shown.'
1043
- }
1221
+ apyMethodology: 'Current annualized APY in terms of base asset of the LST. There is no additional fee taken by Troves on LST APY. We charge a 10% performance fee on the additional gain which is already accounted in the APY shown.',
1222
+ realizedAPYMethodology: 'The realizedAPY is based on past 14 days performance by the vault',
1223
+ tags: lstSymbol.includes('BTC')
1224
+ ? [StrategyTag.BTC, StrategyTag.LEVERED]
1225
+ : [StrategyTag.LEVERED],
1226
+ security: HYPER_LST_SECURITY,
1227
+ redemptionInfo: HYPER_LST_REDEMPTION_INFO,
1228
+ usualTimeToEarnings: '2 weeks',
1229
+ usualTimeToEarningsDescription: 'Strategy returns depend on LST price on DEXes. Even though the true price of LST on Endur increases continuously, the DEX price may lag sometimes, and historically is seen to rebase at least once every 2 hours. This is when you realise your earnings.',
1230
+ points: lstSymbol === 'xSTRK' ? [{
1231
+ multiplier: 4,
1232
+ logo: "https://endur.fi/favicon.ico",
1233
+ toolTip: "This strategy holds xSTRK. Earn 3-4x Endur points on your xSTRK due to the leverage. Points can be found on endur.fi.",
1234
+ }] : undefined,
1235
+ };
1044
1236
  }
1045
1237
 
1046
1238