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

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.
@@ -0,0 +1,693 @@
1
+ import {
2
+ getMainnetConfig,
3
+ IConfig,
4
+ IStrategyMetadata,
5
+ Protocols,
6
+ TokenInfo,
7
+ AuditStatus,
8
+ SourceCodeType,
9
+ AccessControlType,
10
+ InstantWithdrawalVault,
11
+ VaultType,
12
+ RedemptionInfo,
13
+ StrategyTag,
14
+ VaultPosition,
15
+ } from "@/interfaces";
16
+ import {
17
+ AUMTypes,
18
+ getContractDetails,
19
+ UNIVERSAL_MANAGE_IDS,
20
+ UniversalStrategySettings,
21
+ } from "./universal-strategy";
22
+ import { PricerBase } from "@/modules/pricerBase";
23
+ import { ContractAddr, Web3Number } from "@/dataTypes";
24
+ import { Global } from "@/global";
25
+ import {
26
+ APYType,
27
+ AvnuAdapter,
28
+ AvnuAdapterConfig,
29
+ BaseAdapterConfig,
30
+ CommonAdapter,
31
+ ManageCall,
32
+ PositionInfo,
33
+ TokenTransferAdapter,
34
+ VesuModifyPositionAdapter,
35
+ } from "./universal-adapters";
36
+ import { SvkTrovesAdapter } from "./universal-adapters/svk-troves-adapter";
37
+ import { VesuPools } from "./universal-adapters/vesu-adapter";
38
+ import {
39
+ AVNU_EXCHANGE,
40
+ AVNU_QUOTE_URL,
41
+ } from "./universal-adapters/adapter-utils";
42
+ import { PricerFromApi, ERC20 } from "@/modules";
43
+ import { assert, logger } from "@/utils";
44
+ import { SingleActionAmount, SingleTokenInfo } from "./base-strategy";
45
+ import { SVKStrategy } from "./svk-strategy";
46
+ import { BlockIdentifier, Call, uint256 } from "starknet";
47
+ import { AdapterOptimizer } from "./universal-adapters/adapter-optimizer";
48
+
49
+ export interface USDCBoostedStrategySettings extends UniversalStrategySettings {
50
+ vesuPoolId: ContractAddr;
51
+ collateralToken: TokenInfo; // USDC
52
+ debtToken: TokenInfo; // STRK
53
+ maxLTV: number; // e.g. 0.66
54
+ targetLTV: number;
55
+ hyperxSTRKVaultAddress: ContractAddr;
56
+ }
57
+
58
+ export class USDCBoostedStrategy<
59
+ S extends USDCBoostedStrategySettings,
60
+ > extends SVKStrategy<S> {
61
+ constructor(
62
+ config: IConfig,
63
+ pricer: PricerBase,
64
+ metadata: IStrategyMetadata<S>,
65
+ ) {
66
+ super(config, pricer, metadata);
67
+
68
+ this.metadata.additionalInfo.adapters.forEach((adapter) => {
69
+ adapter.adapter.config.networkConfig = this.config;
70
+ adapter.adapter.config.pricer = this.pricer;
71
+ if ((adapter.adapter as any)._vesuAdapter) {
72
+ (adapter.adapter as any)._vesuAdapter.networkConfig = this.config;
73
+ (adapter.adapter as any)._vesuAdapter.pricer = this.pricer;
74
+ }
75
+ });
76
+ }
77
+
78
+ getTag(): string {
79
+ return `${USDCBoostedStrategy.name}:${this.metadata.name}`;
80
+ }
81
+
82
+ private getAdapterById<T>(id: string): T {
83
+ const entry = this.metadata.additionalInfo.adapters.find(
84
+ (a) => a.id === id,
85
+ );
86
+ if (!entry) {
87
+ throw new Error(
88
+ `${this.getTag()}::getAdapterById: adapter not found for id "${id}"`,
89
+ );
90
+ }
91
+ return entry.adapter as T;
92
+ }
93
+
94
+ async getVesuModifyPositionCall(params: {
95
+ isDeposit: boolean;
96
+ leg1DepositAmount: Web3Number;
97
+ debtAmount?: Web3Number;
98
+ }): Promise<Call> {
99
+ logger.verbose(
100
+ `${this.getTag()}::getVesuModifyPositionCall isDeposit=${params.isDeposit}, amount=${params.leg1DepositAmount}, debtAmount=${params.debtAmount?.toNumber()}`,
101
+ );
102
+ return this.buildManageCallFromAdapter(
103
+ this.getAdapterById<VesuModifyPositionAdapter>("vesu_usdc_strk"),
104
+ params.isDeposit,
105
+ params.leg1DepositAmount,
106
+ params.debtAmount ? { debtAmount: params.debtAmount } : undefined,
107
+ );
108
+ }
109
+
110
+ async getVesuPositions(): Promise<VaultPosition[]> {
111
+ const positions =
112
+ await this.getAdapterById<VesuModifyPositionAdapter>(
113
+ "vesu_usdc_strk",
114
+ ).getPositions();
115
+ return positions.map((p) => ({
116
+ amount: p.amount,
117
+ usdValue: p.usdValue,
118
+ remarks: p.remarks ?? "",
119
+ token: p.tokenInfo,
120
+ protocol: p.protocol,
121
+ }));
122
+ }
123
+
124
+ async getVesuHealthFactor(
125
+ blockNumber: BlockIdentifier = "latest",
126
+ ): Promise<number> {
127
+ const vesuAdapter =
128
+ this.getAdapterById<VesuModifyPositionAdapter>("vesu_usdc_strk");
129
+ return await vesuAdapter._vesuAdapter.getHealthFactor(blockNumber);
130
+ }
131
+
132
+ // TODO: will have to still modify for instant withdrawals as of now
133
+ async getModifyHyperPositionCall(params: {
134
+ isDeposit: boolean;
135
+ amount: Web3Number;
136
+ }): Promise<Call> {
137
+ logger.verbose(
138
+ `${this.getTag()}::getModifyHyperPositionCall isDeposit=${params.isDeposit}, amount=${params.amount}`,
139
+ );
140
+ return this.buildManageCallFromAdapter(
141
+ this.getAdapterById<SvkTrovesAdapter>("hyper_xstrk"),
142
+ params.isDeposit,
143
+ params.amount,
144
+ );
145
+ }
146
+
147
+ async getAvnuSwapCall(params: {
148
+ isDeposit: boolean;
149
+ amount: Web3Number;
150
+ }): Promise<Call> {
151
+ logger.verbose(
152
+ `${this.getTag()}::getAvnuSwapCall isDeposit=${params.isDeposit}, amount=${params.amount}`,
153
+ );
154
+ return this.buildManageCallFromAdapter(
155
+ this.getAdapterById<AvnuAdapter>("avnu_strk_xstrk"),
156
+ params.isDeposit,
157
+ params.amount,
158
+ );
159
+ }
160
+
161
+ /**
162
+ * Returns the USDC balance sitting idle inside the vault contract itself.
163
+ * This balance can offset the required liquidity during withdrawal processing.
164
+ */
165
+ async getUnusedBalanceVault(): Promise<Web3Number> {
166
+ const collateralToken = this.metadata.additionalInfo.collateralToken;
167
+ return new ERC20(this.config).balanceOf(
168
+ collateralToken.address,
169
+ this.metadata.additionalInfo.vaultAddress,
170
+ collateralToken.decimals,
171
+ );
172
+ }
173
+
174
+ /**
175
+ * Returns the current STRK balance sitting unused in the vault allocator.
176
+ * This covers STRK from prior borrow cycles that hasn't been swapped yet.
177
+ */
178
+ // Technically we can use this or we can even use the avnu-adapter to get the unused debt
179
+ async getUnusedDebt(): Promise<Web3Number> {
180
+ const debtToken = this.metadata.additionalInfo.debtToken;
181
+ return new ERC20(this.config).balanceOf(
182
+ debtToken.address,
183
+ this.metadata.additionalInfo.vaultAllocator,
184
+ debtToken.decimals,
185
+ );
186
+ }
187
+
188
+ /**
189
+ * Returns the xSTRK balance sitting in the vault allocator.
190
+ * This is non-zero when the previous cycle's request_redeem on the HyperPosition
191
+ * has been settled — the redeemed xSTRK lands here and is ready to be swapped.
192
+ */
193
+ async getxSTRKInVault(): Promise<Web3Number> {
194
+ const xstrkToken = Global.getDefaultTokens().find(
195
+ (t) => t.symbol === "xSTRK",
196
+ );
197
+ if (!xstrkToken) {
198
+ throw new Error(`${this.getTag()}:: xSTRK token not found`);
199
+ }
200
+ return new ERC20(this.config).balanceOf(
201
+ xstrkToken.address.address,
202
+ this.metadata.additionalInfo.vaultAllocator,
203
+ xstrkToken.decimals,
204
+ );
205
+ }
206
+
207
+ /**
208
+ * Simulates depositing `depositAmount` USDC on Vesu and returns the
209
+ * incremental STRK that would be borrowed. Needed to size the AVNU swap
210
+ * call that is batched together with the Vesu call in the same transaction.
211
+ */
212
+ async computeVesuDepositBorrowAmount(
213
+ depositAmount: Web3Number,
214
+ ): Promise<Web3Number> {
215
+ return this.getAdapterById<VesuModifyPositionAdapter>(
216
+ "vesu_usdc_strk",
217
+ ).getExpectedDepositDebtDelta(depositAmount);
218
+ }
219
+
220
+ async computeVesuWithdrawDebtDelta(
221
+ withdrawAmount: Web3Number,
222
+ ): Promise<Web3Number> {
223
+ return this.getAdapterById<VesuModifyPositionAdapter>(
224
+ "vesu_usdc_strk",
225
+ ).getExpectedWithdrawDebtDelta(withdrawAmount);
226
+ }
227
+
228
+ async getPendingHyperAssets(): Promise<Web3Number> {
229
+ const xstrkToken = Global.getDefaultTokens().find(
230
+ (t) => t.symbol === "xSTRK",
231
+ )!;
232
+ // TODO: hardcoding for now ( since this is temp method we will use due_assets_from_owner later on )
233
+ const hyperXstrkRedeemNFT = ContractAddr.from(
234
+ "0x51e40b839dc0c2feca923f863072673b94abfa2483345be3b30b457a90d095"
235
+ );
236
+ return this.getAdapterById<SvkTrovesAdapter>(
237
+ "hyper_xstrk",
238
+ ).getPendingAssetsFromOwnerNFTMethod(
239
+ hyperXstrkRedeemNFT,
240
+ this.metadata.additionalInfo.vaultAllocator,
241
+ xstrkToken.decimals,
242
+ );
243
+ }
244
+
245
+ // TODO: rn we are just making these functions in the strategy itself but
246
+ // later on we will move them to the SVKStrategy for generalization
247
+
248
+ async getUserTVL(
249
+ user: ContractAddr,
250
+ blockIdentifier: BlockIdentifier = "latest",
251
+ ) {
252
+ const shares: any = await this.contract.call("balanceOf", [user.address], {
253
+ blockIdentifier,
254
+ });
255
+ const assets: any = await this.contract.call(
256
+ "convert_to_assets",
257
+ [uint256.bnToUint256(shares)],
258
+ { blockIdentifier },
259
+ );
260
+ const amount = Web3Number.fromWei(
261
+ assets.toString(),
262
+ this.metadata.depositTokens[0].decimals,
263
+ );
264
+
265
+ const blockNumber =
266
+ typeof blockIdentifier === "number" || typeof blockIdentifier === "bigint"
267
+ ? Number(blockIdentifier)
268
+ : undefined;
269
+
270
+ const price = await this.pricer.getPrice(
271
+ this.metadata.depositTokens[0].symbol,
272
+ blockNumber,
273
+ );
274
+ const usdValue = Number(amount.toFixed(6)) * price.price;
275
+ return {
276
+ tokenInfo: this.asset(),
277
+ amount,
278
+ usdValue,
279
+ };
280
+ }
281
+
282
+ async getTVL() {
283
+ const assets = await this.contract.total_assets();
284
+ const amount = Web3Number.fromWei(
285
+ assets.toString(),
286
+ this.metadata.depositTokens[0].decimals,
287
+ );
288
+ const price = await this.pricer.getPrice(
289
+ this.metadata.depositTokens[0].symbol,
290
+ );
291
+ const usdValue = Number(amount.toFixed(6)) * price.price;
292
+ return {
293
+ tokenInfo: this.asset(),
294
+ amount,
295
+ usdValue,
296
+ };
297
+ }
298
+
299
+ async getAUM(): Promise<{
300
+ net: SingleTokenInfo;
301
+ prevAum: Web3Number;
302
+ splits: PositionInfo[];
303
+ }> {
304
+ const underlying = this.asset(); // USDC
305
+ const usdcPrice = await this.pricer.getPrice(underlying.symbol);
306
+
307
+ const allPositions: PositionInfo[] = [];
308
+ for (const adapter of this.metadata.additionalInfo.adapters) {
309
+ const positions = await adapter.adapter.getPositions();
310
+ allPositions.push(...positions);
311
+ }
312
+
313
+ let netAUM = new Web3Number(0, underlying.decimals);
314
+ for (const position of allPositions) {
315
+ if (position.tokenInfo.address.eq(underlying.address)) {
316
+ netAUM = netAUM.plus(position.amount);
317
+ } else {
318
+ const valueInUSDC = position.usdValue;
319
+ netAUM = netAUM.plus(valueInUSDC);
320
+ }
321
+ }
322
+
323
+ const unusedBalance = await this.getUnusedBalance();
324
+ logger.verbose(
325
+ `${this.getTag()} unused balance: ${unusedBalance.amount.toNumber()}`,
326
+ );
327
+
328
+ // Since this is only in USDC -> we are taking directly the amount ( ow can use the usdValue as well )
329
+ netAUM = netAUM.plus(unusedBalance.amount);
330
+
331
+ const prevAum = await this.getPrevAUM();
332
+ logger.verbose(`${this.getTag()} AUM: ${netAUM}`);
333
+
334
+ const realAUM: PositionInfo = {
335
+ tokenInfo: underlying,
336
+ amount: netAUM,
337
+ usdValue: netAUM.toNumber() * usdcPrice.price,
338
+ apy: { apy: 0, type: APYType.BASE },
339
+ remarks: AUMTypes.FINALISED,
340
+ protocol: Protocols.NONE,
341
+ };
342
+
343
+ // TODO: we can completely remove it since this strategy doesnt include rewards
344
+ // but for now keeping it for the structure -> eitherways its 0
345
+ const estimatedAUMDelta: PositionInfo = {
346
+ tokenInfo: underlying,
347
+ amount: Web3Number.fromWei("0", underlying.decimals),
348
+ usdValue: 0,
349
+ apy: { apy: 0, type: APYType.BASE },
350
+ remarks: AUMTypes.DEFISPRING,
351
+ protocol: Protocols.NONE,
352
+ };
353
+
354
+ return {
355
+ net: {
356
+ tokenInfo: underlying,
357
+ amount: netAUM,
358
+ usdValue: netAUM.toNumber() * usdcPrice.price,
359
+ },
360
+ prevAum,
361
+ splits: [realAUM, estimatedAUMDelta],
362
+ };
363
+ }
364
+
365
+ // TODO: can refactor later but seems redundant since not being used ANYWHERE
366
+ // Most of the fund management done through adapters only
367
+
368
+ async getFundManagementCall(params: {
369
+ isDeposit: boolean;
370
+ leg1DepositAmount: Web3Number;
371
+ }) {
372
+ logger.verbose(
373
+ `${this.getTag()}::getFundManagementCall params: ${JSON.stringify(params)}`,
374
+ );
375
+ const allAdapters = this.metadata.additionalInfo.adapters.map(
376
+ (a) => a.adapter,
377
+ );
378
+
379
+ if (!params.isDeposit) {
380
+ const unusedBalance = await this.getUnusedBalance();
381
+ logger.verbose(
382
+ `${this.getTag()}::getFundManagementCall unusedBalance: ${unusedBalance.amount}, required: ${params.leg1DepositAmount}`,
383
+ );
384
+ if (unusedBalance.amount.gte(params.leg1DepositAmount)) {
385
+ return null;
386
+ }
387
+
388
+ const adapters = await AdapterOptimizer.getAdapterToUse(
389
+ allAdapters,
390
+ false,
391
+ params.leg1DepositAmount,
392
+ );
393
+ if (adapters.length > 0) {
394
+ const proofsInfo = adapters.map((adapter) =>
395
+ adapter.getProofs(false, this.getMerkleTree()),
396
+ );
397
+ const calls: Call[] = [];
398
+ for (const info of proofsInfo) {
399
+ const manageCalls = await info.callConstructor({
400
+ amount: params.leg1DepositAmount,
401
+ });
402
+ const call = this.getManageCall(
403
+ this.getProofGroupsForManageCalls(manageCalls),
404
+ manageCalls,
405
+ );
406
+ calls.push(call);
407
+ }
408
+ return calls;
409
+ }
410
+
411
+ throw new Error(
412
+ `${this.getTag()}::getFundManagementCall: no adapters for withdraw: ${unusedBalance.amount}`,
413
+ );
414
+ }
415
+
416
+ const adapters = await AdapterOptimizer.getAdapterToUse(
417
+ allAdapters,
418
+ true,
419
+ params.leg1DepositAmount,
420
+ );
421
+ if (adapters.length > 0) {
422
+ const proofsInfo = adapters.map((adapter) =>
423
+ adapter.getProofs(true, this.getMerkleTree()),
424
+ );
425
+ const calls: Call[] = [];
426
+ for (const info of proofsInfo) {
427
+ const manageCalls = await info.callConstructor({
428
+ amount: params.leg1DepositAmount,
429
+ });
430
+ const call = this.getManageCall(
431
+ this.getProofGroupsForManageCalls(manageCalls),
432
+ manageCalls,
433
+ );
434
+ calls.push(call);
435
+ }
436
+ return calls;
437
+ }
438
+
439
+ throw new Error(
440
+ `${this.getTag()}::getFundManagementCall: no adapters for deposit: ${params.leg1DepositAmount}`,
441
+ );
442
+ }
443
+ }
444
+
445
+ function getUSDCBoostedSettings(vaultSettings: USDCBoostedStrategySettings) {
446
+ vaultSettings.leafAdapters = [];
447
+
448
+ const xSTRKToken = Global.getDefaultTokens().find(
449
+ (t) => t.symbol === "xSTRK",
450
+ )!;
451
+
452
+ // TODO: we are hardcoding for now since one strategy but we can change later on if req.
453
+ const USDCToken = Global.getDefaultTokens().find((t) => t.symbol === "USDC")!;
454
+ const STRKToken = Global.getDefaultTokens().find((t) => t.symbol === "STRK")!;
455
+
456
+ const baseAdapterConfig: BaseAdapterConfig = {
457
+ baseToken: USDCToken,
458
+ supportedPositions: [{ asset: USDCToken, isDebt: false }],
459
+ networkConfig: getMainnetConfig(),
460
+ pricer: new PricerFromApi(getMainnetConfig(), Global.getDefaultTokens()),
461
+ vaultAllocator: vaultSettings.vaultAllocator,
462
+ vaultAddress: vaultSettings.vaultAddress,
463
+ };
464
+
465
+ // ── 1. VesuModifyPositionAdapter (USDC collateral / STRK debt) ──
466
+
467
+ const vesuModifyPositionAdapter = new VesuModifyPositionAdapter({
468
+ poolId: vaultSettings.vesuPoolId,
469
+ collateral: vaultSettings.collateralToken,
470
+ debt: vaultSettings.debtToken,
471
+ targetLtv: vaultSettings.targetLTV,
472
+ maxLtv: vaultSettings.maxLTV,
473
+ ...baseAdapterConfig,
474
+ supportedPositions: [
475
+ { asset: USDCToken, isDebt: false },
476
+ { asset: STRKToken, isDebt: true },
477
+ ],
478
+ });
479
+
480
+ // ── 2. AvnuAdapter (STRK ↔ xSTRK swaps) ──
481
+ const avnuAdapter = new AvnuAdapter({
482
+ baseUrl: AVNU_QUOTE_URL,
483
+ avnuContract: AVNU_EXCHANGE,
484
+ slippage: 0.01,
485
+ minimumExtendedPriceDifferenceForSwapOpen: 0,
486
+ maximumExtendedPriceDifferenceForSwapClosing: 0,
487
+ ...baseAdapterConfig,
488
+ baseToken: STRKToken,
489
+ supportedPositions: [
490
+ { asset: STRKToken, isDebt: false },
491
+ { asset: xSTRKToken, isDebt: false },
492
+ ],
493
+ });
494
+
495
+ // ── 3. SvkTrovesAdapter (deposit xSTRK into / withdraw from Hyper-xSTRK) ──
496
+ const svkTrovesAdapter = new SvkTrovesAdapter({
497
+ ...baseAdapterConfig,
498
+ baseToken: xSTRKToken,
499
+ supportedPositions: [{ asset: xSTRKToken, isDebt: false }],
500
+ strategyVault: vaultSettings.hyperxSTRKVaultAddress,
501
+ trovesStrategyId: "hyper_xstrk",
502
+ });
503
+
504
+ // Used to track the funds in toAddress ( Vault )
505
+ // ── 4. TokenTransferAdapter ──
506
+ const usdcTransferAdapter = new TokenTransferAdapter({
507
+ ...baseAdapterConfig,
508
+ fromAddress: vaultSettings.vaultAllocator,
509
+ toAddress: vaultSettings.vaultAddress,
510
+ });
511
+
512
+ // ── 5. CommonAdapter (approve + bring liquidity) ──
513
+ const commonAdapter = new CommonAdapter({
514
+ id: UNIVERSAL_MANAGE_IDS.FLASH_LOAN,
515
+ vaultAddress: vaultSettings.vaultAddress,
516
+ vaultAllocator: vaultSettings.vaultAllocator,
517
+ manager: vaultSettings.manager,
518
+ asset: USDCToken.address,
519
+ });
520
+
521
+ // ── Register adapters for position tracking ──
522
+ vaultSettings.adapters.push(
523
+ // TODO: generalize the ids
524
+ { id: "vesu_usdc_strk", adapter: vesuModifyPositionAdapter },
525
+ // Used to track swapped funds in vaultAllocator
526
+ { id: "avnu_strk_xstrk", adapter: avnuAdapter },
527
+ { id: "hyper_xstrk", adapter: svkTrovesAdapter },
528
+ { id: "usdc_transfer", adapter: usdcTransferAdapter },
529
+ );
530
+
531
+ // ── Register leaf adapters for merkle tree ──
532
+ // Vesu modify position
533
+ vaultSettings.leafAdapters.push(() =>
534
+ vesuModifyPositionAdapter.getDepositLeaf(),
535
+ );
536
+ vaultSettings.leafAdapters.push(() =>
537
+ vesuModifyPositionAdapter.getWithdrawLeaf(),
538
+ );
539
+
540
+ // Avnu swaps (STRK ↔ xSTRK)
541
+ vaultSettings.leafAdapters.push(() => avnuAdapter.getDepositLeaf());
542
+ vaultSettings.leafAdapters.push(() => avnuAdapter.getWithdrawLeaf());
543
+
544
+ // Hyper-xSTRK deposit / withdraw
545
+ vaultSettings.leafAdapters.push(() => svkTrovesAdapter.getDepositLeaf());
546
+ vaultSettings.leafAdapters.push(() => svkTrovesAdapter.getWithdrawLeaf());
547
+
548
+ // Bring liquidity back to vault
549
+ vaultSettings.leafAdapters.push(
550
+ commonAdapter
551
+ .getApproveAdapter(
552
+ USDCToken.address,
553
+ vaultSettings.vaultAddress,
554
+ UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY,
555
+ )
556
+ .bind(commonAdapter),
557
+ );
558
+ vaultSettings.leafAdapters.push(
559
+ commonAdapter
560
+ .getBringLiquidityAdapter(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY)
561
+ .bind(commonAdapter),
562
+ );
563
+
564
+ return vaultSettings;
565
+ }
566
+
567
+ const usdcBoostedSettings: USDCBoostedStrategySettings = {
568
+ vaultAddress: ContractAddr.from(
569
+ "0xcdb0e3b2e076a2cdc4ee958b726b47c066239ef91c5ac80c94cf814147b84",
570
+ ),
571
+ manager: ContractAddr.from(
572
+ "0x72eea9bac9fa8cfffda637d3b990851446860c6fd8987d6cb50e659b01ee50f",
573
+ ),
574
+ vaultAllocator: ContractAddr.from(
575
+ "0x6d3101cff7f821412a99ebe23bb31a1950f93276285102eb4313e3601f5f927",
576
+ ),
577
+ redeemRequestNFT: ContractAddr.from(
578
+ "0x47dcc6889ca8db4e9eea8f55421e10f8ce7e356ccb45260a1c49a76f733c309",
579
+ ),
580
+ // TODO: not applicable in our case -> remove later ( make it optional if needed)
581
+ aumOracle: ContractAddr.from("0x0"),
582
+ leafAdapters: [],
583
+ adapters: [],
584
+ // Calc using the maxLTV / targetLTV (0.5)
585
+ targetHealthFactor: 1.32,
586
+ // Calc using the maxLTV / maxAcceptableLTV (0.55)
587
+ minHealthFactor: 1.2,
588
+ vesuPoolId: VesuPools.Prime,
589
+ collateralToken: Global.getDefaultTokens().find((t) => t.symbol === "USDC")!,
590
+ debtToken: Global.getDefaultTokens().find((t) => t.symbol === "STRK")!,
591
+ maxLTV: 0.66,
592
+ targetLTV: 0.5,
593
+ hyperxSTRKVaultAddress: ContractAddr.from(
594
+ "0x46c7a54c82b1fe374353859f554a40b8bd31d3e30f742901579e7b57b1b5960",
595
+ ),
596
+ };
597
+
598
+ function getStrategySettings(
599
+ settings: USDCBoostedStrategySettings,
600
+ ): IStrategyMetadata<USDCBoostedStrategySettings> {
601
+ // TODO: we are hardcoding for now since one strategy but we can change later on
602
+ const USDCToken = Global.getDefaultTokens().find((t) => t.symbol === "USDC")!;
603
+ const STRKToken = Global.getDefaultTokens().find((t) => t.symbol === "STRK")!;
604
+
605
+ return {
606
+ id: "usdc_boosted",
607
+ name: "USDC Boosted",
608
+ description:
609
+ "Deposits USDC as collateral on Vesu, borrows STRK, swaps to xSTRK, and deposits into Hyper-xSTRK for boosted yield",
610
+ address: settings.vaultAddress,
611
+ launchBlock: 8742931,
612
+ type: "ERC4626" as const,
613
+ vaultType: {
614
+ // TODO: can change as per need
615
+ type: VaultType.META_VAULT,
616
+ description:
617
+ "Deposits USDC as collateral on Vesu, borrows STRK, swaps to xSTRK, and deposits into Hyper-xSTRK for boosted yield",
618
+ },
619
+ depositTokens: [USDCToken],
620
+ additionalInfo: getUSDCBoostedSettings(settings),
621
+ // TODO: config lateron
622
+ risk: {
623
+ riskFactor: [],
624
+ netRisk: 0,
625
+ notARisks: [],
626
+ },
627
+ protocols: [Protocols.VESU, Protocols.TROVES],
628
+ curator: {
629
+ name: "Unwrap Labs",
630
+ logo: "https://assets.troves.fi/integrations/unwraplabs/white.png",
631
+ },
632
+ settings: {
633
+ maxTVL: Web3Number.fromWei(0, USDCToken.decimals),
634
+ isPaused: false,
635
+ isAudited: false,
636
+ isInstantWithdrawal: false,
637
+ hideHarvestInfo: true,
638
+ quoteToken: USDCToken,
639
+ alerts: [
640
+ {
641
+ tab: "withdraw" as const,
642
+ 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.",
643
+ type: "info" as const,
644
+ },
645
+ ],
646
+ },
647
+ contractDetails: getContractDetails(settings),
648
+ // TODO: config later
649
+ faqs: [],
650
+ investmentSteps: [
651
+ "Deposit USDC into the vault",
652
+ "USDC is supplied as collateral on Vesu, STRK is borrowed",
653
+ "Borrowed STRK is swapped to xSTRK via Avnu",
654
+ "xSTRK is deposited into the Hyper-xSTRK vault for additional yield",
655
+ "On withdrawal, the pipeline reverses to return USDC",
656
+ ],
657
+ // TODO: config later
658
+ tags: [StrategyTag.META_VAULT],
659
+ security: {
660
+ auditStatus: AuditStatus.AUDITED,
661
+ sourceCode: {
662
+ type: SourceCodeType.CLOSED_SOURCE,
663
+ contractLink: "https://github.com/trovesfi/troves-contracts",
664
+ },
665
+ accessControl: {
666
+ type: AccessControlType.STANDARD_ACCOUNT,
667
+ addresses: [ContractAddr.from("0x0")],
668
+ timeLock: "2 Days",
669
+ },
670
+ },
671
+ redemptionInfo: {
672
+ instantWithdrawalVault: InstantWithdrawalVault.NO,
673
+ redemptionsInfo: [
674
+ {
675
+ title: "Typical Duration",
676
+ description: "1-2 hours",
677
+ },
678
+ ],
679
+ alerts: [
680
+ {
681
+ type: "info",
682
+ text: "Redemption times are estimates and may vary based on network conditions and liquidity requirements.",
683
+ tab: "withdraw",
684
+ },
685
+ ],
686
+ },
687
+ usualTimeToEarnings: null,
688
+ usualTimeToEarningsDescription: null,
689
+ };
690
+ }
691
+
692
+ export const USDCBoostedStrategies: IStrategyMetadata<USDCBoostedStrategySettings>[] =
693
+ [getStrategySettings(usdcBoostedSettings)];
@@ -1162,6 +1162,7 @@ export class ExecutionService {
1162
1162
  );
1163
1163
  const netExecutionPrice = this._getNetExecutionPrice(isIncrease);
1164
1164
 
1165
+ // todo doubt. I dont think its a good idea to extended short below ideal price, nor above in long case.
1165
1166
  let executionPrice: number;
1166
1167
 
1167
1168
  if (hasVesuSwapRoute) {