@strkfarm/sdk 2.0.0-dev.4 → 2.0.0-dev.40

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 (77) hide show
  1. package/dist/cli.js +190 -36
  2. package/dist/cli.mjs +188 -34
  3. package/dist/index.browser.global.js +116018 -90768
  4. package/dist/index.browser.mjs +12769 -10876
  5. package/dist/index.d.ts +2222 -1947
  6. package/dist/index.js +13171 -11077
  7. package/dist/index.mjs +13076 -11004
  8. package/package.json +3 -3
  9. package/src/data/avnu.abi.json +840 -0
  10. package/src/data/ekubo-price-fethcer.abi.json +265 -0
  11. package/src/data/redeem-request-nft.abi.json +752 -0
  12. package/src/data/universal-vault.abi.json +8 -7
  13. package/src/dataTypes/_bignumber.ts +13 -4
  14. package/src/dataTypes/bignumber.browser.ts +10 -1
  15. package/src/dataTypes/bignumber.node.ts +10 -1
  16. package/src/dataTypes/index.ts +3 -2
  17. package/src/dataTypes/mynumber.ts +141 -0
  18. package/src/global.ts +96 -41
  19. package/src/index.browser.ts +2 -1
  20. package/src/interfaces/common.tsx +212 -5
  21. package/src/modules/apollo-client-config.ts +28 -0
  22. package/src/modules/avnu.ts +21 -12
  23. package/src/modules/ekubo-pricer.ts +79 -0
  24. package/src/modules/ekubo-quoter.ts +48 -30
  25. package/src/modules/erc20.ts +17 -0
  26. package/src/modules/harvests.ts +43 -29
  27. package/src/modules/index.ts +1 -1
  28. package/src/modules/pragma.ts +23 -8
  29. package/src/modules/pricer-from-api.ts +156 -15
  30. package/src/modules/pricer-lst.ts +1 -1
  31. package/src/modules/pricer.ts +40 -4
  32. package/src/modules/pricerBase.ts +2 -1
  33. package/src/node/deployer.ts +36 -1
  34. package/src/node/pricer-redis.ts +2 -1
  35. package/src/strategies/base-strategy.ts +168 -16
  36. package/src/strategies/constants.ts +8 -3
  37. package/src/strategies/ekubo-cl-vault.tsx +1044 -351
  38. package/src/strategies/factory.ts +199 -0
  39. package/src/strategies/index.ts +5 -3
  40. package/src/strategies/registry.ts +262 -0
  41. package/src/strategies/sensei.ts +353 -9
  42. package/src/strategies/svk-strategy.ts +125 -30
  43. package/src/strategies/token-boosted-xstrk-carry-strategy.tsx +1225 -0
  44. package/src/strategies/types.ts +4 -0
  45. package/src/strategies/universal-adapters/adapter-utils.ts +4 -1
  46. package/src/strategies/universal-adapters/avnu-adapter.ts +196 -272
  47. package/src/strategies/universal-adapters/baseAdapter.ts +263 -251
  48. package/src/strategies/universal-adapters/common-adapter.ts +206 -203
  49. package/src/strategies/universal-adapters/index.ts +10 -8
  50. package/src/strategies/universal-adapters/svk-troves-adapter.ts +511 -0
  51. package/src/strategies/universal-adapters/token-transfer-adapter.ts +200 -0
  52. package/src/strategies/universal-adapters/vesu-adapter.ts +120 -82
  53. package/src/strategies/universal-adapters/vesu-modify-position-adapter.ts +525 -0
  54. package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +1098 -712
  55. package/src/strategies/universal-adapters/vesu-position-common.ts +258 -0
  56. package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +18 -3
  57. package/src/strategies/universal-lst-muliplier-strategy.tsx +551 -405
  58. package/src/strategies/universal-strategy.tsx +1487 -1173
  59. package/src/strategies/vesu-rebalance.tsx +252 -152
  60. package/src/strategies/yoloVault.ts +1084 -0
  61. package/src/utils/cacheClass.ts +11 -2
  62. package/src/utils/health-factor-math.ts +33 -1
  63. package/src/utils/index.ts +3 -1
  64. package/src/utils/logger.browser.ts +22 -4
  65. package/src/utils/logger.node.ts +259 -24
  66. package/src/utils/starknet-call-parser.ts +1036 -0
  67. package/src/utils/strategy-utils.ts +61 -0
  68. package/src/modules/ExtendedWrapperSDk/index.ts +0 -62
  69. package/src/modules/ExtendedWrapperSDk/types.ts +0 -311
  70. package/src/modules/ExtendedWrapperSDk/wrapper.ts +0 -395
  71. package/src/strategies/universal-adapters/extended-adapter.ts +0 -661
  72. package/src/strategies/universal-adapters/unused-balance-adapter.ts +0 -109
  73. package/src/strategies/vesu-extended-strategy/services/operationService.ts +0 -34
  74. package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +0 -77
  75. package/src/strategies/vesu-extended-strategy/utils/constants.ts +0 -49
  76. package/src/strategies/vesu-extended-strategy/utils/helper.ts +0 -372
  77. package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +0 -1140
@@ -1,51 +1,74 @@
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, VesuModifyPositionAdapter, 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 { MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP } from "./vesu-extended-strategy/utils/constants";
17
- import { VesuExtendedStrategySettings } from "./vesu-extended-strategy/vesu-extended-strategy";
16
+ import { LSTPriceType } from "./types";
18
17
 
19
18
  export interface HyperLSTStrategySettings extends UniversalStrategySettings {
20
- borrowable_assets: TokenInfo[];
19
+ borrowable_assets: { tokenInfo: TokenInfo, poolId: ContractAddr }[];
21
20
  underlyingToken: TokenInfo;
22
21
  quoteAmountToFetchPrice: Web3Number;
23
22
  targetHealthFactor: number;
24
23
  minHealthFactor: number;
25
24
  aumOracle: ContractAddr;
25
+ adapterIds?: {
26
+ /** VesuMultiplyAdapter for underlying-matched debt (first matching borrowable pool) */
27
+ primaryMultiply: string;
28
+ multiply: Record<string, string>;
29
+ modify: Record<string, string>;
30
+ };
31
+ }
32
+
33
+ function getHyperLSTAdapterKey(debtSymbol: string, poolId: ContractAddr): string {
34
+ return `${debtSymbol.toLowerCase()}_${poolId.shortString()}`;
35
+ }
36
+
37
+ function getHyperLSTMultiplyAdapterId(
38
+ lstSymbol: string,
39
+ debtSymbol: string,
40
+ poolId: ContractAddr,
41
+ ): string {
42
+ return `vesu_multiply_${lstSymbol.toLowerCase()}_${getHyperLSTAdapterKey(debtSymbol, poolId)}`;
43
+ }
44
+
45
+ function getHyperLSTModifyAdapterId(
46
+ lstSymbol: string,
47
+ debtSymbol: string,
48
+ poolId: ContractAddr,
49
+ ): string {
50
+ return `vesu_modify_${lstSymbol.toLowerCase()}_${getHyperLSTAdapterKey(debtSymbol, poolId)}`;
26
51
  }
27
52
 
28
53
  export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings> extends SVKStrategy<S> {
29
54
 
30
55
  private quoteAmountToFetchPrice = new Web3Number(1, 18);
31
-
56
+
32
57
  constructor(config: IConfig, pricer: PricerBase, metadata: IStrategyMetadata<S>) {
33
58
  super(config, pricer, metadata);
34
59
  const STRKToken = Global.getDefaultTokens().find(token => token.symbol === 'STRK')!;
35
60
  const underlyingToken = this.getLSTUnderlyingTokenInfo();
36
- if (underlyingToken.address.eq(STRKToken.address)) {
37
- this.quoteAmountToFetchPrice = new Web3Number(100, 18);
38
- } else {
39
- // else this BTC
40
- this.quoteAmountToFetchPrice = new Web3Number(0.01, this.asset().decimals);
61
+ if (metadata.additionalInfo.quoteAmountToFetchPrice.isZero()) {
62
+ throw new Error(`${this.getTag()}::quoteAmountToFetchPrice is zero`);
41
63
  }
64
+ this.quoteAmountToFetchPrice = metadata.additionalInfo.quoteAmountToFetchPrice;
42
65
 
43
66
  this.metadata.additionalInfo.adapters.forEach(adapter => {
44
67
  adapter.adapter.config.networkConfig = this.config;
45
68
  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;
69
+ if ((adapter.adapter as any)._vesuAdapter) {
70
+ (adapter.adapter as any)._vesuAdapter.networkConfig = this.config;
71
+ (adapter.adapter as any)._vesuAdapter.pricer = this.pricer;
49
72
  }
50
73
  });
51
74
  }
@@ -54,13 +77,36 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
54
77
  return `${UniversalLstMultiplierStrategy.name}:${this.metadata.name}`;
55
78
  }
56
79
 
57
- // Vesu adapter with LST and base token match
58
- getVesuSameTokenAdapter() {
59
- const vesuMultipleAdapters = this.getVesuAdapters();
60
- console.log(vesuMultipleAdapters.map(adapter => adapter.config.debt.symbol));
61
- const baseAdapter = vesuMultipleAdapters.find((adapter) => {
62
- return adapter.config.debt.address.eq(this.metadata.additionalInfo.underlyingToken.address);
63
- })
80
+ private getAdapterById<T>(id: string): T {
81
+ const entry = this.metadata.additionalInfo.adapters.find((a) => a.id === id);
82
+ if (!entry) {
83
+ throw new Error(`${this.getTag()}::getAdapterById: adapter not found for id "${id}"`);
84
+ }
85
+ return entry.adapter as T;
86
+ }
87
+
88
+ getVesuMultiplyAdapterByKey(key: string): VesuMultiplyAdapter {
89
+ const id = this.metadata.additionalInfo.adapterIds?.multiply[key];
90
+ if (!id) {
91
+ throw new Error(`${this.getTag()}::getVesuMultiplyAdapterByKey: unknown key "${key}"`);
92
+ }
93
+ return this.getAdapterById<VesuMultiplyAdapter>(id);
94
+ }
95
+
96
+ // Vesu multiply adapter whose debt matches the LST underlying (e.g. STRK for xSTRK)
97
+ getVesuSameTokenAdapter(): VesuMultiplyAdapter {
98
+ const primaryId = this.metadata.additionalInfo.adapterIds?.primaryMultiply;
99
+ if (primaryId) {
100
+ const adapter = this.getAdapterById<VesuMultiplyAdapter>(primaryId);
101
+ adapter.config.networkConfig = this.config;
102
+ adapter.config.pricer = this.pricer;
103
+ adapter._vesuAdapter.networkConfig = this.config;
104
+ adapter._vesuAdapter.pricer = this.pricer;
105
+ return adapter;
106
+ }
107
+ const baseAdapter = this.getVesuMultiplyAdapters().find((adapter) =>
108
+ adapter.config.debt.address.eq(this.metadata.additionalInfo.underlyingToken.address),
109
+ );
64
110
  if (!baseAdapter) {
65
111
  throw new Error(`${this.getTag()}::getVesuSameTokenAdapter: base adapter not found`);
66
112
  }
@@ -71,7 +117,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
71
117
 
72
118
  // only one leg is used
73
119
  // todo support lending assets of underlying as well (like if xSTRK looping is not viable, simply supply STRK)
74
- getVesuAdapters() {
120
+ getVesuMultiplyAdapters() {
75
121
  const vesuMultipleAdapters = this.metadata.additionalInfo.adapters
76
122
  .filter(adapter => adapter.adapter.name === VesuMultiplyAdapter.name)
77
123
  .map(adapter => adapter.adapter) as VesuMultiplyAdapter[]
@@ -83,6 +129,82 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
83
129
  return vesuMultipleAdapters;
84
130
  }
85
131
 
132
+ async getRewardsAUM(_prevAum: Web3Number) {
133
+ const lstToken = this.asset();
134
+ // Defi spring rewards handling; currently returns 0 as SVKStrategy has no built-in rewards logic
135
+ return Web3Number.fromWei("0", lstToken.decimals);
136
+ }
137
+
138
+ async getLSTAvnuRate() {
139
+ const vesuAdapter1 = this.getVesuSameTokenAdapter();
140
+ const lstTokenInfo = vesuAdapter1._vesuAdapter.config.collateral;
141
+ const underlyingTokenInfo = vesuAdapter1._vesuAdapter.config.debt;
142
+
143
+ const avnuModule = new AvnuWrapper();
144
+
145
+ const sellAmount = (lstTokenInfo as any).priceCheckAmount
146
+ ? new Web3Number(
147
+ (lstTokenInfo as any).priceCheckAmount,
148
+ underlyingTokenInfo.decimals,
149
+ )
150
+ : new Web3Number(1, underlyingTokenInfo.decimals);
151
+
152
+ const quote = await avnuModule.getQuotes(
153
+ underlyingTokenInfo.address.address,
154
+ lstTokenInfo.address.address,
155
+ sellAmount.toWei(),
156
+ this.metadata.additionalInfo.vaultAllocator.address,
157
+ );
158
+
159
+ const underlyingAmountNumber = sellAmount.toNumber();
160
+ const lstAmountNumber = Web3Number.fromWei(
161
+ quote.buyAmount.toString(),
162
+ lstTokenInfo.decimals,
163
+ ).toNumber();
164
+
165
+ assert(lstAmountNumber > 0, "Avnu LST amount is zero");
166
+
167
+ const exchangeRate = underlyingAmountNumber / lstAmountNumber;
168
+ logger.verbose(
169
+ `${this.getTag()}:: LST Avnu Exchange Rate: ${exchangeRate}`,
170
+ );
171
+ return exchangeRate;
172
+ }
173
+
174
+ async getLSTExchangeRate() {
175
+ const vesuAdapter1 = this.getVesuSameTokenAdapter();
176
+ const lstTokenInfo = vesuAdapter1._vesuAdapter.config.collateral;
177
+ const lstABI = new Contract({
178
+ abi: ERC4626Abi,
179
+ address: lstTokenInfo.address.address,
180
+ providerOrAccount: this.config.provider,
181
+ });
182
+
183
+ const price: any = await lstABI.call("convert_to_assets", [
184
+ uint256.bnToUint256(new Web3Number(1, lstTokenInfo.decimals).toWei()),
185
+ ]);
186
+ const exchangeRate =
187
+ Number(uint256.uint256ToBN(price).toString()) /
188
+ Math.pow(10, lstTokenInfo.decimals);
189
+ logger.verbose(`${this.getTag()}:: LST Exchange Rate: ${exchangeRate}`);
190
+ return exchangeRate;
191
+ }
192
+
193
+ private async _getMinOutputAmountLSTBuy(amountInUnderlying: Web3Number) {
194
+ const lstTruePrice = await this.getLSTExchangeRate();
195
+ const minOutputAmount = amountInUnderlying
196
+ .dividedBy(lstTruePrice)
197
+ .multipliedBy(0.99979);
198
+ return new Web3Number(minOutputAmount.toString(), this.asset().decimals);
199
+ }
200
+
201
+ private async _getMinOutputAmountLSTSell(amountInLST: Web3Number) {
202
+ const lstTruePrice = await this.getLSTExchangeRate();
203
+ const minOutputAmount = amountInLST
204
+ .multipliedBy(lstTruePrice)
205
+ .multipliedBy(0.995);
206
+ return minOutputAmount;
207
+ }
86
208
 
87
209
  // async getAvnuSwapMultiplyCall(params: {
88
210
  // isDeposit: boolean,
@@ -132,7 +254,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
132
254
 
133
255
  // async _getAvnuDepositSwapLegCall(params: {
134
256
  // isDeposit: boolean,
135
- // leg1DepositAmount: Web3Number,
257
+ // leg1DepositAmount: Web3Number,
136
258
  // minHF: number, // e.g. 1.01
137
259
  // vesuAdapter: VesuAdapter
138
260
  // }) {
@@ -144,18 +266,18 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
144
266
  // // approve and swap strk
145
267
  // // add collateral again
146
268
 
147
-
269
+
148
270
  // const legLTV = await vesuAdapter.getLTVConfig(this.config);
149
271
  // logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall legLTV: ${legLTV}`);
150
272
  // const existingPositions = await vesuAdapter.getPositions(this.config);
151
273
  // const collateralisation = await vesuAdapter.getCollateralization(this.config);
152
274
  // const existingCollateralInfo = existingPositions[0];
153
275
  // const existingDebtInfo = existingPositions[1];
154
- // logger.debug(`${this.getTag()}::_getAvnuDepositSwapLegCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
276
+ // logger.debug(`${this.getTag()}::_getAvnuDepositSwapLegCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
155
277
  // existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
156
278
 
157
279
  // // - Prices as seen by Vesu contracts, ideal for HF math
158
- // // Price 1 is ok as fallback bcz that would relatively price the
280
+ // // Price 1 is ok as fallback bcz that would relatively price the
159
281
  // // collateral and debt as equal.
160
282
  // const collateralPrice = collateralisation[0].usdValue > 0 ? collateralisation[0].usdValue / existingCollateralInfo.amount.toNumber() : 1;
161
283
  // const debtPrice = collateralisation[1].usdValue > 0 ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber() : 1;
@@ -202,7 +324,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
202
324
 
203
325
  // logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall debtAmount: ${debtAmount}`);
204
326
  // if (debtAmount.lt(0)) {
205
- // // this is to unwind the position to optimal HF.
327
+ // // this is to unwind the position to optimal HF.
206
328
  // const lstDEXPrice = await this.getLSTDexPrice();
207
329
  // const debtAmountInLST = debtAmount.abs().dividedBy(lstDEXPrice);
208
330
  // const calls = await this.getVesuMultiplyCall({
@@ -228,7 +350,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
228
350
  // }));
229
351
 
230
352
  // console.log(`manageCall1`, manageCall1.call, debtAmount.toWei(), newDepositAmount.toWei());
231
-
353
+
232
354
  // const proofIds: string[] = [STEP0, STEP1];
233
355
  // const manageCalls: ManageCall[] = [manageCall0, manageCall1];
234
356
 
@@ -253,9 +375,9 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
253
375
  // const minAmountWei = (minAmount).toWei();
254
376
  // logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall minAmount: ${minAmount}`);
255
377
  // const swapInfo = await avnuModule.getSwapInfo(
256
- // quote,
257
- // this.metadata.additionalInfo.vaultAllocator.address,
258
- // 0,
378
+ // quote,
379
+ // this.metadata.additionalInfo.vaultAllocator.address,
380
+ // 0,
259
381
  // this.address.address,
260
382
  // minAmountWei
261
383
  // );
@@ -271,7 +393,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
271
393
 
272
394
 
273
395
  // // 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.
396
+ // // then lets close the looping cycle by adding this as collateral.
275
397
  // const newCollateral = minAmount.plus(totalCollateral);
276
398
  // const newHF = newCollateral.multipliedBy(collateralPrice).multipliedBy(legLTV).dividedBy(totalDebtAmount).dividedBy(debtPrice).toNumber();
277
399
  // logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall newHF: ${newHF}`);
@@ -283,7 +405,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
283
405
  // const manageCall4 = manage4Info.callConstructor({
284
406
  // amount: minAmount
285
407
  // });
286
-
408
+
287
409
  // const STEP5 = getVesuLegId(UNIVERSAL_MANAGE_IDS.VESU_LEG1, vesuAdapter.config.debt.symbol);
288
410
  // const manage5Info = this.getProofs<VesuModifyPositionCallParams>(STEP5);
289
411
  // const manageCall5 = manage5Info.callConstructor(VesuAdapter.getDefaultModifyPositionCallParams({
@@ -301,7 +423,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
301
423
  // return manageCall;
302
424
  // }
303
425
 
304
- // todo unwind or not deposit when the yield is bad.
426
+ // todo unwind or not deposit when the yield is bad.
305
427
 
306
428
  // async getLSTMultiplierRebalanceCall(): Promise<{ shouldRebalance: boolean, manageCalls: {vesuAdapter: VesuAdapter, manageCall: Call}[] }> {
307
429
  // let shouldRebalance = false;
@@ -327,11 +449,11 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
327
449
  // const healthFactor = await vesuAdapter.getHealthFactor();
328
450
 
329
451
  // const collateralisation = await vesuAdapter.getCollateralization(this.config);
330
- // logger.debug(`${this.getTag()}::getVesuMultiplyCall::${vesuAdapter.config.debt.symbol} existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
452
+ // logger.debug(`${this.getTag()}::getVesuMultiplyCall::${vesuAdapter.config.debt.symbol} existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
331
453
  // existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
332
454
 
333
455
  // // - Prices as seen by Vesu contracts, ideal for HF math
334
- // // Price 1 is ok as fallback bcz that would relatively price the
456
+ // // Price 1 is ok as fallback bcz that would relatively price the
335
457
  // // collateral and debt as equal.
336
458
  // const collateralPrice = collateralisation[0].usdValue > 0 ? collateralisation[0].usdValue / existingCollateralInfo.amount.toNumber() : 1;
337
459
  // const debtPrice = collateralisation[1].usdValue > 0 ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber() : 1;
@@ -341,11 +463,11 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
341
463
  // const isHFTooLow = healthFactor < this.metadata.additionalInfo.minHealthFactor;
342
464
  // const isHFTooHigh = healthFactor > this.metadata.additionalInfo.targetHealthFactor + 0.05;
343
465
  // if (isHFTooLow || isHFTooHigh || 1) {
344
- // // use unused collateral to target more.
466
+ // // use unused collateral to target more.
345
467
  // const manageCall = await this._getAvnuDepositSwapLegCall({
346
468
  // isDeposit: true,
347
469
  // leg1DepositAmount: unusedBalance.amount,
348
- // minHF: 1.02, // todo, shouldnt use this 1.02 HF, if there isn;t more looping left.
470
+ // minHF: 1.02, // todo, shouldnt use this 1.02 HF, if there isn;t more looping left.
349
471
  // vesuAdapter
350
472
  // })
351
473
  // return { shouldRebalance: true, manageCall };
@@ -355,61 +477,17 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
355
477
  // }
356
478
  // }
357
479
 
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
480
 
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
-
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
481
 
404
482
 
405
483
 
406
484
  // 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.
485
+ // if the current net yield < LST yield, add a function to calculate how much to unwind.
408
486
 
409
487
  /**
410
488
  * Uses vesu's multiple call to create leverage on LST
411
489
  * Deposit amount is in LST
412
- * @param params
490
+ * @param params
413
491
  */
414
492
  async getFundManagementCall(params: {
415
493
  isDeposit: boolean,
@@ -418,268 +496,192 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
418
496
  logger.verbose(`${this.getTag()}::getFundManagementCall params: ${JSON.stringify(params)}`);
419
497
  // const legLTV = await vesuAdapter1.getLTVConfig(this.config);
420
498
  // logger.verbose(`${this.getTag()}::getVesuMultiplyCall legLTV: ${legLTV}`);
421
- const allAdapters = this.metadata.additionalInfo.adapters.map(adapter => adapter.adapter);
499
+ const multiplyAdapters = this.getVesuMultiplyAdapters();
422
500
  if (!params.isDeposit) {
423
- // try using unused balance to unwind.
424
- // no need to unwind.
501
+ // try using unused balance to unwind.
502
+ // no need to unwind.
425
503
  const unusedBalance = await this.getUnusedBalance();
426
504
  logger.verbose(`${this.getTag()}::getVesuMultiplyCall unusedBalance: ${unusedBalance.amount.toString()}, required: ${params.leg1DepositAmount.toString()}`);
427
505
  if (unusedBalance.amount.gte(params.leg1DepositAmount)) {
428
506
  return null;
429
- } else {
430
- const adapters = await AdapterOptimizer.getAdapterToUse(allAdapters, false, params.leg1DepositAmount);
431
- if (adapters.length > 0) {
432
- const proofsInfo = adapters.map(adapter => adapter.getProofs(false, this.getMerkleTree()));
433
- const calls: Call[] = [];
434
- for (const info of proofsInfo) {
435
- const proofGroups = info.proofs;
436
- const call = this.getManageCall(proofGroups, await info.callConstructor({amount: params.leg1DepositAmount}));
437
- calls.push(call);
438
- }
439
- return calls;
440
- } else {
441
- throw new Error(`${this.getTag()}::getVesuMultiplyCall: no adapters to use for unused balance: ${unusedBalance.amount.toString()}`);
442
- }
443
507
  }
444
508
  }
445
509
 
446
- const adapters = await AdapterOptimizer.getAdapterToUse(allAdapters, true, params.leg1DepositAmount);
510
+ const adapters = await AdapterOptimizer.getAdapterToUse(multiplyAdapters, params.isDeposit, params.leg1DepositAmount);
447
511
  if (adapters.length > 0) {
448
- const proofsInfo = adapters.map(adapter => adapter.getProofs(true, this.getMerkleTree()));
512
+ const proofsInfo = adapters.map(adapter => adapter.getProofs(params.isDeposit, this.getMerkleTree()));
449
513
  const calls: Call[] = [];
450
514
  for (const info of proofsInfo) {
451
- const proofGroups = info.proofs;
452
- const call = this.getManageCall(proofGroups, await info.callConstructor({amount: params.leg1DepositAmount}));
515
+ const manageCalls = await info.callConstructor({ amount: params.leg1DepositAmount });
516
+ const call = this.getManageCall(this.getProofGroupsForManageCalls(manageCalls), manageCalls);
453
517
  calls.push(call);
454
518
  }
455
519
  return calls;
456
520
  } else {
457
521
  throw new Error(`${this.getTag()}::getVesuMultiplyCall: no adapters to use for deposit: ${params.leg1DepositAmount.toString()}`);
458
522
  }
459
-
460
- // const existingPositions = await vesuAdapter1.getPositions(this.config);
461
- // const collateralisation = await vesuAdapter1.getCollateralization(this.config);
462
- // const existingCollateralInfo = existingPositions[0];
463
- // const existingDebtInfo = existingPositions[1];
464
- // logger.debug(`${this.getTag()}::getVesuMultiplyCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
465
- // existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
466
-
467
- // // - Prices as seen by Vesu contracts, ideal for HF math
468
- // // Price 1 is ok as fallback bcz that would relatively price the
469
- // // collateral and debt as equal.
470
- // const collateralPrice = collateralisation[0].usdValue > 0 ? collateralisation[0].usdValue / existingCollateralInfo.amount.toNumber() : 1;
471
- // const debtPrice = collateralisation[1].usdValue > 0 ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber() : 1;
472
- // logger.debug(`${this.getTag()}::getVesuMultiplyCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
473
-
474
- // // - Prices as seen by actual swap price
475
- // const dexPrice = await this.getLSTDexPrice();
476
-
477
- // // compute optimal amount of collateral and debt post addition/removal
478
- // // target hf = collateral * collateralPrice * ltv / debt * debtPrice
479
- // // assuming X to be the usd amount of debt borrowed or repaied (negative).
480
- // // target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv / (debt * debtPrice + X)
481
- // // => X * target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv - (debt * debtPrice * target hf)
482
- // // => X * (target hf - ltv)= ((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)
483
- // // => X = (((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)) / (target hf - ltv)
484
- // const addedCollateral = params.leg1DepositAmount
485
- // .multipliedBy(params.isDeposit ? 1 : -1)
486
- // logger.verbose(`${this.getTag()}::getVesuMultiplyCall addedCollateral: ${addedCollateral}`);
487
- // const numeratorPart1 = (existingCollateralInfo.amount.plus((addedCollateral))).multipliedBy(collateralPrice).multipliedBy(legLTV);
488
- // logger.verbose(`${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}`);
489
- // const numeratorPart2 = existingDebtInfo.amount.multipliedBy(debtPrice).multipliedBy(this.metadata.additionalInfo.targetHealthFactor);
490
- // logger.verbose(`${this.getTag()}::getVesuMultiplyCall numeratorPart2: ${numeratorPart2}`);
491
- // const denominatorPart = this.metadata.additionalInfo.targetHealthFactor - (legLTV / dexPrice);
492
- // logger.verbose(`${this.getTag()}::getVesuMultiplyCall denominatorPart: ${denominatorPart}`);
493
- // const x_debt_usd = numeratorPart1.minus(numeratorPart2).dividedBy(denominatorPart);
494
- // logger.verbose(`${this.getTag()}::getVesuMultiplyCall x_debt_usd: ${x_debt_usd}`);
495
- // logger.debug(`${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}, numeratorPart2: ${numeratorPart2}, denominatorPart: ${denominatorPart}`);
496
-
497
- // // both in underlying
498
- // const debtAmount = x_debt_usd.dividedBy(debtPrice);
499
- // const marginAmount = addedCollateral;
500
- // logger.verbose(`${this.getTag()}::getVesuMultiplyCall debtAmount: ${debtAmount}, marginAmount: ${marginAmount}`);
501
-
502
- // // Cases of lever increase (within the scopr of this function)
503
- // // 1. debtAmount > 0 and marginAmount > 0
504
- // // 2. debtAmount > 0 and marginAmount < 0
505
-
506
- // // Cases of lever decrease
507
- // // 3. debtAmount < 0 and marginAmount > 0
508
- // // 4. debtAmount < 0 and marginAmount < 0
509
- // return this.getModifyLeverCall({
510
- // marginAmount,
511
- // debtAmount,
512
- // lstDexPriceInUnderlying: dexPrice,
513
- // isIncrease: debtAmount.greaterThan(0)
514
- // });
515
523
  }
516
524
 
517
-
518
525
  getLSTUnderlyingTokenInfo() {
519
526
  return this.metadata.additionalInfo.underlyingToken;
520
527
  }
521
528
 
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
- // }
529
+ async getLSTAPR(_address: ContractAddr): Promise<number> {
530
+ try {
531
+ const apr = await LSTAPRService.getLSTAPR(this.getLSTUnderlyingTokenInfo().address);
532
+ if (!apr) {
533
+ throw new Error('Failed to get LST APR');
534
+ }
535
+ return apr;
536
+ } catch (error) {
537
+ logger.warn(`${this.getTag()}: Failed to get LST APR: ${error}`);
538
+ return 0;
539
+ }
540
+ }
533
541
 
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
- // }
542
+ async netAPY(): Promise<{
543
+ net: number;
544
+ splits: { apy: number; id: string }[];
545
+ }> {
546
+ const unusedBalance = await this.getUnusedBalance();
547
+ const maxDeposits = await this.maxNewDeposits({ isAPYComputation: true });
548
+ const lstAPY = await this.getLSTAPR(this.getLSTUnderlyingTokenInfo().address);
549
+
550
+ if (maxDeposits * 1.5 < unusedBalance.amount.toNumber()) {
551
+ logger.verbose(
552
+ `${this.getTag()}::netAPY: unused balance is > max servicable from loan, lstAPY: ${lstAPY}`,
553
+ );
554
+ // TODO, need to add the unused balance APY
555
+ const output = await super.netAPY();
556
+ output.splits.push({ apy: lstAPY, id: "lst_apy" });
557
+ return output;
558
+ } else {
559
+ logger.verbose(
560
+ `${this.getTag()}::netAPY: we can take more deposits, use theoretical max APY`,
561
+ );
562
+ const output = await super.netAPY();
563
+ output.splits.push({ apy: lstAPY, id: "lst_apy" });
564
+ return output;
565
+ }
566
+ }
551
567
 
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);
568
+ async maxNewDeposits(
569
+ params: { isAPYComputation: boolean } = { isAPYComputation: false },
570
+ ) {
571
+ let numerator = 0;
572
+ for (let adapter of this.getVesuMultiplyAdapters()) {
573
+ const maxDepositInfo = await adapter.maxDeposit();
574
+ numerator += maxDepositInfo.amount.toNumber();
575
+ }
576
+ return numerator;
577
+ }
557
578
 
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
- // }
579
+ protected async getUnusedBalanceAPY() {
580
+ const unusedBalance = await this.getUnusedBalance();
581
+ const underlying = this.getLSTUnderlyingTokenInfo();
582
+ const lstAPY = await this.getLSTAPR(underlying.address);
583
+ return {
584
+ apy: lstAPY,
585
+ weight: unusedBalance.usdValue,
586
+ };
587
+ }
573
588
 
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
- // }
589
+ async getAUM(unrealizedAUM: boolean = false): Promise<{net: SingleTokenInfo, prevAum: Web3Number, splits: PositionInfo[]}> {
590
+ const underlying = this.asset();
591
+ assert(underlying.symbol.startsWith('x'), 'Underlying is not an LST of Endur');
595
592
 
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
- // }
593
+ // dynamic computation of aum based on price type
594
+ const priceType = unrealizedAUM ? LSTPriceType.ENDUR_PRICE : LSTPriceType.AVNU_PRICE;
619
595
 
620
- async getAUM(): Promise<{net: SingleTokenInfo, prevAum: Web3Number, splits: PositionInfo[]}> {
621
- const allPositions: PositionInfo[] = [];
622
- for (let adapter of this.metadata.additionalInfo.adapters) {
623
- const positions = await adapter.adapter.getPositions();
624
- allPositions.push(...positions);
596
+ // get dex rate
597
+ let tokenUnderlyingPrice: number;
598
+ const avnuRate = await this.getLSTAvnuRate();
599
+ if (avnuRate === 0) {
600
+ throw new Error(`${this.getTag()}::getAUM: tokenUnderlyingPrice (Avnu) is 0`);
625
601
  }
626
602
 
627
- const assetPrice = await this.pricer.getPrice(this.asset().symbol);
628
- let netAUM = new Web3Number(0, this.asset().decimals);
603
+ // get token underlying price
604
+ if (priceType === LSTPriceType.ENDUR_PRICE) {
605
+ tokenUnderlyingPrice = await this.getLSTExchangeRate();
606
+ if (tokenUnderlyingPrice === 0) {
607
+ throw new Error(`${this.getTag()}::getAUM: tokenUnderlyingPrice (Endur) is 0`);
608
+ }
609
+ } else {
610
+ tokenUnderlyingPrice = avnuRate;
611
+ }
612
+
613
+ // check if endur and avnu prices differ by more than 2%
614
+ const diff = Math.abs(tokenUnderlyingPrice - avnuRate) / tokenUnderlyingPrice;
615
+ if (diff > 0.02) {
616
+ throw new Error(
617
+ `${this.getTag()}::getAUM: Endur and Avnu prices differ by more than 2% (Endur: ${tokenUnderlyingPrice}, Avnu: ${avnuRate})`,
618
+ );
619
+ }
620
+
621
+ // get all positions
622
+ const allPositions = await this.getVaultPositions();
623
+
624
+ // compute net aum
625
+ let netAUM = new Web3Number(0, underlying.decimals);
629
626
  for (let position of allPositions) {
630
- if (position.tokenInfo.address.eq(this.asset().address)) {
627
+ if (position.token.address.eq(underlying.address)) {
631
628
  netAUM = netAUM.plus(position.amount);
629
+ } else if (position.token.address.eq(this.getLSTUnderlyingTokenInfo().address)) {
630
+ netAUM = netAUM.plus(position.amount.dividedBy(tokenUnderlyingPrice));
632
631
  } else {
633
- netAUM = netAUM.plus(position.usdValue / assetPrice.price);
632
+ throw new Error(`${this.getTag()}::getAUM: unknown token: ${position.token.symbol}`);
634
633
  }
635
634
  }
636
635
 
636
+ const assetPrice = await this.pricer.getPrice(underlying.symbol);
637
637
  const prevAum = await this.getPrevAUM();
638
+ const priceTypeLabel = priceType === LSTPriceType.ENDUR_PRICE ? "Endur Price" : "Avnu Price";
639
+ logger.verbose(`${this.getTag()} AUM (${priceTypeLabel}): ${netAUM}`);
640
+
638
641
  const realAUM: PositionInfo = {
639
- tokenInfo: this.asset(),
642
+ tokenInfo: underlying,
640
643
  amount: netAUM,
641
644
  usdValue: netAUM.toNumber() * assetPrice.price,
642
- apy: {apy: netAUM.toNumber() * assetPrice.price, type: APYType.BASE},
645
+ apy: {apy: 0, type: APYType.BASE}, // VT: Dont remember why this field exists here. FOr now, set it to 0.
643
646
  remarks: AUMTypes.FINALISED,
644
- protocol: Protocols.NONE // just placeholder
647
+ protocol: Protocols.NONE
645
648
  };
646
649
 
647
650
  const estimatedAUMDelta: PositionInfo = {
648
- tokenInfo: this.asset(),
649
- amount: Web3Number.fromWei('0', this.asset().decimals),
651
+ tokenInfo: underlying,
652
+ amount: Web3Number.fromWei('0', underlying.decimals),
650
653
  usdValue: 0,
651
654
  apy: {apy: 0, type: APYType.BASE},
652
655
  remarks: AUMTypes.DEFISPRING,
653
- protocol: Protocols.NONE // just placeholder
656
+ protocol: Protocols.NONE
654
657
  };
655
658
 
656
659
  return {net: {
657
- tokenInfo: this.asset(),
660
+ tokenInfo: underlying,
658
661
  amount: netAUM,
659
662
  usdValue: netAUM.toNumber() * assetPrice.price
660
663
  }, prevAum: prevAum, splits: [realAUM, estimatedAUMDelta]};
661
664
  }
662
665
 
663
- /**
664
- *
665
- * @param params marginAmount is in LST, debtAmount is in underlying
666
- */
667
- // async getModifyLeverCall(params: {
668
- // marginAmount: Web3Number, // >0 during deposit
669
- // debtAmount: Web3Number,
670
- // lstDexPriceInUnderlying: number,
671
- // isIncrease: boolean
672
- // }): Promise<Call[]> {
673
- // logger.verbose(`${this.getTag()}::getModifyLeverCall marginAmount: ${params.marginAmount}, debtAmount: ${params.debtAmount}, lstDexPriceInUnderlying: ${params.lstDexPriceInUnderlying}, isIncrease: ${params.isIncrease}`);
674
-
675
- // const vesuAdapter = this.getVesuSameTokenAdapter();
676
- // const manage0Info = vesuAdapter.
677
- // const manageCall0 = manage0Info.callConstructor({
678
- // amount: newDepositAmount
679
- // });
680
- // const manageCalls = await vesu
681
- // return [this.getManageCall(proofsIDs, manageCalls)];
682
- // }
666
+ async getTVLUnrealized() {
667
+ return await this.getAUM(true);
668
+ }
669
+
670
+ async getUserUnrealizedGains(user: ContractAddr) {
671
+ const tvl = await this.getTVL();
672
+ const unrealizedTVL = await this.getTVLUnrealized();
673
+ const unrealizedDiff = unrealizedTVL.net.amount.minus(tvl.amount);
674
+ const userTVL = await this.getUserTVL(user);
675
+ const userShare = userTVL.amount.dividedBy(tvl.amount);
676
+ const unrealizedGains = unrealizedDiff.multipliedBy(userShare);
677
+
678
+ return {
679
+ unrealizedGains,
680
+ userShare: userShare.toNumber(),
681
+ tokenInfo: this.asset(),
682
+ };
683
+ }
684
+
683
685
  }
684
686
 
685
687
  export default function VaultDescription(
@@ -689,7 +691,6 @@ export default function VaultDescription(
689
691
  const containerStyle = {
690
692
  maxWidth: "800px",
691
693
  margin: "0 auto",
692
- backgroundColor: "#111",
693
694
  color: "#eee",
694
695
  fontFamily: "Arial, sans-serif",
695
696
  borderRadius: "12px",
@@ -716,7 +717,7 @@ export default function VaultDescription(
716
717
  </div>
717
718
  <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
718
719
  <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.
720
+ <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
721
  </p>
721
722
  </div>
722
723
  {/* <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
@@ -733,69 +734,82 @@ function getDescription(tokenSymbol: string, underlyingSymbol: string) {
733
734
  return VaultDescription(tokenSymbol, underlyingSymbol);
734
735
  }
735
736
 
736
-
737
- enum LST_MULTIPLIER_MANAGE_IDS {
738
- MULTIPLE_APPROVE = 'multiple_approve',
739
- MULTIPLY_VESU = 'multiply_vesu',
740
- SWITCH_DELEGATION_ON = 'switch_delegation_on',
741
- SWITCH_DELEGATION_OFF = 'switch_delegation_off',
742
- AVNU_MULTIPLY_APPROVE_DEPOSIT = 'avnu_mul_approve_dep',
743
- AVNU_MULTIPLY_SWAP_DEPOSIT = 'avnu_mul_swap_dep',
744
- AVNU_MULTIPLY_APPROVE_WITHDRAW = 'avnu_mul_approve_withdr',
745
- AVNU_MULTIPLY_SWAP_WITHDRAW = 'avnu_mul_swap_withdr',
746
- }
747
-
748
- function getAvnuManageIDs(baseID: LST_MULTIPLIER_MANAGE_IDS, debtTokenSymbol: string) {
749
- return `${baseID}_${debtTokenSymbol.toLowerCase()}`;
750
- }
751
-
752
- function getVesuLegId(baseID: string, debtTokenSymbol: string) {
753
- return `${baseID}_${debtTokenSymbol.toLowerCase()}`;
754
- }
755
-
756
737
  function getLooperSettings(
757
738
  lstSymbol: string,
758
739
  underlyingSymbol: string,
759
740
  vaultSettings: HyperLSTStrategySettings,
760
- pool1: ContractAddr,
761
741
  ) {
762
742
  vaultSettings.leafAdapters = [];
743
+ vaultSettings.adapters = [];
763
744
 
764
745
  const lstToken = Global.getDefaultTokens().find(token => token.symbol === lstSymbol)!;
765
746
  const underlyingToken = Global.getDefaultTokens().find(token => token.symbol === underlyingSymbol)!;
766
747
 
767
748
  const baseAdapterConfig: BaseAdapterConfig = {
768
749
  baseToken: lstToken,
769
- supportedPositions: [{asset: lstToken, isDebt: false}],
750
+ supportedPositions: [{asset: lstToken, isDebt: false}, {asset: underlyingToken, isDebt: true}],
770
751
  networkConfig: getMainnetConfig(),
771
752
  pricer: new PricerFromApi(getMainnetConfig(), Global.getDefaultTokens()),
772
753
  vaultAllocator: vaultSettings.vaultAllocator,
773
754
  vaultAddress: vaultSettings.vaultAddress
774
755
  }
775
- const vesuAdapterLST = new VesuSupplyOnlyAdapter({
776
- // xWBTC vToken on re7 xBTC pool
777
- vTokenContract: ContractAddr.from('0x062a162d0827db6f43ebb850cbef3c99fc7969e3070b83a2236c9f3713c89fd8'),
778
- ...baseAdapterConfig,
779
- })
780
756
 
781
- const vesuMultiplyAdapters = borrowableAssets.map(position => new VesuMultiplyAdapter({
782
- poolId: pool1,
783
- collateral: lstToken,
784
- debt: Global.getDefaultTokens().find(token => token.symbol === position)!,
785
- targetHealthFactor: vaultSettings.targetHealthFactor,
786
- minHealthFactor: vaultSettings.minHealthFactor,
787
- quoteAmountToFetchPrice: vaultSettings.quoteAmountToFetchPrice,
788
- ...baseAdapterConfig,
789
- supportedPositions: [{asset: lstToken, isDebt: false}, {asset: Global.getDefaultTokens().find(token => token.symbol === position)!, isDebt: true}]
790
- }));
791
-
792
- const unusedBalanceAdapter = new UnusedBalanceAdapter({
793
- ...baseAdapterConfig,
757
+ const adapterIds: NonNullable<HyperLSTStrategySettings["adapterIds"]> = {
758
+ primaryMultiply: "",
759
+ multiply: {},
760
+ modify: {},
761
+ };
762
+
763
+ const vesuMultiplyAdapters = vaultSettings.borrowable_assets.map((position) => {
764
+ const key = getHyperLSTAdapterKey(position.tokenInfo.symbol, position.poolId);
765
+ const id = getHyperLSTMultiplyAdapterId(lstSymbol, position.tokenInfo.symbol, position.poolId);
766
+ adapterIds.multiply[key] = id;
767
+ return {
768
+ id,
769
+ adapter: new VesuMultiplyAdapter({
770
+ poolId: position.poolId,
771
+ collateral: lstToken,
772
+ debt: position.tokenInfo,
773
+ marginToken: lstToken,
774
+ targetHealthFactor: vaultSettings.targetHealthFactor,
775
+ minHealthFactor: vaultSettings.minHealthFactor,
776
+ quoteAmountToFetchPrice: vaultSettings.quoteAmountToFetchPrice,
777
+ ...baseAdapterConfig,
778
+ minimumVesuMovementAmount: 0,
779
+ }),
780
+ };
781
+ });
782
+
783
+ const vesuModifyPositionAdapters = vaultSettings.borrowable_assets.map((position) => {
784
+ const key = getHyperLSTAdapterKey(position.tokenInfo.symbol, position.poolId);
785
+ const id = getHyperLSTModifyAdapterId(lstSymbol, position.tokenInfo.symbol, position.poolId);
786
+ adapterIds.modify[key] = id;
787
+ return {
788
+ id,
789
+ adapter: new VesuModifyPositionAdapter({
790
+ poolId: position.poolId,
791
+ collateral: lstToken,
792
+ debt: position.tokenInfo,
793
+ targetLtv: 0.8,
794
+ maxLtv: 0.9,
795
+ ...baseAdapterConfig,
796
+ }),
797
+ };
794
798
  });
795
799
 
796
- vaultSettings.adapters.push({id: `${vesuAdapterLST.name}_${lstToken.symbol}_${underlyingToken.symbol}`, adapter: vesuAdapterLST});
797
- vesuMultiplyAdapters.map(adapter => vaultSettings.adapters.push({id: `${adapter.name}_${lstToken.symbol}_${underlyingToken.symbol}`, adapter: adapter}));
798
- vaultSettings.adapters.push({id: `${unusedBalanceAdapter.name}_${lstToken.symbol}`, adapter: unusedBalanceAdapter});
800
+ if (!adapterIds.primaryMultiply && vesuMultiplyAdapters.length > 0) {
801
+ adapterIds.primaryMultiply = vesuMultiplyAdapters[0].id;
802
+ } else {
803
+ throw new Error(`${lstSymbol}::getLooperSettings: primaryMultiply adapter not found`);
804
+ }
805
+ vaultSettings.adapterIds = adapterIds;
806
+
807
+ for (const entry of vesuMultiplyAdapters) {
808
+ vaultSettings.adapters.push(entry);
809
+ }
810
+ for (const entry of vesuModifyPositionAdapters) {
811
+ vaultSettings.adapters.push(entry);
812
+ }
799
813
 
800
814
  const commonAdapter = new CommonAdapter({
801
815
  id: UNIVERSAL_MANAGE_IDS.FLASH_LOAN,
@@ -805,25 +819,21 @@ function getLooperSettings(
805
819
  asset: lstToken.address
806
820
  })
807
821
 
808
- // push vesu adapter to leaf adapters
809
- vaultSettings.leafAdapters.push(() => vesuAdapterLST.getDepositLeaf());
810
- vaultSettings.leafAdapters.push(() => vesuAdapterLST.getWithdrawLeaf());
822
+ for (const entry of vesuMultiplyAdapters) {
823
+ const adapter = entry.adapter;
824
+ vaultSettings.leafAdapters.push(() => adapter.getDepositLeaf());
825
+ vaultSettings.leafAdapters.push(() => adapter.getWithdrawLeaf());
826
+ }
827
+ for (const entry of vesuModifyPositionAdapters) {
828
+ const adapter = entry.adapter;
829
+ vaultSettings.leafAdapters.push(() => adapter.getDepositLeaf());
830
+ vaultSettings.leafAdapters.push(() => adapter.getWithdrawLeaf());
831
+ }
811
832
 
812
- // push vesu multiply adapter to leaf adapters
813
- vesuMultiplyAdapters.map(adapter => vaultSettings.leafAdapters.push(() => adapter.getDepositLeaf()));
814
- vesuMultiplyAdapters.map(adapter => vaultSettings.leafAdapters.push(() => adapter.getWithdrawLeaf()));
815
-
816
833
  // to bridge liquidity back to vault (used by bring_liquidity)
817
834
  vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(lstToken.address, vaultSettings.vaultAddress, UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY).bind(commonAdapter));
818
835
  vaultSettings.leafAdapters.push(commonAdapter.getBringLiquidityAdapter(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY).bind(commonAdapter));
819
-
820
- // claim rewards
821
- // vaultSettings.leafAdapters.push(vesuAdapterLST.getDefispringRewardsAdapter(UNIVERSAL_MANAGE_IDS.DEFISPRING_REWARDS).bind(vesuAdapterLST));
822
-
823
- // // avnu swap for claims rewards
824
- // const STRKToken = Global.getDefaultTokens().find(token => token.symbol === 'STRK')!;
825
- // vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(STRKToken.address, AVNU_EXCHANGE, UNIVERSAL_MANAGE_IDS.APPROVE_SWAP_TOKEN1).bind(commonAdapter));
826
- // vabaseAdapterConfigultSettings.leafAdapters.push(commonAdapter.getAvnuAdapter(STRKToken.address, lstToken.address, UNIVERSAL_MANAGE_IDS.AVNU_SWAP_REWARDS, false).bind(commonAdapter));
836
+
827
837
  return vaultSettings;
828
838
  }
829
839
 
@@ -892,10 +902,6 @@ export const _riskFactor: RiskFactor[] = [
892
902
  {type: RiskType.DEPEG_RISK, value: DepegRiskLevel.GENERALLY_STABLE, weight: 25, reason: "Generally stable pegged assets" },
893
903
  ];
894
904
 
895
- const borrowableAssets = [
896
- 'WBTC', 'tBTC', 'LBTC', 'solvBTC'
897
- ]
898
-
899
905
  const hyperxSTRK: HyperLSTStrategySettings = {
900
906
  vaultAddress: ContractAddr.from('0x46c7a54c82b1fe374353859f554a40b8bd31d3e30f742901579e7b57b1b5960'),
901
907
  manager: ContractAddr.from('0x5d499cd333757f461a0bedaca3dfc4d77320c773037e0aa299f22a6dbfdc03a'),
@@ -906,9 +912,15 @@ const hyperxSTRK: HyperLSTStrategySettings = {
906
912
  adapters: [],
907
913
  targetHealthFactor: 1.1,
908
914
  minHealthFactor: 1.05,
909
- borrowable_assets: Global.getDefaultTokens().filter(token => token.symbol === 'STRK'),
915
+ borrowable_assets: [{
916
+ tokenInfo: Global.getDefaultTokens().find(token => token.symbol === 'STRK')!,
917
+ poolId: VesuPools.Re7STRK,
918
+ }, {
919
+ tokenInfo: Global.getDefaultTokens().find(token => token.symbol === 'STRK')!,
920
+ poolId: VesuPools.Prime,
921
+ }],
910
922
  underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'STRK')!,
911
- quoteAmountToFetchPrice: new Web3Number('100', Global.getDefaultTokens().find(token => token.symbol === 'STRK')!.decimals),
923
+ quoteAmountToFetchPrice: new Web3Number('1000', Global.getDefaultTokens().find(token => token.symbol === 'STRK')!.decimals),
912
924
  }
913
925
 
914
926
  const hyperxWBTC: HyperLSTStrategySettings = {
@@ -921,9 +933,13 @@ const hyperxWBTC: HyperLSTStrategySettings = {
921
933
  adapters: [],
922
934
  targetHealthFactor: 1.1,
923
935
  minHealthFactor: 1.05,
924
- borrowable_assets: borrowableAssets.map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
936
+ borrowable_assets: [{
937
+ tokenInfo: Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!,
938
+ poolId: VesuPools.Re7xBTC,
939
+ }],
925
940
  underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!,
926
941
  quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!.decimals),
942
+ redemptionRouter: ContractAddr.from('0x6ea649f402898f69baf775c1afdd08522c071c640b9c4460192070ec2b96417'),
927
943
  }
928
944
 
929
945
  const hyperxtBTC: HyperLSTStrategySettings = {
@@ -936,9 +952,16 @@ const hyperxtBTC: HyperLSTStrategySettings = {
936
952
  adapters: [],
937
953
  targetHealthFactor: 1.1,
938
954
  minHealthFactor: 1.05,
939
- borrowable_assets: borrowableAssets.map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
955
+ borrowable_assets: [{
956
+ tokenInfo: Global.getDefaultTokens().find(token => token.symbol === 'tBTC')!,
957
+ poolId: VesuPools.Re7xBTC,
958
+ }, {
959
+ tokenInfo: Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!,
960
+ poolId: VesuPools.Re7xBTC,
961
+ }],
940
962
  underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'tBTC')!,
941
963
  quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'tBTC')!.decimals),
964
+ redemptionRouter: ContractAddr.from('0x3de9c409d1e357e25778fb7a3e2e2393666956846a5c2caa607296fa8e76b5d'),
942
965
  }
943
966
 
944
967
  const hyperxsBTC: HyperLSTStrategySettings = {
@@ -951,7 +974,10 @@ const hyperxsBTC: HyperLSTStrategySettings = {
951
974
  adapters: [],
952
975
  targetHealthFactor: 1.1,
953
976
  minHealthFactor: 1.05,
954
- borrowable_assets: borrowableAssets.map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
977
+ borrowable_assets: [{
978
+ tokenInfo: Global.getDefaultTokens().find(token => token.symbol === 'solvBTC')!,
979
+ poolId: VesuPools.Re7xBTC,
980
+ }],
955
981
  underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'solvBTC')!,
956
982
  quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'solvBTC')!.decimals),
957
983
  }
@@ -966,44 +992,47 @@ const hyperxLBTC: HyperLSTStrategySettings = {
966
992
  adapters: [],
967
993
  targetHealthFactor: 1.1,
968
994
  minHealthFactor: 1.05,
969
- borrowable_assets: borrowableAssets.map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
995
+ borrowable_assets: [{
996
+ tokenInfo: Global.getDefaultTokens().find(token => token.symbol === 'LBTC')!,
997
+ poolId: VesuPools.Re7xBTC,
998
+ }],
970
999
  underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'LBTC')!,
971
1000
  quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'LBTC')!.decimals),
972
1001
  }
973
1002
 
974
- const hypermRe7BTC: HyperLSTStrategySettings = {
975
- vaultAddress: ContractAddr.from('0x6c89b75d09de82477edb86b2c2918cfc1a5dc0177cfbdea46278386b6499645'),
976
- manager: ContractAddr.from('0x7bc2d0535e13352d3ab78ea047616a3162068294297304943d2302122a150a1'),
977
- vaultAllocator: ContractAddr.from('0x760f9cebca9d2ee624f4224591da6da8b3ea5fd2410590735709551bd4e7570'),
978
- redeemRequestNFT: ContractAddr.from('0x5e045ae0160f7650f8a4dd7c826f25630a89fe62434db4441e7e0075608180f'),
979
- aumOracle: ContractAddr.from('0x3958df341b838813c24efb9183c23bddd1c57d44b1b86c0dd57f67887b89fba'),
980
- leafAdapters: [],
981
- adapters: [],
982
- targetHealthFactor: 1.1,
983
- minHealthFactor: 1.05,
984
- borrowable_assets: borrowableAssets.map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
985
- underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!,
986
- quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'mRe7BTC')!.decimals),
987
- }
1003
+ // const hypermRe7BTC: HyperLSTStrategySettings = {
1004
+ // vaultAddress: ContractAddr.from('0x6c89b75d09de82477edb86b2c2918cfc1a5dc0177cfbdea46278386b6499645'),
1005
+ // manager: ContractAddr.from('0x7bc2d0535e13352d3ab78ea047616a3162068294297304943d2302122a150a1'),
1006
+ // vaultAllocator: ContractAddr.from('0x760f9cebca9d2ee624f4224591da6da8b3ea5fd2410590735709551bd4e7570'),
1007
+ // redeemRequestNFT: ContractAddr.from('0x5e045ae0160f7650f8a4dd7c826f25630a89fe62434db4441e7e0075608180f'),
1008
+ // aumOracle: ContractAddr.from('0x3958df341b838813c24efb9183c23bddd1c57d44b1b86c0dd57f67887b89fba'),
1009
+ // leafAdapters: [],
1010
+ // adapters: [],
1011
+ // targetHealthFactor: 1.1,
1012
+ // minHealthFactor: 1.05,
1013
+ // borrowable_assets: .map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
1014
+ // underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!,
1015
+ // quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'mRe7BTC')!.decimals),
1016
+ // }
988
1017
  // Contract deployed: Vault, addr: 0x42797ab4eb1f72787442e91a73d63a39e3a141c1106470a946ecc328db6896c
989
1018
  // Contract deployed: RedeemRequest, addr: 0x4bbb25c2568af07967342833f7db1aece1be1be2330798dab4ee585aa6c2c72
990
1019
  // Contract deployed: VaultAllocator, addr: 0x456c4c6afca90512aeb5c735d84405fea6e51ab06d1851ac8cdb0a235e14f15
991
1020
  // Contract deployed: Manager, addr: 0x435b45d40fbb406cf69ac84bb471e7b7a4ea2295d0893c05dd2db565295e77f
992
1021
 
993
- const hypermRe7YIELD: HyperLSTStrategySettings = {
994
- vaultAddress: ContractAddr.from('0x42797ab4eb1f72787442e91a73d63a39e3a141c1106470a946ecc328db6896c'),
995
- manager: ContractAddr.from('0x435b45d40fbb406cf69ac84bb471e7b7a4ea2295d0893c05dd2db565295e77f'),
996
- vaultAllocator: ContractAddr.from('0x456c4c6afca90512aeb5c735d84405fea6e51ab06d1851ac8cdb0a235e14f15'),
997
- redeemRequestNFT: ContractAddr.from('0x4bbb25c2568af07967342833f7db1aece1be1be2330798dab4ee585aa6c2c72'),
998
- aumOracle: ContractAddr.from('0x3e1f2825158cafccc9b42a8165d17ceb6b8e966474d9c63587d338746888382'),
999
- leafAdapters: [],
1000
- adapters: [],
1001
- targetHealthFactor: 1.1,
1002
- minHealthFactor: 1.05,
1003
- borrowable_assets: [Global.getDefaultTokens().find(token => token.symbol === 'USDC')!],
1004
- underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'USDC')!,
1005
- quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'mRe7BTC')!.decimals),
1006
- }
1022
+ // const hypermRe7YIELD: HyperLSTStrategySettings = {
1023
+ // vaultAddress: ContractAddr.from('0x42797ab4eb1f72787442e91a73d63a39e3a141c1106470a946ecc328db6896c'),
1024
+ // manager: ContractAddr.from('0x435b45d40fbb406cf69ac84bb471e7b7a4ea2295d0893c05dd2db565295e77f'),
1025
+ // vaultAllocator: ContractAddr.from('0x456c4c6afca90512aeb5c735d84405fea6e51ab06d1851ac8cdb0a235e14f15'),
1026
+ // redeemRequestNFT: ContractAddr.from('0x4bbb25c2568af07967342833f7db1aece1be1be2330798dab4ee585aa6c2c72'),
1027
+ // aumOracle: ContractAddr.from('0x3e1f2825158cafccc9b42a8165d17ceb6b8e966474d9c63587d338746888382'),
1028
+ // leafAdapters: [],
1029
+ // adapters: [],
1030
+ // targetHealthFactor: 1.1,
1031
+ // minHealthFactor: 1.05,
1032
+ // borrowable_assets: [Global.getDefaultTokens().find(token => token.symbol === 'USDC')!],
1033
+ // underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'USDC')!,
1034
+ // quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'mRe7BTC')!.decimals),
1035
+ // }
1007
1036
 
1008
1037
  export function getInvestmentSteps(lstSymbol: string, underlyingSymbol: string) {
1009
1038
  return [
@@ -1011,35 +1040,152 @@ export function getInvestmentSteps(lstSymbol: string, underlyingSymbol: string)
1011
1040
  `The vault manager loops the ${underlyingSymbol} to buy ${lstSymbol}`,
1012
1041
  `The vault manager collateralizes the ${lstSymbol} on Vesu`,
1013
1042
  `The vault manager borrows more ${underlyingSymbol} to loop further`,
1014
- `If required, adjust leverage or re-allocate assets within pool on Vesu to optimize yield`
1043
+ `If required, adjust leverage or re-allocate assets within LST pool on Vesu to optimize yield`
1015
1044
  ]
1016
1045
  }
1017
1046
 
1018
- function getStrategySettings(lstSymbol: string, underlyingSymbol: string, addresses: HyperLSTStrategySettings, isPreview: boolean = false, isLST: boolean): IStrategyMetadata<HyperLSTStrategySettings> {
1047
+ function getMaxTVL(lstSymbol: string): Web3Number {
1048
+ const lstMaxTVLs: Record<string, number> = {
1049
+ xWBTC: 5,
1050
+ xLBTC: 5,
1051
+ xtBTC: 5,
1052
+ xsBTC: 5,
1053
+ xSTRK: 7000000,
1054
+ };
1055
+ const maxTVLValue = lstMaxTVLs[lstSymbol] || 0;
1056
+ const token = Global.getDefaultTokens().find(
1057
+ (token) => token.symbol === lstSymbol,
1058
+ );
1059
+ if (!token) {
1060
+ return Web3Number.fromWei(0, 18);
1061
+ }
1062
+ return Web3Number.fromWei(maxTVLValue, token.decimals);
1063
+ }
1064
+
1065
+ function createHyperLSTSettings(
1066
+ lstSymbol: string,
1067
+ underlyingSymbol: string,
1068
+ ): StrategySettings {
1069
+ const depositToken = Global.getDefaultTokens().find(
1070
+ (token) => token.symbol === lstSymbol,
1071
+ )!;
1019
1072
  return {
1073
+ maxTVL: getMaxTVL(lstSymbol),
1074
+ isPaused: false,
1075
+ liveStatus: StrategyLiveStatus.HOT,
1076
+ isAudited: true,
1077
+ isInstantWithdrawal: false,
1078
+ hideHarvestInfo: true,
1079
+ quoteToken: depositToken,
1080
+ showWithdrawalWarningModal: false,
1081
+ alerts: [
1082
+ {
1083
+ tab: "withdraw" as const,
1084
+ 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.",
1085
+ type: "info" as const,
1086
+ },
1087
+ {
1088
+ tab: "deposit" as const,
1089
+ text: (
1090
+ <>
1091
+ To acquire the LST, please visit{" "}
1092
+ <a href="https://app.endur.fi" target="_blank" rel="noopener noreferrer">endur.fi</a>
1093
+ </>
1094
+ ),
1095
+ type: "info" as const,
1096
+ },
1097
+ {
1098
+ tab: "deposit" as const,
1099
+ 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.",
1100
+ type: "info" as const,
1101
+ },
1102
+ ],
1103
+ };
1104
+ }
1105
+
1106
+ const HYPER_LST_SECURITY = {
1107
+ auditStatus: AuditStatus.AUDITED,
1108
+ sourceCode: {
1109
+ type: SourceCodeType.CLOSED_SOURCE,
1110
+ contractLink: "https://github.com/trovesfi/troves-contracts",
1111
+ },
1112
+ accessControl: {
1113
+ type: AccessControlType.STANDARD_ACCOUNT,
1114
+ addresses: [ContractAddr.from("0x0")],
1115
+ timeLock: "2 Days",
1116
+ },
1117
+ };
1118
+
1119
+ const HYPER_LST_REDEMPTION_INFO: RedemptionInfo = {
1120
+ instantWithdrawalVault: InstantWithdrawalVault.NO,
1121
+ redemptionsInfo: [
1122
+ {
1123
+ title: "Typical Duration",
1124
+ description: "1-2 hours",
1125
+ },
1126
+ ],
1127
+ alerts: [
1128
+ {
1129
+ type: "info",
1130
+ 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.",
1131
+ tab: "withdraw",
1132
+ },
1133
+ ],
1134
+ };
1135
+
1136
+ function getStrategySettings(
1137
+ lstSymbol: string,
1138
+ underlyingSymbol: string,
1139
+ settings: HyperLSTStrategySettings,
1140
+ isPreview: boolean = false,
1141
+ isLST: boolean,
1142
+ ): IStrategyMetadata<HyperLSTStrategySettings> {
1143
+ return {
1144
+ id: `hyper_${lstSymbol.toLowerCase()}`,
1020
1145
  name: `Hyper ${lstSymbol}`,
1021
1146
  description: getDescription(lstSymbol, underlyingSymbol),
1022
- address: addresses.vaultAddress,
1147
+ address: settings.vaultAddress,
1023
1148
  launchBlock: 0,
1024
1149
  type: 'Other',
1150
+ vaultType: {
1151
+ type: VaultType.LOOPING,
1152
+ description: `Creates leveraged looping position on ${lstSymbol} by borrowing ${underlyingSymbol} to increase yield`,
1153
+ },
1025
1154
  depositTokens: [Global.getDefaultTokens().find(token => token.symbol === lstSymbol)!],
1026
- additionalInfo: getLooperSettings(lstSymbol, underlyingSymbol, addresses, lstSymbol === 'xSTRK' ? VesuPools.Re7xSTRK : VesuPools.Re7xBTC),
1155
+ additionalInfo: getLooperSettings(lstSymbol, underlyingSymbol, settings),
1027
1156
  risk: {
1028
1157
  riskFactor: _riskFactor,
1029
1158
  netRisk:
1030
1159
  _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) /
1031
1160
  _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
1032
- notARisks: getNoRiskTags(_riskFactor)
1161
+ notARisks: getNoRiskTags(_riskFactor),
1033
1162
  },
1034
1163
  auditUrl: AUDIT_URL,
1035
1164
  protocols: [Protocols.ENDUR, Protocols.VESU],
1036
- maxTVL: Web3Number.fromWei(0, 18),
1037
- contractDetails: getContractDetails(addresses),
1165
+ curator: {
1166
+ name: "Unwrap Labs",
1167
+ logo: "https://assets.troves.fi/integrations/unwraplabs/white.png",
1168
+ },
1169
+ settings: createHyperLSTSettings(lstSymbol, underlyingSymbol),
1170
+ contractDetails: getContractDetails(settings),
1038
1171
  faqs: getFAQs(lstSymbol, underlyingSymbol, isLST),
1039
1172
  investmentSteps: getInvestmentSteps(lstSymbol, underlyingSymbol),
1040
1173
  isPreview: isPreview,
1041
- 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.'
1042
- }
1174
+ 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.',
1175
+ realizedApyMethodology: 'The realizedAPY is based on past 14 days performance by the vault',
1176
+ tags: lstSymbol.includes('BTC')
1177
+ ? [StrategyTag.BTC, StrategyTag.LEVERED]
1178
+ : [StrategyTag.LEVERED],
1179
+ security: HYPER_LST_SECURITY,
1180
+ redemptionInfo: HYPER_LST_REDEMPTION_INFO,
1181
+ usualTimeToEarnings: '2 weeks',
1182
+ 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.',
1183
+ points: lstSymbol === 'xSTRK' ? [{
1184
+ multiplier: 4,
1185
+ logo: "https://endur.fi/favicon.ico",
1186
+ toolTip: "This strategy holds xSTRK. Earn 3-4x Endur points on your xSTRK due to the leverage. Points can be found on endur.fi.",
1187
+ }] : undefined,
1188
+ };
1043
1189
  }
1044
1190
 
1045
1191
 
@@ -1062,10 +1208,10 @@ function getStrategySettings(lstSymbol: string, underlyingSymbol: string, addres
1062
1208
  export const HyperLSTStrategies: IStrategyMetadata<HyperLSTStrategySettings>[] =
1063
1209
  [
1064
1210
  getStrategySettings('xSTRK', 'STRK', hyperxSTRK, false, true),
1065
- getStrategySettings('xWBTC', 'WBTC', hyperxWBTC, false, false),
1066
- getStrategySettings('xtBTC', 'tBTC', hyperxtBTC, false, false),
1067
- getStrategySettings('xsBTC', 'solvBTC', hyperxsBTC, false, false),
1068
- getStrategySettings('xLBTC', 'LBTC', hyperxLBTC, false, false),
1069
- getStrategySettings('mRe7BTC', 'mRe7BTC', hypermRe7BTC, false, false),
1070
- getStrategySettings('mRe7YIELD', 'mRe7YIELD', hypermRe7YIELD, false, false),
1211
+ getStrategySettings('xWBTC', 'WBTC', hyperxWBTC, false, true),
1212
+ getStrategySettings('xtBTC', 'tBTC', hyperxtBTC, false, true),
1213
+ getStrategySettings('xsBTC', 'solvBTC', hyperxsBTC, false, true),
1214
+ getStrategySettings('xLBTC', 'LBTC', hyperxLBTC, false, true),
1215
+ // getStrategySettings('mRe7BTC', 'mRe7BTC', hypermRe7BTC, false, false),
1216
+ // getStrategySettings('mRe7YIELD', 'mRe7YIELD', hypermRe7YIELD, false, false),
1071
1217
  ]