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

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 +19596 -28786
  4. package/dist/index.browser.mjs +8559 -17931
  5. package/dist/index.d.ts +578 -2746
  6. package/dist/index.js +8649 -18059
  7. package/dist/index.mjs +8577 -17949
  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 +1223 -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
@@ -1,1014 +0,0 @@
1
- import {
2
- getMainnetConfig,
3
- IConfig,
4
- IStrategyMetadata,
5
- TokenInfo,
6
- AuditStatus,
7
- SourceCodeType,
8
- AccessControlType,
9
- InstantWithdrawalVault,
10
- VaultType,
11
- VaultPosition,
12
- } from "@/interfaces";
13
- import {
14
- UNIVERSAL_MANAGE_IDS,
15
- UniversalStrategySettings,
16
- } from "../universal-strategy";
17
- import {
18
- ExtendedSVKVesuStateManager,
19
- StateManagerConfig,
20
- } from "./services/extended-vesu-state-manager";
21
- import { ExecutionService, ExecutionConfig } from "./services/executionService";
22
- import { logger } from "@/utils";
23
- import { AUDIT_URL } from "../universal-lst-muliplier-strategy";
24
- import { getNoRiskTags } from "@/interfaces";
25
- import { _riskFactor } from "../universal-lst-muliplier-strategy";
26
- import {
27
- LIMIT_BALANCE,
28
- USDC_TOKEN_DECIMALS,
29
- WALLET_ADDRESS,
30
- WBTC_TOKEN_DECIMALS,
31
- } from "./utils/constants";
32
- import { ExecutionCallback } from "./types/transaction-metadata";
33
- import { PricerBase } from "@/modules/pricerBase";
34
- import { ContractAddr, Web3Number } from "@/dataTypes";
35
- import { Global } from "@/global";
36
- import { ERC20 } from "@/modules";
37
- import { Protocols } from "@/interfaces";
38
- import { MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP } from "./utils/constants";
39
- import {
40
- getInvestmentSteps,
41
- getFAQs,
42
- } from "../universal-lst-muliplier-strategy";
43
- import { getContractDetails } from "../universal-strategy";
44
- import { highlightTextWithLinks } from "@/interfaces";
45
- import { PositionInfo } from "../universal-adapters";
46
- import { APYType } from "../universal-adapters";
47
- import { AUMTypes } from "../universal-strategy";
48
- import { VesuPools } from "../universal-adapters";
49
- import {
50
- BaseAdapterConfig,
51
- CommonAdapter,
52
- TokenTransferAdapter,
53
- VesuModifyPositionAdapter,
54
- VesuMultiplyAdapter,
55
- } from "../universal-adapters";
56
- import {
57
- AVNU_QUOTE_URL,
58
- AVNU_MIDDLEWARE,
59
- EXTENDED_CONTRACT,
60
- } from "../universal-adapters/adapter-utils";
61
- import { PricerFromApi } from "@/modules";
62
- import { ExtendedAdapter } from "../universal-adapters/extended-adapter";
63
- import { SVKStrategy } from "../svk-strategy";
64
- import { AvnuAdapter } from "../universal-adapters/avnu-adapter";
65
- import { SingleTokenInfo } from "../base-strategy";
66
- import { VesuConfig } from "./utils/config.runtime";
67
-
68
- export interface VesuExtendedStrategySettings extends UniversalStrategySettings {
69
- underlyingToken: TokenInfo;
70
- borrowable_assets: TokenInfo[];
71
- targetHealthFactor: number;
72
- quoteAmountToFetchPrice: Web3Number;
73
- minHealthFactor: number;
74
- aumOracle: ContractAddr;
75
- minimumWBTCDifferenceForAvnuSwap: number;
76
- walletAddress: string;
77
- }
78
-
79
- export class VesuExtendedMultiplierStrategy<
80
- S extends VesuExtendedStrategySettings,
81
- > extends SVKStrategy<S> {
82
- public wbtcToken: TokenInfo;
83
- public usdcToken: TokenInfo;
84
- public readonly stateManager: ExtendedSVKVesuStateManager;
85
-
86
- constructor(
87
- config: IConfig,
88
- pricer: PricerBase,
89
- metadata: IStrategyMetadata<S>,
90
- ) {
91
- super(config, pricer, metadata);
92
- this.metadata.additionalInfo.adapters.forEach((adapter) => {
93
- adapter.adapter.config.networkConfig = this.config;
94
- adapter.adapter.config.pricer = this.pricer;
95
- if ((adapter.adapter as any)._vesuAdapter) {
96
- (adapter.adapter as any)._vesuAdapter.networkConfig =
97
- this.config;
98
- (adapter.adapter as any)._vesuAdapter.pricer =
99
- this.pricer;
100
- }
101
- });
102
- // todo check if this can be generalized
103
- this.wbtcToken = Global.getDefaultTokens().find((token) => token.symbol === "WBTC")!;
104
- this.usdcToken = this.metadata.additionalInfo.borrowable_assets[0]!;
105
- this.stateManager = this._initializeStateManager();
106
- }
107
-
108
- /**
109
- * Extracts the required adapters from metadata and constructs the
110
- * state manager used by shouldInvest / handleWithdraw.
111
- */
112
- private _initializeStateManager(): ExtendedSVKVesuStateManager {
113
- const vesuAdapters = this.metadata.additionalInfo.adapters
114
- .filter((a) => a.adapter.name === VesuMultiplyAdapter.name)
115
- .map((a) => a.adapter as VesuMultiplyAdapter);
116
-
117
- const extendedAdapterEntry = this.metadata.additionalInfo.adapters.find(
118
- (a) => a.adapter.name === ExtendedAdapter.name,
119
- );
120
- if (!extendedAdapterEntry) {
121
- throw new Error(
122
- `${this.getTag()} ExtendedAdapter not found in adapters — cannot initialise state manager.`,
123
- );
124
- }
125
-
126
- const stateManagerConfig: StateManagerConfig = {
127
- pricer: this.pricer,
128
- networkConfig: this.config,
129
- vesuAdapters,
130
- extendedAdapter: extendedAdapterEntry.adapter as ExtendedAdapter,
131
- vaultAllocator: this.metadata.additionalInfo.vaultAllocator,
132
- walletAddress: this.metadata.additionalInfo.walletAddress,
133
- assetToken: this.asset(),
134
- usdcToken: this.usdcToken,
135
- collateralToken: this.wbtcToken,
136
- limitBalanceBufferFactor: LIMIT_BALANCE,
137
- };
138
-
139
- return new ExtendedSVKVesuStateManager(stateManagerConfig);
140
- }
141
-
142
- getTag() {
143
- return `${VesuExtendedMultiplierStrategy.name}:${this.metadata.name}`;
144
- }
145
-
146
- /**
147
- * Fetches current WBTC (collateral) and USDC (debt) prices from the pricer.
148
- * Validates that both prices are finite and positive, throwing if not.
149
- */
150
- async getAssetPrices() {
151
- const wbtcToken = Global.getDefaultTokens().find(
152
- (token) => token.symbol === "WBTC"
153
- )!;
154
- const usdcToken = Global.getDefaultTokens().find(
155
- (token) => token.symbol === "USDC"
156
- )!;
157
- const collateralPrice = await this.pricer.getPrice(wbtcToken.symbol);
158
- const debtPrice = await this.pricer.getPrice(usdcToken.symbol);
159
-
160
- if (!Number.isFinite(collateralPrice.price) || collateralPrice.price <= 0) {
161
- throw new Error(
162
- `${this.getTag()} Invalid collateralPrice: ${collateralPrice.price}. Expected a finite, positive number.`
163
- );
164
- }
165
- if (!Number.isFinite(debtPrice.price) || debtPrice.price <= 0) {
166
- throw new Error(
167
- `${this.getTag()} Invalid debtPrice: ${debtPrice.price}. Expected a finite, positive number.`
168
- );
169
- }
170
-
171
- return { collateralPrice, debtPrice };
172
- }
173
-
174
- async getVesuMultiplyAdapter(): Promise<VesuMultiplyAdapter> {
175
- const vesuAdapter = this.metadata.additionalInfo.adapters.find(
176
- (adapter) => adapter.adapter.name === VesuMultiplyAdapter.name,
177
- );
178
- if (!vesuAdapter) {
179
- throw new Error(
180
- `${this.getTag()} Vesu adapter not configured in metadata.`
181
- );
182
- }
183
- return vesuAdapter.adapter as VesuMultiplyAdapter;
184
- }
185
-
186
- async getVesuModifyPositionAdapter(): Promise<VesuModifyPositionAdapter> {
187
- const vesuModifyPositionAdapter = this.metadata.additionalInfo.adapters.find(
188
- (adapter) => adapter.adapter.name === VesuModifyPositionAdapter.name,
189
- );
190
- if (!vesuModifyPositionAdapter) {
191
- throw new Error(
192
- `${this.getTag()} Vesu modify position adapter not configured in metadata.`,
193
- );
194
- }
195
- return vesuModifyPositionAdapter.adapter as VesuModifyPositionAdapter;
196
- }
197
-
198
- async getUsdcTransferAdapter(): Promise<TokenTransferAdapter> {
199
- const usdcTransferAdapter = this.metadata.additionalInfo.adapters.find(
200
- (adapter) => adapter.adapter.name === TokenTransferAdapter.name,
201
- );
202
- if (!usdcTransferAdapter) {
203
- throw new Error(
204
- `${this.getTag()} Usdc transfer adapter not configured in metadata.`
205
- );
206
- }
207
- return usdcTransferAdapter.adapter as TokenTransferAdapter;
208
- }
209
-
210
- async getAvnuAdapter(): Promise<AvnuAdapter> {
211
- const avnuAdapter = this.metadata.additionalInfo.adapters.find(
212
- (adapter) => adapter.adapter.name === AvnuAdapter.name,
213
- );
214
- if (!avnuAdapter) {
215
- throw new Error(
216
- `${this.getTag()} Avnu adapter not configured in metadata.`
217
- );
218
- }
219
- return avnuAdapter.adapter as AvnuAdapter;
220
- }
221
-
222
- async getExtendedAdapter(): Promise<ExtendedAdapter> {
223
- const extendedAdapter = this.metadata.additionalInfo.adapters.find(
224
- (adapter) => adapter.adapter.name === ExtendedAdapter.name,
225
- );
226
- if (!extendedAdapter) {
227
- throw new Error(
228
- `${this.getTag()} Extended adapter not configured in metadata.`
229
- );
230
- }
231
- if (!extendedAdapter.adapter || !(extendedAdapter.adapter as ExtendedAdapter).client) {
232
- throw new Error(
233
- `${this.getTag()} Extended adapter client not initialized.`
234
- );
235
- }
236
- return extendedAdapter.adapter as ExtendedAdapter;
237
- }
238
-
239
- /**
240
- * Creates an ExecutionService wired to this strategy's adapters and config.
241
- * Use with `stateManager.solve()` to get a SolveResult, then pass it to
242
- * `executionService.execute(solveResult)` for execution.
243
- *
244
- * @param onExecutionEvent - Optional callback for execution lifecycle events (DB persistence, alerts, etc.)
245
- * @param extendedAcceptableSlippageBps - Slippage for Extended limit orders (default: 10 = 0.1%)
246
- * @param maxPriceDivergenceBps - Max price divergence between Extended and AVNU (default: 50 = 0.5%)
247
- */
248
- async createExecutionService(opts?: {
249
- onExecutionEvent?: ExecutionCallback;
250
- extendedAcceptableSlippageBps?: number;
251
- maxPriceDivergenceBps?: number;
252
- }): Promise<ExecutionService> {
253
- const [
254
- vesuMultiplyAdapter,
255
- vesuModifyPositionAdapter,
256
- extendedAdapter,
257
- avnuAdapter,
258
- usdcTransferAdapter,
259
- ] =
260
- await Promise.all([
261
- this.getVesuMultiplyAdapter(),
262
- this.getVesuModifyPositionAdapter(),
263
- this.getExtendedAdapter(),
264
- this.getAvnuAdapter(),
265
- this.getUsdcTransferAdapter(),
266
- ]);
267
-
268
- const executionConfig: ExecutionConfig = {
269
- networkConfig: this.config,
270
- pricer: this.pricer,
271
- vesuMultiplyAdapter,
272
- vesuModifyPositionAdapter,
273
- extendedAdapter,
274
- avnuAdapter,
275
- usdcTransferAdapter,
276
- vaultAllocator: this.metadata.additionalInfo.vaultAllocator,
277
- walletAddress: this.metadata.additionalInfo.walletAddress,
278
- wbtcToken: this.wbtcToken,
279
- usdcToken: this.usdcToken,
280
- getMerkleTree: () => this.getMerkleTree(),
281
- getManageCall: (proofs, manageCalls) => this.getManageCall(proofs, manageCalls),
282
- getBringLiquidityCall: (params) => this.getBringLiquidityCall(params),
283
- onExecutionEvent: opts?.onExecutionEvent,
284
- extendedAcceptableSlippageBps: opts?.extendedAcceptableSlippageBps,
285
- maxPriceDivergenceBps: opts?.maxPriceDivergenceBps,
286
- };
287
-
288
- return new ExecutionService(executionConfig);
289
- }
290
-
291
- /**
292
- * Calculates the total Assets Under Management across all adapters.
293
- * Aggregates position values from every adapter, converts to the vault's
294
- * base asset, and returns the net AUM along with the previous AUM snapshot
295
- * and per-position breakdowns.
296
- */
297
- async getAUM(): Promise<{
298
- net: SingleTokenInfo;
299
- prevAum: Web3Number;
300
- splits: PositionInfo[];
301
- }> {
302
- const allPositions: PositionInfo[] = [];
303
- for (let adapter of this.metadata.additionalInfo.adapters) {
304
- let positions = await adapter.adapter.getPositions();
305
- if (positions && positions.length > 0) {
306
- const filteredPositions = positions;
307
- allPositions.push(...filteredPositions);
308
- }
309
- }
310
- const assetPrice = await this.pricer.getPrice(this.asset().symbol);
311
- let netAUM = new Web3Number(0, this.asset().decimals);
312
- for (let position of allPositions) {
313
- if (position.tokenInfo.address.eq(this.asset().address)) {
314
- netAUM = netAUM.plus(position.amount);
315
- } else {
316
- netAUM = netAUM.plus(position.usdValue / assetPrice.price);
317
- }
318
- }
319
-
320
- // ! IMO should also include USDC in wallet.
321
- const walletHoldings = await this.getWalletHoldings();
322
- for (let holding of walletHoldings) {
323
- if (holding.tokenInfo.address.eq(this.asset().address)) {
324
- netAUM = netAUM.plus(holding.amount);
325
- } else {
326
- netAUM = netAUM.plus(holding.usdValue / assetPrice.price);
327
- }
328
- }
329
-
330
- const prevAum = await this.getPrevAUM();
331
- const realAUM: PositionInfo = {
332
- tokenInfo: this.asset(),
333
- amount: netAUM,
334
- usdValue: netAUM.toNumber() * assetPrice.price,
335
- apy: { apy: netAUM.toNumber() * assetPrice.price, type: APYType.BASE },
336
- remarks: AUMTypes.FINALISED,
337
- protocol: Protocols.NONE, // just placeholder
338
- };
339
-
340
- const estimatedAUMDelta: PositionInfo = {
341
- tokenInfo: this.asset(),
342
- amount: Web3Number.fromWei("0", this.asset().decimals),
343
- usdValue: 0,
344
- apy: { apy: 0, type: APYType.BASE },
345
- remarks: AUMTypes.DEFISPRING,
346
- protocol: Protocols.NONE, // just placeholder
347
- };
348
-
349
- return {
350
- net: {
351
- tokenInfo: this.asset(),
352
- amount: netAUM,
353
- usdValue: netAUM.toNumber() * assetPrice.price,
354
- },
355
- prevAum: prevAum,
356
- splits: [realAUM, estimatedAUMDelta],
357
- };
358
- }
359
-
360
-
361
- /**
362
- * Computes the maximum additional USDC that can be borrowed from Vesu
363
- * while keeping the strategy profitable. Uses the Extended funding rate
364
- * and Vesu supply APY to derive a break-even borrow APY, then queries
365
- * Vesu for the max borrowable amount at that rate.
366
- */
367
- async getMaxBorrowableAmount(): Promise<Web3Number> {
368
- const vesuAdapter = await this.getVesuModifyPositionAdapter();
369
- const extendedAdapter = await this.getExtendedAdapter();
370
- const extendedFundingRate = new Web3Number(
371
- (await extendedAdapter.getNetAPY()).toFixed(4),
372
- 0,
373
- );
374
- const extendedPositions = await extendedAdapter.getAllOpenPositions();
375
- if (!extendedPositions || extendedPositions.length === 0) {
376
- logger.warn(`${this.getTag()} getMaxBorrowableAmount: no extended positions found`);
377
- return new Web3Number(0, 0);
378
- }
379
- const extendePositionSizeUSD = new Web3Number(
380
- extendedPositions[0].value || 0,
381
- 0,
382
- );
383
- const vesuPositions = await vesuAdapter.getPositions();
384
- const vesuSupplyApy = vesuPositions[0].apy.apy;
385
- const vesuCollateralSizeUSD = new Web3Number(
386
- vesuPositions[0].usdValue.toFixed(USDC_TOKEN_DECIMALS),
387
- USDC_TOKEN_DECIMALS,
388
- );
389
- const vesuDebtSizeUSD = new Web3Number(
390
- vesuPositions[1].usdValue.toFixed(USDC_TOKEN_DECIMALS),
391
- USDC_TOKEN_DECIMALS,
392
- );
393
- const num1 = extendePositionSizeUSD.multipliedBy(extendedFundingRate);
394
- const num2 = vesuCollateralSizeUSD.multipliedBy(vesuSupplyApy);
395
- const num3 = vesuDebtSizeUSD.abs();
396
- const maxBorrowApy = num1.plus(num2).minus(0.1).dividedBy(num3);
397
- const vesuMaxBorrowableResult =
398
- await vesuAdapter._vesuAdapter.getMaxBorrowableByInterestRate(
399
- this.config,
400
- vesuAdapter.config.debt,
401
- maxBorrowApy.toNumber(),
402
- );
403
- return new Web3Number(
404
- vesuMaxBorrowableResult.maxDebtToHave.toFixed(USDC_TOKEN_DECIMALS),
405
- USDC_TOKEN_DECIMALS,
406
- );
407
- }
408
-
409
- /**
410
- * Returns the current health metrics for the strategy:
411
- * [0] Vesu health factor (maxLTV / actualLTV) — higher is safer.
412
- * [1] Extended margin ratio (as percentage) — higher means more margin available.
413
- */
414
- async getVesuHealthFactors(): Promise<number[]> {
415
- const vesuAdapter = await this.getVesuModifyPositionAdapter();
416
- const extendedAdapter = await this.getExtendedAdapter();
417
- const vesuPositions = await vesuAdapter.getPositions();
418
- const vesuCollateralSizeUSD = new Web3Number(
419
- vesuPositions[0].usdValue.toFixed(USDC_TOKEN_DECIMALS),
420
- 0,
421
- );
422
- const vesuDebtSizeUSD = new Web3Number(
423
- vesuPositions[1].usdValue.toFixed(USDC_TOKEN_DECIMALS),
424
- 0,
425
- );
426
- const actualLtv = vesuDebtSizeUSD.dividedBy(vesuCollateralSizeUSD).abs();
427
- logger.debug(`${this.getTag()} getVesuHealthFactors: actualLtv=${actualLtv.toNumber()}`);
428
- const maxLtv = new Web3Number(
429
- await vesuAdapter._vesuAdapter.getLTVConfig(this.config),
430
- 4,
431
- );
432
- const healthFactor = new Web3Number(
433
- maxLtv.dividedBy(actualLtv).toFixed(4),
434
- 4,
435
- );
436
- logger.debug(`${this.getTag()} getVesuHealthFactors: healthFactor=${healthFactor.toNumber()}`);
437
- const extendedBalance = await extendedAdapter.getExtendedDepositAmount();
438
- if (!extendedBalance) {
439
- return [0, 0];
440
- }
441
- const extendedLeverage = new Web3Number(
442
- (Number(extendedBalance.marginRatio) * 100).toFixed(4),
443
- 4,
444
- );
445
- logger.debug(`${this.getTag()} getVesuHealthFactors: extendedLeverage=${extendedLeverage.toNumber()}`);
446
- return [healthFactor.toNumber(), extendedLeverage.toNumber()];
447
- }
448
-
449
- /**
450
- * Calculates the weighted net APY of the strategy across all positions.
451
- * Combines Vesu supply APY (scaled by 0.1 performance fee) and Extended
452
- * position APY, weighted by their respective USD values.
453
- * Also returns per-position APY splits.
454
- */
455
- async netAPY(): Promise<{
456
- net: number;
457
- splits: { apy: number; id: string }[];
458
- }> {
459
- const allPositions: PositionInfo[] = [];
460
- for (let adapter of this.metadata.additionalInfo.adapters) {
461
- if (adapter.adapter.name !== ExtendedAdapter.name) {
462
- let positions = await adapter.adapter.getPositions();
463
- if (positions.length > 0) {
464
- allPositions.push(...positions);
465
- }
466
- }
467
- }
468
- const extendedAdapter = await this.getExtendedAdapter();
469
- let vesuPositions = allPositions.filter(
470
- (item) => item.protocol === Protocols.VESU,
471
- );
472
- vesuPositions.map((item) => {
473
- item.apy.apy = item.apy.apy * 0.1;
474
- });
475
- const extendedPositions = await extendedAdapter.getAllOpenPositions();
476
- if (!extendedPositions || !this.usdcToken) {
477
- return {
478
- net: 0,
479
- splits: [],
480
- };
481
- }
482
- const extendedPosition = extendedPositions[0] || 0;
483
- const extendedEquity =
484
- (await extendedAdapter.getExtendedDepositAmount())?.equity || 0;
485
- const extendedApy = await extendedAdapter.getNetAPY();
486
- const totalHoldingsUSDValue =
487
- allPositions.reduce((acc, curr) => acc + curr.usdValue, 0) +
488
- Number(extendedEquity);
489
- const extendedPositionSizeMultipliedByApy =
490
- Number(extendedPosition.value) * extendedApy;
491
- let weightedAPYs =
492
- allPositions.reduce(
493
- (acc, curr) => acc + curr.apy.apy * curr.usdValue,
494
- 0,
495
- ) + extendedPositionSizeMultipliedByApy;
496
- const netAPY = weightedAPYs / totalHoldingsUSDValue;
497
- logger.debug(
498
- `${this.getTag()} netAPY: holdingsUsd=${totalHoldingsUSDValue}, weightedApy=${weightedAPYs}, net=${netAPY}`,
499
- );
500
- allPositions.push({
501
- tokenInfo: this.usdcToken,
502
- amount: new Web3Number(extendedPosition?.size || 0, 0),
503
- usdValue: Number(extendedEquity),
504
- apy: { apy: extendedApy, type: APYType.BASE },
505
- remarks: AUMTypes.FINALISED,
506
- protocol: Protocols.EXTENDED,
507
- });
508
- return {
509
- net: netAPY,
510
- splits: allPositions.map((p) => ({
511
- apy: p.apy.apy,
512
- id: p.remarks ?? "",
513
- })),
514
- };
515
- }
516
-
517
- /**
518
- * Fetches the operator wallet's current holdings for USDC and WBTC,
519
- * returning each token's balance and USD value.
520
- */
521
- async getWalletHoldings(): Promise<
522
- {
523
- tokenInfo: TokenInfo;
524
- amount: Web3Number;
525
- usdValue: number;
526
- }[]
527
- > {
528
- if (!this.wbtcToken || !this.usdcToken) {
529
- return [];
530
- }
531
- const walletAddress = this.metadata.additionalInfo.walletAddress;
532
- const usdcWalletBalance = await new ERC20(this.config).balanceOf(
533
- this.usdcToken.address,
534
- walletAddress,
535
- this.usdcToken.decimals,
536
- );
537
- const price = await this.pricer.getPrice(this.usdcToken.symbol);
538
- const usdcUsdValue =
539
- Number(usdcWalletBalance.toFixed(this.usdcToken.decimals)) * price.price;
540
- return [
541
- {
542
- tokenInfo: this.usdcToken,
543
- amount: usdcWalletBalance,
544
- usdValue: usdcUsdValue,
545
- }
546
- ];
547
- }
548
- }
549
-
550
- /**
551
- * Configures all adapters (Vesu, Extended, Avnu, TokenTransfer)
552
- * and registers their leaf adapters on the vault settings. This is the central
553
- * wiring function that connects the strategy to its underlying protocol adapters.
554
- */
555
- function getLooperSettings(
556
- collateralSymbol: string,
557
- underlyingSymbol: string,
558
- vaultSettings: VesuExtendedStrategySettings,
559
- pool1: ContractAddr,
560
- extendedBackendReadUrl: string,
561
- extendedBackendWriteUrl: string,
562
- vaultIdExtended: number,
563
- minimumExtendedMovementAmount: number,
564
- minimumVesuMovementAmount: number,
565
- minimumExtendedRetriesDelayForOrderStatus: number,
566
- minimumExtendedPriceDifferenceForSwapOpen: number,
567
- maximumExtendedPriceDifferenceForSwapClosing: number,
568
- ) {
569
- vaultSettings.leafAdapters = [];
570
-
571
- const wbtcToken = Global.getDefaultTokens().find(
572
- (token) => token.symbol === collateralSymbol,
573
- )!;
574
- const usdcToken = Global.getDefaultTokens().find(
575
- (token) => token.symbol === underlyingSymbol,
576
- )!;
577
-
578
- const baseAdapterConfig: BaseAdapterConfig = {
579
- baseToken: usdcToken,
580
- supportedPositions: [
581
- { asset: usdcToken, isDebt: true },
582
- { asset: wbtcToken, isDebt: false },
583
- ],
584
- //Since we open 2 positions, we need to add both positions, one is debt another is collateral
585
- networkConfig: getMainnetConfig(),
586
- pricer: new PricerFromApi(getMainnetConfig(), Global.getDefaultTokens()),
587
- vaultAllocator: vaultSettings.vaultAllocator,
588
- vaultAddress: vaultSettings.vaultAddress,
589
- };
590
-
591
- const avnuAdapter = new AvnuAdapter({
592
- ...baseAdapterConfig,
593
- avnuContract: AVNU_MIDDLEWARE,
594
- slippage: 0.01,
595
- baseUrl: AVNU_QUOTE_URL,
596
- minimumExtendedPriceDifferenceForSwapOpen:
597
- minimumExtendedPriceDifferenceForSwapOpen,
598
- maximumExtendedPriceDifferenceForSwapClosing:
599
- maximumExtendedPriceDifferenceForSwapClosing,
600
- });
601
-
602
- const extendedAdapter = new ExtendedAdapter({
603
- ...baseAdapterConfig,
604
- supportedPositions: [
605
- { asset: usdcToken, isDebt: false },
606
- ],
607
- vaultIdExtended: vaultIdExtended,
608
- extendedContract: EXTENDED_CONTRACT,
609
- extendedBackendWriteUrl: extendedBackendWriteUrl,
610
- extendedBackendReadUrl: extendedBackendReadUrl,
611
- extendedTimeout: 30000,
612
- extendedRetries: 3,
613
- extendedBaseUrl: "https://api.starknet.extended.exchange",
614
- extendedMarketName: "BTC-USD",
615
- extendedPrecision: 5,
616
- avnuAdapter: avnuAdapter,
617
- retryDelayForOrderStatus: minimumExtendedRetriesDelayForOrderStatus ?? 3000,
618
- minimumExtendedMovementAmount: minimumExtendedMovementAmount ?? 5, //5 usdcs
619
- });
620
-
621
- const vesuMultiplyAdapter = new VesuMultiplyAdapter({
622
- poolId: pool1,
623
- collateral: wbtcToken,
624
- debt: usdcToken,
625
- marginToken: usdcToken,
626
- targetHealthFactor: vaultSettings.targetHealthFactor,
627
- minHealthFactor: vaultSettings.minHealthFactor,
628
- quoteAmountToFetchPrice: vaultSettings.quoteAmountToFetchPrice,
629
- ...baseAdapterConfig,
630
- supportedPositions: [
631
- { asset: wbtcToken, isDebt: false },
632
- { asset: usdcToken, isDebt: true },
633
- ],
634
- minimumVesuMovementAmount: minimumVesuMovementAmount ?? 5, //5 usdc
635
- });
636
-
637
- const vesuModifyPositionMaxLtv = VesuConfig.maxLtv;
638
- const vesuModifyPositionTargetLtv = VesuConfig.targetLtv;
639
- const vesuModifyPositionAdapter = new VesuModifyPositionAdapter({
640
- poolId: pool1,
641
- collateral: wbtcToken,
642
- debt: usdcToken,
643
- targetLtv: vesuModifyPositionTargetLtv,
644
- maxLtv: vesuModifyPositionMaxLtv,
645
- ...baseAdapterConfig,
646
- supportedPositions: [
647
- { asset: wbtcToken, isDebt: false },
648
- { asset: usdcToken, isDebt: true },
649
- ],
650
- });
651
-
652
- // Transfers USDC between the vault allocator (fromAddress) and the operator wallet (toAddress)
653
- const usdcTransferAdapter = new TokenTransferAdapter({
654
- ...baseAdapterConfig,
655
- baseToken: usdcToken,
656
- supportedPositions: [{ asset: usdcToken, isDebt: false }],
657
- fromAddress: vaultSettings.vaultAllocator,
658
- toAddress: ContractAddr.from(vaultSettings.walletAddress),
659
- });
660
-
661
- vaultSettings.adapters.push({
662
- id: `${vesuMultiplyAdapter.name}_${wbtcToken.symbol}_${usdcToken.symbol}`,
663
- adapter: vesuMultiplyAdapter,
664
- });
665
-
666
- vaultSettings.adapters.push({
667
- id: `${vesuModifyPositionAdapter.name}_${wbtcToken.symbol}_${usdcToken.symbol}`,
668
- adapter: vesuModifyPositionAdapter,
669
- });
670
-
671
- vaultSettings.adapters.push({
672
- id: `${usdcTransferAdapter.name}_${usdcToken.symbol}`,
673
- adapter: usdcTransferAdapter,
674
- });
675
-
676
- vaultSettings.adapters.push({
677
- id: `${extendedAdapter.name}_${wbtcToken.symbol}`,
678
- adapter: extendedAdapter,
679
- });
680
-
681
- vaultSettings.adapters.push({
682
- id: `${avnuAdapter.name}_${wbtcToken.symbol}`,
683
- adapter: avnuAdapter,
684
- });
685
-
686
- const commonAdapter = new CommonAdapter({
687
- id: UNIVERSAL_MANAGE_IDS.FLASH_LOAN,
688
- vaultAddress: vaultSettings.vaultAddress,
689
- vaultAllocator: vaultSettings.vaultAllocator,
690
- manager: vaultSettings.manager,
691
- asset: usdcToken.address,
692
- });
693
-
694
- vaultSettings.leafAdapters.push(() => vesuMultiplyAdapter.getDepositLeaf());
695
- vaultSettings.leafAdapters.push(() => vesuMultiplyAdapter.getWithdrawLeaf());
696
- vaultSettings.leafAdapters.push(() => vesuModifyPositionAdapter.getDepositLeaf());
697
- vaultSettings.leafAdapters.push(() => vesuModifyPositionAdapter.getWithdrawLeaf());
698
- vaultSettings.leafAdapters.push(() => extendedAdapter.getDepositLeaf());
699
- vaultSettings.leafAdapters.push(() => avnuAdapter.getDepositLeaf());
700
- vaultSettings.leafAdapters.push(() => avnuAdapter.getWithdrawLeaf());
701
- vaultSettings.leafAdapters.push(() => usdcTransferAdapter.getDepositLeaf());
702
- vaultSettings.leafAdapters.push(() => usdcTransferAdapter.getWithdrawLeaf());
703
- vaultSettings.leafAdapters.push(
704
- commonAdapter
705
- .getApproveAdapter(
706
- usdcToken.address,
707
- vaultSettings.vaultAddress,
708
- UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY,
709
- )
710
- .bind(commonAdapter),
711
- );
712
-
713
- vaultSettings.leafAdapters.push(
714
- commonAdapter
715
- .getBringLiquidityAdapter(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY)
716
- .bind(commonAdapter),
717
- );
718
- return vaultSettings;
719
- }
720
-
721
- function getDescription(tokenSymbol: string, underlyingSymbol: string) {
722
- return VaultDescription(tokenSymbol, underlyingSymbol);
723
- }
724
-
725
- export default function VaultDescription(
726
- lstSymbol: string,
727
- underlyingSymbol: string,
728
- ) {
729
- const containerStyle = {
730
- maxWidth: "800px",
731
- margin: "0 auto",
732
- backgroundColor: "#111",
733
- color: "#eee",
734
- fontFamily: "Arial, sans-serif",
735
- borderRadius: "12px",
736
- };
737
-
738
- return (
739
- <div style={containerStyle}>
740
- <h1 style={{ fontSize: "18px", marginBottom: "10px" }}>
741
- Liquidation risk managed leverged {lstSymbol} Vault
742
- </h1>
743
- <p style={{ fontSize: "14px", lineHeight: "1.5", marginBottom: "16px" }}>
744
- This Levered Endur {lstSymbol} vault is a tokenized leveraged Vault,
745
- auto-compounding strategy that takes upto 5x leverage on {lstSymbol} by
746
- borrow {underlyingSymbol}. Borrowed amount is swapped to {lstSymbol} to
747
- create leverage. Depositors receive vault shares that represent a
748
- proportional claim on the underlying assets and accrued yield.
749
- </p>
750
-
751
- <p style={{ fontSize: "14px", lineHeight: "1.5", marginBottom: "16px" }}>
752
- This vault uses Vesu for lending and borrowing. The oracle used by this
753
- pool is a{" "}
754
- {highlightTextWithLinks("conversion rate oracle", [
755
- {
756
- highlight: "conversion rate oracle",
757
- link: "https://docs.pragma.build/starknet/development#conversion-rate",
758
- },
759
- ])}{" "}
760
- which is resilient to liquidity issues and price volatility, hence
761
- reducing the risk of liquidation. However, overtime, if left
762
- un-monitored, debt can increase enough to trigger a liquidation. But no
763
- worries, our continuous monitoring systems look for situations with
764
- reduced health factor and balance collateral/debt to bring it back to
765
- safe levels. With Troves, you can have a peaceful sleep.
766
- </p>
767
-
768
- <div
769
- style={{
770
- backgroundColor: "#222",
771
- padding: "10px",
772
- borderRadius: "8px",
773
- marginBottom: "20px",
774
- border: "1px solid #444",
775
- }}
776
- >
777
- <p style={{ fontSize: "13px", color: "#ccc" }}>
778
- <strong>Withdrawals:</strong> Requests can take up to{" "}
779
- <strong>1-2 hours</strong> to process as the vault unwinds and settles
780
- routing.
781
- </p>
782
- </div>
783
- <div
784
- style={{
785
- backgroundColor: "#222",
786
- padding: "10px",
787
- borderRadius: "8px",
788
- marginBottom: "20px",
789
- border: "1px solid #444",
790
- }}
791
- >
792
- <p style={{ fontSize: "13px", color: "#ccc" }}>
793
- <strong>Debt limits:</strong> Pools on Vesu have debt caps that are
794
- gradually increased over time. Until caps are raised, deposited Tokens
795
- remain in the vault, generating a shared net return for all
796
- depositors. There is no additional fee taken by Troves on Yield
797
- token's APY, its only on added gain.
798
- </p>
799
- </div>
800
- {/* <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
801
- <p style={{ fontSize: "13px", color: "#ccc" }}>
802
- <strong>APY assumptions:</strong> APY shown is the max possible value given current LST and borrowing rates. True APY will be subject to the actual leverage, based on above point. More insights on exact APY will be added soon.
803
- </p>
804
- </div> */}
805
- </div>
806
- );
807
- }
808
-
809
- const re7UsdcPrimeDevansh: VesuExtendedStrategySettings = {
810
- vaultAddress: ContractAddr.from(
811
- "0x772d6cf5038c18ff5ab89f8945017bbf4d2c6959891339975c70a4f74ac6c8e",
812
- ),
813
- manager: ContractAddr.from(
814
- "0x3340c9d7231838e2dccff72b9004f1598a74e65c74b954f07fe1ea19d04a625",
815
- ),
816
- vaultAllocator: ContractAddr.from(
817
- "0x537353b35eee5ca2d9a45eb646977baddd4e89ce870a231dcada79884117292",
818
- ),
819
- redeemRequestNFT: ContractAddr.from(
820
- "0x6117d1a8c72c0457948083757e1a17ee8c0833b969d5c959b629e5f8feb56ec",
821
- ),
822
- aumOracle: ContractAddr.from(
823
- "0x6d7d68045bf5e0b5a4cec43241549851cb9645f7a73a20894152165dbe7083a",
824
- ),
825
- leafAdapters: [],
826
- adapters: [],
827
- targetHealthFactor: 1.4,
828
- minHealthFactor: 1.05,
829
- underlyingToken: Global.getDefaultTokens().find(
830
- (token) => token.symbol === "USDC",
831
- )!,
832
- quoteAmountToFetchPrice: new Web3Number(
833
- "0.001",
834
- Global.getDefaultTokens().find((token) => token.symbol === "WBTC")!
835
- .decimals,
836
- ),
837
- borrowable_assets: [
838
- Global.getDefaultTokens().find((token) => token.symbol === "USDC")!,
839
- ],
840
- minimumWBTCDifferenceForAvnuSwap: MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP,
841
- walletAddress: '0x024b563C1C7d41B32BF4EFB9F38828508a65Be2d6e25268E9f63F22C5e9E51c5',
842
- };
843
-
844
- const pureUsdc: VesuExtendedStrategySettings = {
845
- vaultAddress: ContractAddr.from(
846
- "0x745c972db65bdee10022fd875dd328c7f40a90849135b6a0f875a40f3c632ae",
847
- ),
848
- manager: ContractAddr.from(
849
- "0x364e0894edefb616ec090f57f5c0274517fcd98ab276ae1f021c5e962fa1deb",
850
- ),
851
- vaultAllocator: ContractAddr.from(
852
- "0x6fceed28e03a96091877568893df0dd89b9bb80fec30da2b742dacbd5526179",
853
- ),
854
- redeemRequestNFT: ContractAddr.from(
855
- "0x501c2b87728e22c6dfcebe4c0b2b3a9fba5845606e4d59fa7bf591badcbb42",
856
- ),
857
- aumOracle: ContractAddr.from(
858
- "0x6ccd95f5765242695d3c75e1440b1d0b30efac8babb864ce15729977b97cb82",
859
- ),
860
- leafAdapters: [],
861
- adapters: [],
862
- targetHealthFactor: 1.4,
863
- minHealthFactor: 1.35,
864
- underlyingToken: Global.getDefaultTokens().find(
865
- (token) => token.symbol === "USDC",
866
- )!,
867
- quoteAmountToFetchPrice: new Web3Number(
868
- "0.001",
869
- Global.getDefaultTokens().find((token) => token.symbol === "USDC")!
870
- .decimals,
871
- ),
872
- borrowable_assets: [
873
- Global.getDefaultTokens().find((token) => token.symbol === "USDC")!,
874
- ],
875
- minimumWBTCDifferenceForAvnuSwap: MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP,
876
- walletAddress: '0x058571C23da5FEdd4e36003FAE3fE2fA9782f2692E552f081839142B10770D0B',
877
- };
878
-
879
- export const VesuExtendedTestStrategies = (
880
- extendedBackendReadUrl: string,
881
- extendedBackendWriteUrl: string,
882
- vaultIdExtended: number,
883
- minimumExtendedMovementAmount: number,
884
- minimumVesuMovementAmount: number,
885
- minimumExtendedRetriesDelayForOrderStatus: number,
886
- minimumExtendedPriceDifferenceForSwapOpen: number,
887
- maximumExtendedPriceDifferenceForSwapClosing: number,
888
- ): IStrategyMetadata<VesuExtendedStrategySettings>[] => {
889
- return [
890
- getStrategySettingsVesuExtended(
891
- 0, // index
892
- "WBTC",
893
- "USDC",
894
- re7UsdcPrimeDevansh,
895
- false,
896
- false,
897
- extendedBackendReadUrl,
898
- extendedBackendWriteUrl,
899
- vaultIdExtended,
900
- minimumExtendedMovementAmount,
901
- minimumVesuMovementAmount,
902
- minimumExtendedRetriesDelayForOrderStatus,
903
- minimumExtendedPriceDifferenceForSwapOpen,
904
- maximumExtendedPriceDifferenceForSwapClosing,
905
- ),
906
- getStrategySettingsVesuExtended(
907
- 1, // index
908
- "WBTC",
909
- "USDC",
910
- pureUsdc,
911
- false,
912
- false,
913
- extendedBackendReadUrl,
914
- extendedBackendWriteUrl,
915
- vaultIdExtended,
916
- minimumExtendedMovementAmount,
917
- minimumVesuMovementAmount,
918
- minimumExtendedRetriesDelayForOrderStatus,
919
- minimumExtendedPriceDifferenceForSwapOpen,
920
- maximumExtendedPriceDifferenceForSwapClosing,
921
- ),
922
- ];
923
- };
924
-
925
- /**
926
- * Constructs a complete IStrategyMetadata object for a Vesu-Extended strategy,
927
- * including adapter wiring, risk configuration, FAQ, and UI description.
928
- */
929
- function getStrategySettingsVesuExtended(
930
- index: number,
931
- collateralSymbol: string,
932
- underlyingSymbol: string,
933
- addresses: VesuExtendedStrategySettings,
934
- isPreview: boolean = false,
935
- isLST: boolean,
936
- extendedBackendReadUrl: string,
937
- extendedBackendWriteUrl: string,
938
- vaultIdExtended: number,
939
- minimumExtendedMovementAmount: number,
940
- minimumVesuMovementAmount: number,
941
- minimumExtendedRetriesDelayForOrderStatus: number,
942
- minimumExtendedPriceDifferenceForSwapOpen: number,
943
- maximumExtendedPriceDifferenceForSwapClosing: number,
944
- ): IStrategyMetadata<VesuExtendedStrategySettings> {
945
- return {
946
- id: `extended_${underlyingSymbol.toLowerCase()}_test_${index}`,
947
- name: `Extended Test ${underlyingSymbol} ${index}`,
948
- description: getDescription(collateralSymbol, underlyingSymbol),
949
- address: addresses.vaultAddress,
950
- launchBlock: 0,
951
- type: "Other",
952
- vaultType: {
953
- type: VaultType.DELTA_NEUTRAL,
954
- description: "Delta Neutral strategy using extended position on Vesu"
955
- },
956
- depositTokens: [
957
- Global.getDefaultTokens().find(
958
- (token) => token.symbol === underlyingSymbol,
959
- )!,
960
- ],
961
- additionalInfo: getLooperSettings(
962
- collateralSymbol,
963
- underlyingSymbol,
964
- addresses,
965
- VesuPools.Re7USDCPrime,
966
- extendedBackendReadUrl,
967
- extendedBackendWriteUrl,
968
- vaultIdExtended,
969
- minimumExtendedMovementAmount,
970
- minimumVesuMovementAmount,
971
- minimumExtendedRetriesDelayForOrderStatus,
972
- minimumExtendedPriceDifferenceForSwapOpen,
973
- maximumExtendedPriceDifferenceForSwapClosing,
974
- ),
975
- risk: {
976
- riskFactor: _riskFactor,
977
- netRisk:
978
- _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) /
979
- _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
980
- notARisks: getNoRiskTags(_riskFactor),
981
- },
982
- auditUrl: AUDIT_URL,
983
- protocols: [Protocols.ENDUR, Protocols.VESU],
984
- contractDetails: getContractDetails(addresses),
985
- faqs: getFAQs(collateralSymbol, underlyingSymbol, isLST),
986
- investmentSteps: getInvestmentSteps(collateralSymbol, underlyingSymbol),
987
- isPreview: isPreview,
988
- apyMethodology: isLST
989
- ? "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."
990
- : "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.",
991
- security: {
992
- auditStatus: AuditStatus.NOT_AUDITED,
993
- sourceCode: {
994
- type: SourceCodeType.OPEN_SOURCE,
995
- contractLink: AUDIT_URL,
996
- },
997
- accessControl: {
998
- type: AccessControlType.MULTISIG_ACCOUNT,
999
- addresses: [addresses.vaultAddress],
1000
- timeLock: "None",
1001
- },
1002
- },
1003
- redemptionInfo: {
1004
- instantWithdrawalVault: InstantWithdrawalVault.NO,
1005
- redemptionsInfo: [{
1006
- title: "Up to $500k",
1007
- description: "1-24 hours",
1008
- }],
1009
- alerts: [],
1010
- },
1011
- usualTimeToEarnings: null,
1012
- usualTimeToEarningsDescription: null,
1013
- };
1014
- }