@strkfarm/sdk 2.0.0-dev.35 → 2.0.0-dev.36

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 (50) hide show
  1. package/dist/cli.js +2 -2
  2. package/dist/cli.mjs +2 -2
  3. package/dist/index.browser.global.js +19244 -28574
  4. package/dist/index.browser.mjs +8439 -17951
  5. package/dist/index.d.ts +578 -2746
  6. package/dist/index.js +8528 -18078
  7. package/dist/index.mjs +8456 -17968
  8. package/package.json +3 -3
  9. package/src/data/universal-vault.abi.json +8 -7
  10. package/src/dataTypes/bignumber.browser.ts +5 -1
  11. package/src/dataTypes/bignumber.node.ts +5 -0
  12. package/src/global.ts +21 -1
  13. package/src/interfaces/common.tsx +39 -4
  14. package/src/modules/avnu.ts +19 -10
  15. package/src/modules/index.ts +1 -1
  16. package/src/strategies/base-strategy.ts +92 -8
  17. package/src/strategies/constants.ts +8 -3
  18. package/src/strategies/ekubo-cl-vault.tsx +150 -16
  19. package/src/strategies/factory.ts +21 -1
  20. package/src/strategies/index.ts +2 -7
  21. package/src/strategies/registry.ts +28 -5
  22. package/src/strategies/sensei.ts +29 -13
  23. package/src/strategies/svk-strategy.ts +26 -2
  24. package/src/strategies/token-boosted-xstrk-carry-strategy.tsx +1057 -0
  25. package/src/strategies/universal-adapters/avnu-adapter.ts +16 -8
  26. package/src/strategies/universal-adapters/index.ts +1 -2
  27. package/src/strategies/universal-adapters/svk-troves-adapter.ts +19 -6
  28. package/src/strategies/universal-adapters/vesu-modify-position-adapter.ts +22 -3
  29. package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +75 -52
  30. package/src/strategies/universal-adapters/vesu-position-common.ts +38 -31
  31. package/src/strategies/universal-lst-muliplier-strategy.tsx +222 -269
  32. package/src/strategies/universal-strategy.tsx +166 -105
  33. package/src/strategies/vesu-rebalance.tsx +3 -6
  34. package/src/strategies/yoloVault.ts +1084 -0
  35. package/src/utils/health-factor-math.ts +29 -0
  36. package/src/modules/ExtendedWrapperSDk/index.ts +0 -62
  37. package/src/modules/ExtendedWrapperSDk/types.ts +0 -334
  38. package/src/modules/ExtendedWrapperSDk/wrapper.ts +0 -611
  39. package/src/strategies/universal-adapters/extended-adapter.ts +0 -860
  40. package/src/strategies/universal-adapters/usdc<>usdce-adapter.ts +0 -200
  41. package/src/strategies/usdc-boosted-strategy.tsx +0 -693
  42. package/src/strategies/vesu-extended-strategy/services/executionService.ts +0 -2234
  43. package/src/strategies/vesu-extended-strategy/services/extended-vesu-state-manager.ts +0 -4254
  44. package/src/strategies/vesu-extended-strategy/services/ltv-imbalance-rebalance-math.ts +0 -783
  45. package/src/strategies/vesu-extended-strategy/services/operationService.ts +0 -56
  46. package/src/strategies/vesu-extended-strategy/types/transaction-metadata.ts +0 -88
  47. package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +0 -78
  48. package/src/strategies/vesu-extended-strategy/utils/constants.ts +0 -48
  49. package/src/strategies/vesu-extended-strategy/utils/helper.ts +0 -528
  50. package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +0 -1014
@@ -0,0 +1,1057 @@
1
+ import { ContractAddr, Web3Number } from "@/dataTypes";
2
+ import { Global } from "@/global";
3
+ import {
4
+ AccessControlType,
5
+ AuditStatus,
6
+ getMainnetConfig,
7
+ IConfig,
8
+ InstantWithdrawalVault,
9
+ IStrategyMetadata,
10
+ Protocols,
11
+ SourceCodeType,
12
+ StrategyTag,
13
+ TokenInfo,
14
+ VaultPosition,
15
+ VaultType,
16
+ } from "@/interfaces";
17
+ import { ERC20, PricerFromApi, TokenMarketData } from "@/modules";
18
+ import { PricerBase } from "@/modules/pricerBase";
19
+ import { logger, assert } from "@/utils";
20
+ import { BlockIdentifier, Call, uint256, num } from "starknet";
21
+ import { SingleTokenInfo } from "./base-strategy";
22
+ import { SVKStrategy } from "./svk-strategy";
23
+ import {
24
+ APYType,
25
+ AvnuAdapter,
26
+ BaseAdapterConfig,
27
+ CommonAdapter,
28
+ PositionInfo,
29
+ TokenTransferAdapter,
30
+ VesuModifyPositionAdapter,
31
+ } from "./universal-adapters";
32
+ import { AdapterOptimizer } from "./universal-adapters/adapter-optimizer";
33
+ import {
34
+ AVNU_EXCHANGE,
35
+ AVNU_QUOTE_URL,
36
+ } from "./universal-adapters/adapter-utils";
37
+ import { SvkTrovesAdapter } from "./universal-adapters/svk-troves-adapter";
38
+ import { VesuPools, VesuAdapter } from "./universal-adapters/vesu-adapter";
39
+ import {
40
+ AUMTypes,
41
+ getContractDetails,
42
+ UNIVERSAL_MANAGE_IDS,
43
+ UniversalStrategySettings,
44
+ } from "./universal-strategy";
45
+
46
+ export interface BoostedxSTRKCarryStrategySettings extends UniversalStrategySettings {
47
+ depositToken: TokenInfo; // USDC, WBTC, etc. - the collateral token
48
+ debtToken: TokenInfo; // STRK, etc. - the debt token
49
+ lstHyperToken: TokenInfo; // xSTRK, etc. - the LST token deposited into hyper vault
50
+ vesuPoolId: ContractAddr;
51
+ maxLTV: number; // e.g. 0.66
52
+ targetLTV: number;
53
+ hyperLstVaultAddress: ContractAddr; // Address of the hyper vault for LST
54
+ hyperLstRedeemNFT: ContractAddr; // NFT contract for pending hyper redeems
55
+ trovesStrategyId: string; // e.g. "hyper_xstrk" - used for APY fetching
56
+ // TODO: will refac later on to a better approach where we dont need flags
57
+ hasBtcFiRewards: boolean; // Whether this strategy has BTC.Fi rewards (affects reporting flow)
58
+ // Adapter IDs computed from token symbols
59
+ adapterIds?: {
60
+ vesu: string;
61
+ avnu: string;
62
+ hyper: string;
63
+ transfer: string;
64
+ };
65
+ }
66
+
67
+ export class BoostedxSTRKCarryStrategy<
68
+ S extends BoostedxSTRKCarryStrategySettings,
69
+ > extends SVKStrategy<S> {
70
+ constructor(
71
+ config: IConfig,
72
+ pricer: PricerBase,
73
+ metadata: IStrategyMetadata<S>,
74
+ ) {
75
+ super(config, pricer, metadata);
76
+
77
+ this.metadata.additionalInfo.adapters.forEach((adapter) => {
78
+ adapter.adapter.config.networkConfig = this.config;
79
+ adapter.adapter.config.pricer = this.pricer;
80
+ if ((adapter.adapter as any)._vesuAdapter) {
81
+ (adapter.adapter as any)._vesuAdapter.networkConfig = this.config;
82
+ (adapter.adapter as any)._vesuAdapter.pricer = this.pricer;
83
+ }
84
+ });
85
+ }
86
+
87
+ getTag(): string {
88
+ return `${BoostedxSTRKCarryStrategy.name}:${this.metadata.name}`;
89
+ }
90
+
91
+ private getAdapterById<T>(id: string): T {
92
+ const entry = this.metadata.additionalInfo.adapters.find(
93
+ (a) => a.id === id,
94
+ );
95
+ if (!entry) {
96
+ throw new Error(
97
+ `${this.getTag()}::getAdapterById: adapter not found for id "${id}"`,
98
+ );
99
+ }
100
+ return entry.adapter as T;
101
+ }
102
+
103
+ private async getTruePriceForToken(token: TokenInfo): Promise<number> {
104
+ return new TokenMarketData(this.pricer, this.config).getTruePrice(token);
105
+ }
106
+
107
+ async getMinOutputAmountLSTBuy(amountInUnderlying: Web3Number) {
108
+ const lst = this.metadata.additionalInfo.lstHyperToken;
109
+ const lstTruePrice = await this.getTruePriceForToken(lst);
110
+ const minOutputAmount = amountInUnderlying
111
+ .dividedBy(lstTruePrice)
112
+ .multipliedBy(0.99979); // 0.21 % avnu fees
113
+ return new Web3Number(minOutputAmount.toString(), lst.decimals);
114
+ }
115
+
116
+ async getMinOutputAmountLSTSell(amountInLST: Web3Number) {
117
+ const lst = this.metadata.additionalInfo.lstHyperToken;
118
+ const lstTruePrice = await this.getTruePriceForToken(lst);
119
+ return amountInLST.multipliedBy(lstTruePrice).multipliedBy(0.995);
120
+ }
121
+
122
+ async getVesuModifyPositionCall(params: {
123
+ isDeposit: boolean;
124
+ leg1DepositAmount: Web3Number;
125
+ debtAmount?: Web3Number;
126
+ }): Promise<Call> {
127
+ logger.verbose(
128
+ `${this.getTag()}::getVesuModifyPositionCall isDeposit=${params.isDeposit}, amount=${params.leg1DepositAmount}, debtAmount=${params.debtAmount?.toNumber()}`,
129
+ );
130
+ return this.buildManageCallFromAdapter(
131
+ this.getAdapterById<VesuModifyPositionAdapter>(
132
+ this.metadata.additionalInfo.adapterIds!.vesu,
133
+ ),
134
+ params.isDeposit,
135
+ params.leg1DepositAmount,
136
+ params.debtAmount ? { debtAmount: params.debtAmount } : undefined,
137
+ );
138
+ }
139
+
140
+ async getVesuPositions(): Promise<VaultPosition[]> {
141
+ const positions = await this.getAdapterById<VesuModifyPositionAdapter>(
142
+ this.metadata.additionalInfo.adapterIds!.vesu,
143
+ ).getPositions();
144
+ return positions.map((p) => ({
145
+ amount: p.amount,
146
+ usdValue: p.usdValue,
147
+ remarks: p.remarks ?? "",
148
+ token: p.tokenInfo,
149
+ protocol: p.protocol,
150
+ }));
151
+ }
152
+
153
+ async getVesuHealthFactor(
154
+ blockNumber: BlockIdentifier = "latest",
155
+ ): Promise<number> {
156
+ const vesuAdapter = this.getAdapterById<VesuModifyPositionAdapter>(
157
+ this.metadata.additionalInfo.adapterIds!.vesu,
158
+ );
159
+ return await vesuAdapter._vesuAdapter.getHealthFactor(blockNumber);
160
+ }
161
+
162
+ // TODO: will have to still modify for instant withdrawals as of now
163
+ async getModifyHyperPositionCall(params: {
164
+ isDeposit: boolean;
165
+ amount: Web3Number;
166
+ }): Promise<Call> {
167
+ logger.verbose(
168
+ `${this.getTag()}::getModifyHyperPositionCall isDeposit=${params.isDeposit}, amount=${params.amount}`,
169
+ );
170
+ return this.buildManageCallFromAdapter(
171
+ this.getAdapterById<SvkTrovesAdapter>(
172
+ this.metadata.additionalInfo.adapterIds!.hyper,
173
+ ),
174
+ params.isDeposit,
175
+ params.amount,
176
+ );
177
+ }
178
+
179
+ async getAvnuSwapCall(params: {
180
+ isDeposit: boolean;
181
+ amount: Web3Number;
182
+ minAmount?: Web3Number;
183
+ }): Promise<Call> {
184
+ logger.verbose(
185
+ `${this.getTag()}::getAvnuSwapCall isDeposit=${params.isDeposit}, amount=${params.amount}, minAmount=${params.minAmount?.toNumber()}`,
186
+ );
187
+ return this.buildManageCallFromAdapter(
188
+ this.getAdapterById<AvnuAdapter>(
189
+ this.metadata.additionalInfo.adapterIds!.avnu,
190
+ ),
191
+ params.isDeposit,
192
+ params.amount,
193
+ params.minAmount ? { minAmount: params.minAmount } : undefined,
194
+ );
195
+ }
196
+
197
+ /**
198
+ * Returns the deposit token balance sitting idle inside the vault contract itself.
199
+ * This balance can offset the required liquidity during withdrawal processing.
200
+ */
201
+ async getUnusedBalanceVault(): Promise<Web3Number> {
202
+ const depositToken = this.metadata.additionalInfo.depositToken;
203
+ return new ERC20(this.config).balanceOf(
204
+ depositToken.address,
205
+ this.metadata.additionalInfo.vaultAddress,
206
+ depositToken.decimals,
207
+ );
208
+ }
209
+
210
+ /**
211
+ * Returns the current borrow token balance sitting unused in the vault allocator.
212
+ * This covers borrow token from prior borrow cycles that hasn't been swapped yet.
213
+ */
214
+ // Technically we can use this or we can even use the avnu-adapter to get the unused debt
215
+ async getUnusedDebt(): Promise<Web3Number> {
216
+ const debtToken = this.metadata.additionalInfo.debtToken;
217
+ return new ERC20(this.config).balanceOf(
218
+ debtToken.address,
219
+ this.metadata.additionalInfo.vaultAllocator,
220
+ debtToken.decimals,
221
+ );
222
+ }
223
+
224
+ /**
225
+ * Returns the LST balance sitting in the vault allocator.
226
+ * This is non-zero when the previous cycle's request_redeem on the HyperPosition
227
+ * has been settled — the redeemed LST lands here and is ready to be swapped.
228
+ */
229
+ async getLstInVaultAllocator(): Promise<Web3Number> {
230
+ const lstToken = this.metadata.additionalInfo.lstHyperToken;
231
+ return new ERC20(this.config).balanceOf(
232
+ lstToken.address.address,
233
+ this.metadata.additionalInfo.vaultAllocator,
234
+ lstToken.decimals,
235
+ );
236
+ }
237
+
238
+ /**
239
+ * Simulates depositing `depositAmount` on Vesu and returns the
240
+ * incremental borrow token that would be borrowed. Needed to size the AVNU swap
241
+ * call that is batched together with the Vesu call in the same transaction.
242
+ */
243
+ async computeVesuDepositBorrowAmount(
244
+ depositAmount: Web3Number,
245
+ ): Promise<Web3Number> {
246
+ return this.getAdapterById<VesuModifyPositionAdapter>(
247
+ this.metadata.additionalInfo.adapterIds!.vesu,
248
+ ).getExpectedDepositDebtDelta(depositAmount);
249
+ }
250
+
251
+ async computeVesuWithdrawDebtDelta(
252
+ withdrawAmount: Web3Number,
253
+ ): Promise<Web3Number> {
254
+ return this.getAdapterById<VesuModifyPositionAdapter>(
255
+ this.metadata.additionalInfo.adapterIds!.vesu,
256
+ ).getExpectedWithdrawDebtDelta(withdrawAmount);
257
+ }
258
+
259
+ async getPendingHyperAssets(): Promise<Web3Number> {
260
+ const lstToken = this.metadata.additionalInfo.lstHyperToken;
261
+ const hyperLstRedeemNFT = this.metadata.additionalInfo.hyperLstRedeemNFT;
262
+ return this.getAdapterById<SvkTrovesAdapter>(
263
+ this.metadata.additionalInfo.adapterIds!.hyper,
264
+ ).getPendingAssetsFromOwnerNFTMethod(
265
+ hyperLstRedeemNFT,
266
+ this.metadata.additionalInfo.vaultAllocator,
267
+ lstToken.decimals,
268
+ );
269
+ }
270
+
271
+ // TODO: rn we are just making these functions in the strategy itself but
272
+ // later on we will move them to the SVKStrategy for generalization
273
+
274
+ async getUserTVL(
275
+ user: ContractAddr,
276
+ blockIdentifier: BlockIdentifier = "latest",
277
+ ) {
278
+ const shares: any = await this.contract.call("balanceOf", [user.address], {
279
+ blockIdentifier,
280
+ });
281
+ const assets: any = await this.contract.call(
282
+ "convert_to_assets",
283
+ [uint256.bnToUint256(shares)],
284
+ { blockIdentifier },
285
+ );
286
+ const amount = Web3Number.fromWei(
287
+ assets.toString(),
288
+ this.metadata.depositTokens[0].decimals,
289
+ );
290
+
291
+ const blockNumber =
292
+ typeof blockIdentifier === "number" || typeof blockIdentifier === "bigint"
293
+ ? Number(blockIdentifier)
294
+ : undefined;
295
+
296
+ const price = await this.pricer.getPrice(
297
+ this.metadata.depositTokens[0].symbol,
298
+ blockNumber,
299
+ );
300
+ const usdValue = Number(amount.toFixed(6)) * price.price;
301
+ return {
302
+ tokenInfo: this.asset(),
303
+ amount,
304
+ usdValue,
305
+ };
306
+ }
307
+
308
+ async getAUM(): Promise<{
309
+ net: SingleTokenInfo;
310
+ prevAum: Web3Number;
311
+ splits: PositionInfo[];
312
+ }> {
313
+ const underlying = this.asset(); // Deposit token (USDC, WBTC, etc.)
314
+ const depositTokenPrice = await this.pricer.getPrice(underlying.symbol);
315
+
316
+ const allPositions = await this.getVaultPositions();
317
+
318
+ let netAUM = new Web3Number(0, underlying.decimals);
319
+ for (const position of allPositions) {
320
+ if (position.token.address.eq(underlying.address)) {
321
+ // Same token as underlying - add amount directly
322
+ netAUM = netAUM.plus(position.amount);
323
+ } else {
324
+ // Different token - convert USD value to underlying token amount
325
+ const amountInUnderlying = new Web3Number(
326
+ position.usdValue.toString(),
327
+ underlying.decimals,
328
+ ).dividedBy(depositTokenPrice.price);
329
+ netAUM = netAUM.plus(amountInUnderlying);
330
+ }
331
+ }
332
+
333
+ const prevAum = await this.getPrevAUM();
334
+ logger.verbose(`${this.getTag()} Actual AUM: ${netAUM}`);
335
+
336
+ // Calculate BTCFI rewards contribution
337
+ const rewardAssets = await this.getRewardsAUM(prevAum);
338
+ logger.verbose(`${this.getTag()} Rewards AUM: ${rewardAssets}`);
339
+
340
+ // Sum up total AUM
341
+ const totalAUM = netAUM.plus(rewardAssets);
342
+ logger.verbose(`${this.getTag()} Total AUM: ${totalAUM}`);
343
+
344
+ const realAUM: PositionInfo = {
345
+ tokenInfo: underlying,
346
+ amount: netAUM,
347
+ usdValue: netAUM.toNumber() * depositTokenPrice.price,
348
+ apy: { apy: 0, type: APYType.BASE },
349
+ remarks: AUMTypes.FINALISED,
350
+ protocol: Protocols.NONE,
351
+ };
352
+
353
+ const estimatedAUMDelta: PositionInfo = {
354
+ tokenInfo: underlying,
355
+ amount: rewardAssets,
356
+ usdValue: rewardAssets.toNumber() * depositTokenPrice.price,
357
+ apy: { apy: 0, type: APYType.BASE },
358
+ remarks: AUMTypes.BTCFI,
359
+ protocol: Protocols.NONE,
360
+ };
361
+
362
+ return {
363
+ net: {
364
+ tokenInfo: underlying,
365
+ amount: totalAUM,
366
+ usdValue: totalAUM.toNumber() * depositTokenPrice.price,
367
+ },
368
+ prevAum,
369
+ splits: [realAUM, estimatedAUMDelta],
370
+ };
371
+ }
372
+
373
+ /**
374
+ * Get Vesu APYs for collateral and debt positions
375
+ */
376
+ async getVesuAPYs(): Promise<{
377
+ baseAPYs: number[];
378
+ rewardAPYs: number[];
379
+ positions: VaultPosition[];
380
+ }> {
381
+ const vesuAdapter = this.getAdapterById<VesuModifyPositionAdapter>(
382
+ this.metadata.additionalInfo.adapterIds!.vesu,
383
+ );
384
+
385
+ // Get Vesu pool data
386
+ const allVesuPools = await VesuAdapter.getVesuPools();
387
+ const pool = allVesuPools.pools.find((p: any) =>
388
+ vesuAdapter.config.poolId.eqString(num.getHexString(p.id)),
389
+ );
390
+
391
+ if (!pool) {
392
+ throw new Error(
393
+ `${this.getTag()}::getVesuAPYs: Pool not found for ${vesuAdapter.config.poolId.address}`,
394
+ );
395
+ }
396
+
397
+ // logger.verbose(
398
+ // `${this.getTag()}::getVesuAPYs: vesu-pool: ${JSON.stringify(pool)}`,
399
+ // );
400
+
401
+ // Get positions
402
+ const positions = await this.getVesuPositions();
403
+ logger.verbose(
404
+ `${this.getTag()}::getVesuAPYs: positions: ${JSON.stringify(positions)}`,
405
+ );
406
+
407
+ const baseAPYs: number[] = [];
408
+ const rewardAPYs: number[] = [];
409
+
410
+ // Find collateral and debt asset stats
411
+ const collateralAsset = pool.assets.find(
412
+ (a: any) =>
413
+ a.symbol.toLowerCase() ===
414
+ vesuAdapter.config.collateral.symbol.toLowerCase(),
415
+ )?.stats!;
416
+ const debtAsset = pool.assets.find(
417
+ (a: any) =>
418
+ a.symbol.toLowerCase() === vesuAdapter.config.debt.symbol.toLowerCase(),
419
+ )?.stats!;
420
+
421
+ if (!collateralAsset || !debtAsset) {
422
+ throw new Error(
423
+ `${this.getTag()}::getVesuAPYs: Collateral or debt asset stats not found`,
424
+ );
425
+ }
426
+
427
+ const supplyApy = Number(collateralAsset.supplyApy.value || 0) / 1e18;
428
+ const borrowApr = Number(debtAsset.borrowApr.value) / 1e18;
429
+ const collateralRewardsApr = this.metadata.additionalInfo.hasBtcFiRewards
430
+ ? Number(collateralAsset.btcFiSupplyApr?.value || "0") / 1e18
431
+ : 0;
432
+
433
+ logger.verbose(
434
+ `${this.getTag()}::getVesuAPYs: collateralRewardsApr: ${collateralRewardsApr}`,
435
+ );
436
+ logger.verbose(
437
+ `${this.getTag()}::getVesuAPYs: collateralAsset: ${JSON.stringify(collateralAsset)}`,
438
+ );
439
+ logger.verbose(
440
+ `${this.getTag()}::getVesuAPYs: supplyApy=${supplyApy}, borrowApr=${borrowApr}, collateralRewards=${collateralRewardsApr}`,
441
+ );
442
+
443
+ // Collateral: supply APY, Debt: borrow APR (cost)
444
+ baseAPYs.push(supplyApy, borrowApr);
445
+ rewardAPYs.push(collateralRewardsApr, 0);
446
+
447
+ logger.verbose(
448
+ `${this.getTag()}::getVesuAPYs: baseAPYs: ${JSON.stringify(baseAPYs)}, rewardAPYs: ${JSON.stringify(rewardAPYs)}`,
449
+ );
450
+
451
+ assert(
452
+ baseAPYs.length === positions.length,
453
+ "APYs and positions length mismatch",
454
+ );
455
+
456
+ return { baseAPYs, rewardAPYs, positions };
457
+ }
458
+
459
+ /**
460
+ * Get APY from the Hyper LST vault position
461
+ */
462
+ async getHyperVaultAPY(): Promise<{ apy: number; weight: number }> {
463
+ try {
464
+ const hyperAdapter = this.getAdapterById<SvkTrovesAdapter>(
465
+ this.metadata.additionalInfo.adapterIds!.hyper,
466
+ );
467
+ const positions = await hyperAdapter.getPositions();
468
+
469
+ if (positions.length === 0 || positions[0].amount.isZero()) {
470
+ return { apy: 0, weight: 0 };
471
+ }
472
+
473
+ const position = positions[0];
474
+ const apy = position.apy.apy;
475
+ const weight = position.usdValue;
476
+
477
+ logger.verbose(
478
+ `${this.getTag()}::getHyperVaultAPY: apy=${apy}, weight=${weight}`,
479
+ );
480
+
481
+ return { apy, weight };
482
+ } catch (error) {
483
+ logger.warn(
484
+ `${this.getTag()}::getHyperVaultAPY: Error getting Hyper vault APY: ${error}`,
485
+ );
486
+ return { apy: 0, weight: 0 };
487
+ }
488
+ }
489
+
490
+ /**
491
+ * Get APY for unused balance (idle funds in vault allocator)
492
+ */
493
+ protected async getUnusedBalanceAPY() {
494
+ return {
495
+ apy: 0,
496
+ weight: 0,
497
+ };
498
+ }
499
+
500
+ /**
501
+ * Compute weighted APY from individual APYs, weights, and total AUM
502
+ */
503
+ private computeAPY(
504
+ apys: number[],
505
+ weights: number[],
506
+ totalWeight: number,
507
+ ): number {
508
+ assert(apys.length === weights.length, "APYs and weights length mismatch");
509
+ const weightedSum = apys.reduce((acc, apy, i) => acc + apy * weights[i], 0);
510
+ logger.verbose(
511
+ `${this.getTag()} computeAPY: apys: ${JSON.stringify(apys)}, weights: ${JSON.stringify(weights)}, weightedSum: ${weightedSum}, totalWeight: ${totalWeight}`,
512
+ );
513
+ return weightedSum / totalWeight;
514
+ }
515
+
516
+ /**
517
+ * Calculate net APY from base and reward APYs
518
+ */
519
+ protected async returnNetAPY(
520
+ baseAPYs: number[],
521
+ rewardAPYs: number[],
522
+ weights: number[],
523
+ totalWeightUSD: number,
524
+ ) {
525
+ // If no positions, return 0
526
+ if (weights.every((p) => p == 0)) {
527
+ return {
528
+ net: 0,
529
+ splits: [
530
+ {
531
+ apy: 0,
532
+ id: "base",
533
+ },
534
+ {
535
+ apy: 0,
536
+ id: "btcfi",
537
+ },
538
+ ],
539
+ };
540
+ }
541
+
542
+ const baseAPY = this.computeAPY(baseAPYs, weights, totalWeightUSD);
543
+ const rewardAPY = this.computeAPY(rewardAPYs, weights, totalWeightUSD);
544
+ const netAPY = baseAPY + rewardAPY;
545
+ logger.verbose(
546
+ `${this.getTag()}::netAPY: net: ${netAPY}, baseAPY: ${baseAPY}, rewardAPY: ${rewardAPY}`,
547
+ );
548
+ return {
549
+ net: netAPY,
550
+ splits: [
551
+ {
552
+ apy: baseAPY,
553
+ id: "base",
554
+ },
555
+ {
556
+ apy: rewardAPY,
557
+ id: "btcfi",
558
+ },
559
+ ],
560
+ };
561
+ }
562
+
563
+ /**
564
+ * Calculate net APY across all positions (Vesu, Hyper vault, unused balance)
565
+ * weighted by USD value
566
+ */
567
+ // TODO: will be reviewing this later on, we only need BTCFI APY for now for AUM
568
+ async netAPY(): Promise<{
569
+ net: number;
570
+ splits: { apy: number; id: string }[];
571
+ }> {
572
+ try {
573
+ // Get Vesu APYs and positions
574
+ const { positions, baseAPYs, rewardAPYs } = await this.getVesuAPYs();
575
+
576
+ // Get Hyper vault APY
577
+ const hyperAPY = await this.getHyperVaultAPY();
578
+ baseAPYs.push(hyperAPY.apy);
579
+ rewardAPYs.push(0); // Hyper vault rewards already included in its APY
580
+
581
+ // Get unused balance APY
582
+ const unusedBalanceAPY = await this.getUnusedBalanceAPY();
583
+ baseAPYs.push(unusedBalanceAPY.apy);
584
+ rewardAPYs.push(0);
585
+
586
+ // Compute weights - debt usdValue is already negative from adapter
587
+ const weights = positions.map((p) => p.usdValue);
588
+ weights.push(hyperAPY.weight);
589
+ weights.push(unusedBalanceAPY.weight);
590
+
591
+ logger.verbose(
592
+ `${this.getTag()}::netAPY: weights: ${JSON.stringify(weights)}`,
593
+ );
594
+
595
+ // Calculate total weight (current total AUM in USD)
596
+ const totalWeightUSD = weights.reduce((sum, weight) => sum + weight, 0);
597
+
598
+ logger.verbose(
599
+ `${this.getTag()}::netAPY: totalWeightUSD: ${totalWeightUSD}`,
600
+ );
601
+
602
+ return this.returnNetAPY(baseAPYs, rewardAPYs, weights, totalWeightUSD);
603
+ } catch (error: any) {
604
+ logger.error(
605
+ `${this.getTag()}::netAPY: Error calculating net APY: ${error?.message || error}`,
606
+ );
607
+ return {
608
+ net: 0,
609
+ splits: [
610
+ {
611
+ apy: 0,
612
+ id: "base",
613
+ },
614
+ {
615
+ apy: 0,
616
+ id: "btcfi",
617
+ },
618
+ ],
619
+ };
620
+ }
621
+ }
622
+
623
+ /**
624
+ * Calculate future rewards (e.g. BTCFI rewards) contribution to AUM
625
+ * Similar to universal-strategy.tsx approach
626
+ */
627
+ protected async getRewardsAUM(prevAum: Web3Number): Promise<Web3Number> {
628
+ if (!this.metadata.additionalInfo.hasBtcFiRewards) {
629
+ return Web3Number.fromWei("0", this.asset().decimals);
630
+ }
631
+
632
+ try {
633
+ const lastReportTime = await this.contract.call(
634
+ "last_report_timestamp",
635
+ [],
636
+ );
637
+
638
+ // Calculate estimated growth from rewards
639
+ const netAPY = await this.netAPY();
640
+ // Account only 80% of value for conservative estimate
641
+ const btcfiAPY =
642
+ (netAPY.splits.find((s) => s.id === "btcfi")?.apy || 0) * 0.8;
643
+
644
+ if (!btcfiAPY) {
645
+ logger.verbose(
646
+ `${this.getTag()} No BTCFI APY found, returning 0 rewards`,
647
+ );
648
+ return Web3Number.fromWei("0", this.asset().decimals);
649
+ }
650
+
651
+ // Compute rewards contribution to AUM
652
+ const timeDiff = Math.round(Date.now() / 1000) - Number(lastReportTime);
653
+ const growthRate = (timeDiff * btcfiAPY) / (365 * 24 * 60 * 60);
654
+ const rewardAssets = prevAum.multipliedBy(growthRate);
655
+
656
+ logger.verbose(
657
+ `${this.getTag()} BtcFi AUM time difference: ${timeDiff}s`,
658
+ );
659
+ logger.verbose(`${this.getTag()} Previous AUM: ${prevAum.toString()}`);
660
+ logger.verbose(`${this.getTag()} BtcFi APY: ${btcfiAPY}`);
661
+ logger.verbose(`${this.getTag()} Growth rate: ${growthRate}`);
662
+ logger.verbose(
663
+ `${this.getTag()} Rewards AUM: ${rewardAssets.toString()}`,
664
+ );
665
+
666
+ return rewardAssets;
667
+ } catch (error: any) {
668
+ logger.warn(
669
+ `${this.getTag()} Error calculating rewards AUM: ${error?.message || error}`,
670
+ );
671
+ return Web3Number.fromWei("0", this.asset().decimals);
672
+ }
673
+ }
674
+
675
+ // TODO: can refactor later but seems redundant since not being used ANYWHERE
676
+ // Most of the fund management done through adapters only
677
+
678
+ async getFundManagementCall(params: {
679
+ isDeposit: boolean;
680
+ leg1DepositAmount: Web3Number;
681
+ }) {
682
+ logger.verbose(
683
+ `${this.getTag()}::getFundManagementCall params: ${JSON.stringify(params)}`,
684
+ );
685
+ const allAdapters = this.metadata.additionalInfo.adapters.map(
686
+ (a) => a.adapter,
687
+ );
688
+
689
+ if (!params.isDeposit) {
690
+ const unusedBalance = await this.getUnusedBalance();
691
+ logger.verbose(
692
+ `${this.getTag()}::getFundManagementCall unusedBalance: ${unusedBalance.amount}, required: ${params.leg1DepositAmount}`,
693
+ );
694
+ if (unusedBalance.amount.gte(params.leg1DepositAmount)) {
695
+ return null;
696
+ }
697
+
698
+ const adapters = await AdapterOptimizer.getAdapterToUse(
699
+ allAdapters,
700
+ false,
701
+ params.leg1DepositAmount,
702
+ );
703
+ if (adapters.length > 0) {
704
+ const proofsInfo = adapters.map((adapter) =>
705
+ adapter.getProofs(false, this.getMerkleTree()),
706
+ );
707
+ const calls: Call[] = [];
708
+ for (const info of proofsInfo) {
709
+ const manageCalls = await info.callConstructor({
710
+ amount: params.leg1DepositAmount,
711
+ });
712
+ const call = this.getManageCall(
713
+ this.getProofGroupsForManageCalls(manageCalls),
714
+ manageCalls,
715
+ );
716
+ calls.push(call);
717
+ }
718
+ return calls;
719
+ }
720
+
721
+ throw new Error(
722
+ `${this.getTag()}::getFundManagementCall: no adapters for withdraw: ${unusedBalance.amount}`,
723
+ );
724
+ }
725
+
726
+ const adapters = await AdapterOptimizer.getAdapterToUse(
727
+ allAdapters,
728
+ true,
729
+ params.leg1DepositAmount,
730
+ );
731
+ if (adapters.length > 0) {
732
+ const proofsInfo = adapters.map((adapter) =>
733
+ adapter.getProofs(true, this.getMerkleTree()),
734
+ );
735
+ const calls: Call[] = [];
736
+ for (const info of proofsInfo) {
737
+ const manageCalls = await info.callConstructor({
738
+ amount: params.leg1DepositAmount,
739
+ });
740
+ const call = this.getManageCall(
741
+ this.getProofGroupsForManageCalls(manageCalls),
742
+ manageCalls,
743
+ );
744
+ calls.push(call);
745
+ }
746
+ return calls;
747
+ }
748
+
749
+ throw new Error(
750
+ `${this.getTag()}::getFundManagementCall: no adapters for deposit: ${params.leg1DepositAmount}`,
751
+ );
752
+ }
753
+ }
754
+
755
+ function getBoostedxSTRKCarrySettings(
756
+ vaultSettings: BoostedxSTRKCarryStrategySettings,
757
+ ) {
758
+ vaultSettings.leafAdapters = [];
759
+
760
+ // Use metadata-driven token configuration
761
+ const depositToken = vaultSettings.depositToken;
762
+ const debtToken = vaultSettings.debtToken;
763
+ const lstToken = vaultSettings.lstHyperToken;
764
+
765
+ // Build dynamic adapter IDs based on token symbols
766
+ const adapterIds = {
767
+ vesu: `vesu_${depositToken.symbol.toLowerCase()}_${debtToken.symbol.toLowerCase()}`,
768
+ avnu: `avnu_${debtToken.symbol.toLowerCase()}_${lstToken.symbol.toLowerCase()}`,
769
+ hyper: `hyper_${lstToken.symbol.toLowerCase()}`,
770
+ transfer: `${depositToken.symbol.toLowerCase()}_transfer`,
771
+ };
772
+ vaultSettings.adapterIds = adapterIds;
773
+
774
+ const baseAdapterConfig: BaseAdapterConfig = {
775
+ baseToken: depositToken,
776
+ supportedPositions: [{ asset: depositToken, isDebt: false }],
777
+ networkConfig: getMainnetConfig(),
778
+ pricer: new PricerFromApi(getMainnetConfig(), Global.getDefaultTokens()),
779
+ vaultAllocator: vaultSettings.vaultAllocator,
780
+ vaultAddress: vaultSettings.vaultAddress,
781
+ };
782
+
783
+ // ── 1. VesuModifyPositionAdapter (deposit token collateral / borrow token debt) ──
784
+
785
+ const vesuModifyPositionAdapter = new VesuModifyPositionAdapter({
786
+ poolId: vaultSettings.vesuPoolId,
787
+ collateral: depositToken,
788
+ debt: debtToken,
789
+ targetLtv: vaultSettings.targetLTV,
790
+ maxLtv: vaultSettings.maxLTV,
791
+ ...baseAdapterConfig,
792
+ supportedPositions: [
793
+ { asset: depositToken, isDebt: false },
794
+ { asset: debtToken, isDebt: true },
795
+ ],
796
+ });
797
+
798
+ // ── 2. AvnuAdapter (borrow token ↔ LST swaps) ──
799
+ const avnuAdapter = new AvnuAdapter({
800
+ baseUrl: AVNU_QUOTE_URL,
801
+ avnuContract: AVNU_EXCHANGE,
802
+ slippage: 0.01,
803
+ minimumExtendedPriceDifferenceForSwapOpen: 0,
804
+ maximumExtendedPriceDifferenceForSwapClosing: 0,
805
+ ...baseAdapterConfig,
806
+ baseToken: debtToken,
807
+ supportedPositions: [
808
+ { asset: debtToken, isDebt: false },
809
+ { asset: lstToken, isDebt: false },
810
+ ],
811
+ });
812
+
813
+ // ── 3. SvkTrovesAdapter (deposit LST into / withdraw from Hyper vault) ──
814
+ const svkTrovesAdapter = new SvkTrovesAdapter({
815
+ ...baseAdapterConfig,
816
+ baseToken: lstToken,
817
+ supportedPositions: [{ asset: lstToken, isDebt: false }],
818
+ strategyVault: vaultSettings.hyperLstVaultAddress,
819
+ trovesStrategyId: vaultSettings.trovesStrategyId,
820
+ redeemRequestNFT: vaultSettings.hyperLstRedeemNFT,
821
+ });
822
+
823
+ // ── 5. CommonAdapter (approve + bring liquidity) ──
824
+ const commonAdapter = new CommonAdapter({
825
+ id: UNIVERSAL_MANAGE_IDS.FLASH_LOAN,
826
+ vaultAddress: vaultSettings.vaultAddress,
827
+ vaultAllocator: vaultSettings.vaultAllocator,
828
+ manager: vaultSettings.manager,
829
+ asset: depositToken.address,
830
+ });
831
+
832
+ // ── Register adapters for position tracking ──
833
+ vaultSettings.adapters.push(
834
+ { id: adapterIds.vesu, adapter: vesuModifyPositionAdapter },
835
+ // Used to track swapped funds in vaultAllocator
836
+ { id: adapterIds.avnu, adapter: avnuAdapter },
837
+ { id: adapterIds.hyper, adapter: svkTrovesAdapter },
838
+ );
839
+
840
+ // ── Register leaf adapters for merkle tree ──
841
+ // Vesu modify position
842
+ vaultSettings.leafAdapters.push(() =>
843
+ vesuModifyPositionAdapter.getDepositLeaf(),
844
+ );
845
+ vaultSettings.leafAdapters.push(() =>
846
+ vesuModifyPositionAdapter.getWithdrawLeaf(),
847
+ );
848
+
849
+ // Avnu swaps (borrow token ↔ LST)
850
+ vaultSettings.leafAdapters.push(() => avnuAdapter.getDepositLeaf());
851
+ vaultSettings.leafAdapters.push(() => avnuAdapter.getWithdrawLeaf());
852
+
853
+ // Hyper LST vault deposit / withdraw
854
+ vaultSettings.leafAdapters.push(() => svkTrovesAdapter.getDepositLeaf());
855
+ vaultSettings.leafAdapters.push(() => svkTrovesAdapter.getWithdrawLeaf());
856
+
857
+ // Bring liquidity back to vault
858
+ vaultSettings.leafAdapters.push(
859
+ commonAdapter
860
+ .getApproveAdapter(
861
+ depositToken.address,
862
+ vaultSettings.vaultAddress,
863
+ UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY,
864
+ )
865
+ .bind(commonAdapter),
866
+ );
867
+ vaultSettings.leafAdapters.push(
868
+ commonAdapter
869
+ .getBringLiquidityAdapter(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY)
870
+ .bind(commonAdapter),
871
+ );
872
+
873
+ return vaultSettings;
874
+ }
875
+
876
+ const boostedxSTRKCarrySettings: BoostedxSTRKCarryStrategySettings = {
877
+ vaultAddress: ContractAddr.from(
878
+ "0xcdb0e3b2e076a2cdc4ee958b726b47c066239ef91c5ac80c94cf814147b84",
879
+ ),
880
+ manager: ContractAddr.from(
881
+ "0x72eea9bac9fa8cfffda637d3b990851446860c6fd8987d6cb50e659b01ee50f",
882
+ ),
883
+ vaultAllocator: ContractAddr.from(
884
+ "0x6d3101cff7f821412a99ebe23bb31a1950f93276285102eb4313e3601f5f927",
885
+ ),
886
+ redeemRequestNFT: ContractAddr.from(
887
+ "0x47dcc6889ca8db4e9eea8f55421e10f8ce7e356ccb45260a1c49a76f733c309",
888
+ ),
889
+ // Not there for USDC but will be there for WBTC
890
+ aumOracle: ContractAddr.from("0x0"),
891
+ leafAdapters: [],
892
+ adapters: [],
893
+ // Calc using the maxLTV / targetLTV (0.5)
894
+ targetHealthFactor: 1.32,
895
+ // Calc using the maxLTV / maxAcceptableLTV (0.55)
896
+ minHealthFactor: 1.2,
897
+ vesuPoolId: VesuPools.Prime,
898
+ // New metadata-driven token configuration
899
+ depositToken: Global.getDefaultTokens().find((t) => t.symbol === "USDC")!,
900
+ debtToken: Global.getDefaultTokens().find((t) => t.symbol === "STRK")!,
901
+ lstHyperToken: Global.getDefaultTokens().find((t) => t.symbol === "xSTRK")!,
902
+ maxLTV: 0.66,
903
+ targetLTV: 0.5,
904
+ // BTC.Fi rewards flag - false for USDC (uses old report flow)
905
+ hasBtcFiRewards: false,
906
+ // New fields
907
+ hyperLstVaultAddress: ContractAddr.from(
908
+ "0x46c7a54c82b1fe374353859f554a40b8bd31d3e30f742901579e7b57b1b5960",
909
+ ),
910
+ hyperLstRedeemNFT: ContractAddr.from(
911
+ "0x51e40b839dc0c2feca923f863072673b94abfa2483345be3b30b457a90d095",
912
+ ),
913
+ trovesStrategyId: "hyper_xstrk",
914
+ };
915
+
916
+ const wbtcBoostedSettings: BoostedxSTRKCarryStrategySettings = {
917
+ vaultAddress: ContractAddr.from(
918
+ "0x69e081304917cbf2d39772483aa8b4b389c1a55d658493b28139e921cf83b6",
919
+ ),
920
+ manager: ContractAddr.from(
921
+ "0x1936da7771d657fa6c41a2c6a7b64f7fc9c88782098f537ea4180263930f9a8",
922
+ ),
923
+ vaultAllocator: ContractAddr.from(
924
+ "0x68008fce59ad4dfb63cc293f7bf020d12902957ea85f95c4e1c1e98f415b668",
925
+ ),
926
+ redeemRequestNFT: ContractAddr.from(
927
+ "0x1ed34e1a94ca6dd766ded6f207c67a3857ea489d16b42cd03403c5071f131c7",
928
+ ),
929
+ aumOracle: ContractAddr.from(
930
+ "0xed774c6484dba664354ebaa1c993a37e9ad4c14870cda0762ae0194caed056",
931
+ ),
932
+ leafAdapters: [],
933
+ adapters: [],
934
+ targetHealthFactor: 1.32,
935
+ minHealthFactor: 1.2,
936
+ vesuPoolId: VesuPools.Prime,
937
+ depositToken: Global.getDefaultTokens().find((t) => t.symbol === "WBTC")!,
938
+ debtToken: Global.getDefaultTokens().find((t) => t.symbol === "STRK")!,
939
+ lstHyperToken: Global.getDefaultTokens().find((t) => t.symbol === "xSTRK")!,
940
+ maxLTV: 0.66,
941
+ targetLTV: 0.5,
942
+ hasBtcFiRewards: true,
943
+ hyperLstVaultAddress: ContractAddr.from(
944
+ "0x46c7a54c82b1fe374353859f554a40b8bd31d3e30f742901579e7b57b1b5960",
945
+ ),
946
+ hyperLstRedeemNFT: ContractAddr.from(
947
+ "0x51e40b839dc0c2feca923f863072673b94abfa2483345be3b30b457a90d095",
948
+ ),
949
+ trovesStrategyId: "hyper_xstrk",
950
+ };
951
+
952
+ function getStrategySettings(
953
+ settings: BoostedxSTRKCarryStrategySettings,
954
+ meta: { id: string; name: string; launchBlock: number },
955
+ ): IStrategyMetadata<BoostedxSTRKCarryStrategySettings> {
956
+ const depositToken = settings.depositToken;
957
+ const debtToken = settings.debtToken;
958
+ const lstToken = settings.lstHyperToken;
959
+
960
+ return {
961
+ id: meta.id,
962
+ name: meta.name,
963
+ description: `Deposits ${depositToken.symbol} as collateral on Vesu, borrows ${debtToken.symbol}, swaps to ${lstToken.symbol}, and deposits into Hyper-${lstToken.symbol} for boosted yield`,
964
+ address: settings.vaultAddress,
965
+ launchBlock: meta.launchBlock,
966
+ type: "ERC4626" as const,
967
+ vaultType: {
968
+ type: VaultType.META_VAULT,
969
+ description: `Deposits ${depositToken.symbol} as collateral on Vesu, borrows ${debtToken.symbol}, swaps to ${lstToken.symbol}, and deposits into Hyper-${lstToken.symbol} for boosted yield`,
970
+ },
971
+ depositTokens: [depositToken],
972
+ additionalInfo: getBoostedxSTRKCarrySettings(settings),
973
+ // TODO: config lateron
974
+ risk: {
975
+ riskFactor: [],
976
+ netRisk: 0,
977
+ notARisks: [],
978
+ },
979
+ protocols: [Protocols.VESU, Protocols.TROVES],
980
+ curator: {
981
+ name: "Unwrap Labs",
982
+ logo: "https://assets.troves.fi/integrations/unwraplabs/white.png",
983
+ },
984
+ settings: {
985
+ maxTVL: Web3Number.fromWei(0, depositToken.decimals),
986
+ isPaused: false,
987
+ isAudited: false,
988
+ isInstantWithdrawal: false,
989
+ hideHarvestInfo: true,
990
+ quoteToken: depositToken,
991
+ alerts: [
992
+ {
993
+ tab: "withdraw" as const,
994
+ text: "On withdrawal, you will receive an NFT representing your withdrawal request. The funds will be automatically sent to your wallet (NFT owner) in 1-2 hours. You can monitor the status in transactions tab.",
995
+ type: "info" as const,
996
+ },
997
+ ],
998
+ },
999
+ contractDetails: getContractDetails(settings),
1000
+ // TODO: config later
1001
+ faqs: [],
1002
+ investmentSteps: [
1003
+ `Deposit ${depositToken.symbol} into the vault`,
1004
+ `${depositToken.symbol} is supplied as collateral on Vesu, ${debtToken.symbol} is borrowed`,
1005
+ `Borrowed ${debtToken.symbol} is swapped to ${lstToken.symbol} via Avnu`,
1006
+ `${lstToken.symbol} is deposited into the Hyper-${lstToken.symbol} vault for additional yield`,
1007
+ `On withdrawal, the pipeline reverses to return ${depositToken.symbol}`,
1008
+ ],
1009
+ // TODO: config later
1010
+ tags: [StrategyTag.META_VAULT],
1011
+ security: {
1012
+ auditStatus: AuditStatus.AUDITED,
1013
+ sourceCode: {
1014
+ type: SourceCodeType.CLOSED_SOURCE,
1015
+ contractLink: "https://github.com/trovesfi/troves-contracts",
1016
+ },
1017
+ accessControl: {
1018
+ type: AccessControlType.STANDARD_ACCOUNT,
1019
+ addresses: [ContractAddr.from("0x0")],
1020
+ timeLock: "2 Days",
1021
+ },
1022
+ },
1023
+ redemptionInfo: {
1024
+ instantWithdrawalVault: InstantWithdrawalVault.NO,
1025
+ redemptionsInfo: [
1026
+ {
1027
+ title: "Typical Duration",
1028
+ description: "1-2 hours",
1029
+ },
1030
+ ],
1031
+ alerts: [
1032
+ {
1033
+ type: "info",
1034
+ text: "Redemption times are estimates and may vary based on network conditions and liquidity requirements.",
1035
+ tab: "withdraw",
1036
+ },
1037
+ ],
1038
+ },
1039
+ usualTimeToEarnings: null,
1040
+ usualTimeToEarningsDescription: null,
1041
+ };
1042
+ }
1043
+
1044
+ // TODO: rename to BoostedCollateralStrategies later on or smthin
1045
+ export const BoostedxSTRKCarryStrategies: IStrategyMetadata<BoostedxSTRKCarryStrategySettings>[] =
1046
+ [
1047
+ getStrategySettings(boostedxSTRKCarrySettings, {
1048
+ id: "usdc_boosted",
1049
+ name: "USDC Boosted",
1050
+ launchBlock: 8742931,
1051
+ }),
1052
+ getStrategySettings(wbtcBoostedSettings, {
1053
+ id: "wbtc_boosted",
1054
+ name: "WBTC Boosted",
1055
+ launchBlock: 9803209,
1056
+ }),
1057
+ ];