@strkfarm/sdk 1.2.0 → 2.0.0-dev-strategy2.1

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 (60) hide show
  1. package/dist/index.browser.global.js +76556 -66640
  2. package/dist/index.browser.mjs +34235 -24392
  3. package/dist/index.d.ts +2372 -793
  4. package/dist/index.js +31967 -22084
  5. package/dist/index.mjs +25545 -15719
  6. package/package.json +86 -76
  7. package/readme.md +56 -1
  8. package/src/data/extended-deposit.abi.json +3613 -0
  9. package/src/data/universal-vault.abi.json +135 -20
  10. package/src/dataTypes/_bignumber.ts +11 -0
  11. package/src/dataTypes/address.ts +7 -0
  12. package/src/global.ts +240 -193
  13. package/src/interfaces/common.tsx +26 -2
  14. package/src/modules/ExtendedWrapperSDk/index.ts +62 -0
  15. package/src/modules/ExtendedWrapperSDk/types.ts +311 -0
  16. package/src/modules/ExtendedWrapperSDk/wrapper.ts +448 -0
  17. package/src/modules/avnu.ts +17 -4
  18. package/src/modules/ekubo-quoter.ts +89 -10
  19. package/src/modules/erc20.ts +67 -21
  20. package/src/modules/harvests.ts +29 -43
  21. package/src/modules/index.ts +5 -1
  22. package/src/modules/lst-apr.ts +36 -0
  23. package/src/modules/midas.ts +159 -0
  24. package/src/modules/pricer-from-api.ts +2 -2
  25. package/src/modules/pricer-lst.ts +1 -1
  26. package/src/modules/pricer.ts +3 -38
  27. package/src/modules/token-market-data.ts +202 -0
  28. package/src/node/deployer.ts +1 -36
  29. package/src/strategies/autoCompounderStrk.ts +1 -1
  30. package/src/strategies/base-strategy.ts +20 -3
  31. package/src/strategies/btc-vesu-extended-strategy/core-strategy.tsx +1486 -0
  32. package/src/strategies/btc-vesu-extended-strategy/services/operationService.ts +32 -0
  33. package/src/strategies/btc-vesu-extended-strategy/utils/constants.ts +3 -0
  34. package/src/strategies/btc-vesu-extended-strategy/utils/helper.ts +396 -0
  35. package/src/strategies/btc-vesu-extended-strategy/utils/types.ts +5 -0
  36. package/src/strategies/ekubo-cl-vault.tsx +123 -306
  37. package/src/strategies/index.ts +7 -1
  38. package/src/strategies/svk-strategy.ts +247 -0
  39. package/src/strategies/universal-adapters/adapter-optimizer.ts +65 -0
  40. package/src/strategies/universal-adapters/adapter-utils.ts +5 -1
  41. package/src/strategies/universal-adapters/avnu-adapter.ts +432 -0
  42. package/src/strategies/universal-adapters/baseAdapter.ts +181 -153
  43. package/src/strategies/universal-adapters/common-adapter.ts +98 -77
  44. package/src/strategies/universal-adapters/extended-adapter.ts +976 -0
  45. package/src/strategies/universal-adapters/index.ts +7 -1
  46. package/src/strategies/universal-adapters/unused-balance-adapter.ts +109 -0
  47. package/src/strategies/universal-adapters/vesu-adapter.ts +230 -230
  48. package/src/strategies/universal-adapters/vesu-borrow-adapter.ts +1247 -0
  49. package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +1306 -0
  50. package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +58 -51
  51. package/src/strategies/universal-lst-muliplier-strategy.tsx +716 -844
  52. package/src/strategies/universal-strategy.tsx +1103 -1181
  53. package/src/strategies/vesu-extended-strategy/services/operationService.ts +34 -0
  54. package/src/strategies/vesu-extended-strategy/types/transaction-metadata.ts +25 -0
  55. package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +77 -0
  56. package/src/strategies/vesu-extended-strategy/utils/constants.ts +50 -0
  57. package/src/strategies/vesu-extended-strategy/utils/helper.ts +367 -0
  58. package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +1420 -0
  59. package/src/strategies/vesu-rebalance.tsx +16 -20
  60. package/src/utils/health-factor-math.ts +11 -5
@@ -0,0 +1,1486 @@
1
+ import { getContractDetails, UniversalStrategySettings, PositionTypeAvnuExtended } from "../universal-strategy";
2
+ import { Web3Number } from "@/dataTypes";
3
+ import { ContractAddr } from "@/dataTypes";
4
+ import { SVKStrategy } from "../svk-strategy";
5
+ import { VesuBorrowSupplyAdapter } from "../universal-adapters/vesu-borrow-adapter";
6
+ import { getNoRiskTags, IStrategyMetadata, TokenInfo } from "@/interfaces";
7
+ import { IConfig } from "@/interfaces";
8
+ import { LIMIT_BALANCE, LIMIT_BALANCE_VALUE, MIN_PRICE_DIFFERENCE_BETWEEN_AVNU_AND_EXTENDED, MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP, WBTC_TOKEN_ADDRESS } from "../vesu-extended-strategy/utils/constants";
9
+ import { VesuMultiplyAdapter, VesuPools } from "../universal-adapters";
10
+ import { PricerBase } from "@/modules"
11
+ import { AUMTypes } from "../universal-strategy";
12
+ import { UNIVERSAL_MANAGE_IDS } from "../universal-strategy";
13
+ import { BaseAdapterConfig } from "../universal-adapters";
14
+ import { Global } from "@/global";
15
+ import { highlightTextWithLinks } from "@/interfaces";
16
+ import { PricerFromApi } from "@/modules";
17
+ import { APYType } from "../universal-adapters";
18
+ import { getMainnetConfig } from "@/interfaces";
19
+ import { AVNU_QUOTE_URL, AVNU_MIDDLEWARE, EXTENDED_CONTRACT } from "../universal-adapters";
20
+ import { PositionInfo } from "../universal-adapters";
21
+ import { ERC20 } from "@/modules";
22
+ import { SingleTokenInfo } from "../base-strategy";
23
+ import { logger } from "@/utils";
24
+ import { ExtendedAdapter } from "../universal-adapters";
25
+ import { AvnuAdapter } from "../universal-adapters/avnu-adapter";
26
+ import { MAX_LTV_BTC_USDC, TARGET_HF, USDC_TOKEN_DECIMALS } from "../vesu-extended-strategy/utils/constants";
27
+ import { WBTC_TOKEN_DECIMALS } from "../vesu-extended-strategy/utils/constants";
28
+ import { calculateBorrowAmountForStage1, calculateDecisiveFactorF, calculateVesuExposureExtraToDepositedCollateral } from "./utils/helper";
29
+ import { Call } from "starknet";
30
+ import { MINIMUM_EXTENDED_POSITION_SIZE } from "../vesu-extended-strategy/utils/constants";
31
+ import { OrderSide } from "@/modules";
32
+ import { _riskFactor, AUDIT_URL, calculateAmountDistributionForWithdrawalInWbtcStrategy, calculateExtendedLevergae, getFAQs, getInvestmentSteps } from "@/index.browser";
33
+ import { Operations } from "./services/operationService";
34
+ import { Protocols } from "@/interfaces";
35
+ import { CommonAdapter, UnusedBalanceAdapter } from "../universal-adapters";
36
+ import { calculateDebtAmountForMainatiningLtvOnVesu } from "./utils/helper"
37
+ import { calculateVesuLeverage } from "../vesu-extended-strategy/utils/helper";
38
+
39
+ export interface BtcFiVesuExtendedStrategySettings
40
+ extends UniversalStrategySettings {
41
+ underlyingToken: TokenInfo;
42
+ borrowable_assets: TokenInfo[];
43
+ targetHealthFactor: number;
44
+ quoteAmountToFetchPrice: Web3Number;
45
+ minHealthFactor: number;
46
+ aumOracle: ContractAddr;
47
+ minimumWBTCDifferenceForAvnuSwap: number;
48
+ }
49
+
50
+ export class BtcFiVesuExtendedStrategy<
51
+ S extends BtcFiVesuExtendedStrategySettings
52
+ >
53
+ extends SVKStrategy<S>
54
+ implements Operations {
55
+ constructor(
56
+ config: IConfig,
57
+ pricer: PricerBase,
58
+ metadata: IStrategyMetadata<S>
59
+ ) {
60
+ super(config, pricer, metadata);
61
+ this.metadata.additionalInfo.adapters.forEach((adapter) => {
62
+ adapter.adapter.config.networkConfig = this.config;
63
+ adapter.adapter.config.pricer = this.pricer;
64
+ if ((adapter.adapter as VesuMultiplyAdapter).vesuAdapter) {
65
+ (adapter.adapter as VesuMultiplyAdapter).vesuAdapter.networkConfig =
66
+ this.config;
67
+ (adapter.adapter as VesuMultiplyAdapter).vesuAdapter.pricer =
68
+ this.pricer;
69
+ }
70
+ });
71
+ }
72
+
73
+ getTag() {
74
+ return `${BtcFiVesuExtendedStrategy.name}:${this.metadata.name}`;
75
+ }
76
+
77
+ async getAssetPrices() {
78
+ const wbtcToken = Global.getDefaultTokens().find(
79
+ (token) => token.symbol === "WBTC"
80
+ )!;
81
+ const usdcToken = Global.getDefaultTokens().find(
82
+ (token) => token.symbol === "USDC"
83
+ )!;
84
+ const collateralPrice = await this.pricer.getPrice(wbtcToken.symbol);
85
+ const debtPrice = await this.pricer.getPrice(usdcToken.symbol);
86
+ return {
87
+ collateralPrice,
88
+ debtPrice,
89
+ };
90
+ }
91
+
92
+ async getUnusedBalanceUSDC(): Promise<SingleTokenInfo> {
93
+ const usdcToken = Global.getDefaultTokens().find(
94
+ (token) => token.symbol === "USDC"
95
+ )!;
96
+ const balance = await new ERC20(this.config).balanceOf(
97
+ usdcToken.address,
98
+ this.metadata.additionalInfo.vaultAllocator,
99
+ usdcToken.decimals
100
+ );
101
+ const price = await this.pricer.getPrice(usdcToken.symbol);
102
+ const usdValue =
103
+ Number(balance.toFixed(usdcToken.decimals)) * price.price;
104
+ return {
105
+ tokenInfo: usdcToken,
106
+ amount: balance,
107
+ usdValue,
108
+ };
109
+ }
110
+
111
+ async getVesuBorrowSupplyAdapter(): Promise<VesuBorrowSupplyAdapter | null> {
112
+ const vesuBorrowSupplyAdapter = this.metadata.additionalInfo.adapters.find(
113
+ (adapter) => adapter.adapter.name === VesuBorrowSupplyAdapter.name
114
+ );
115
+ if (!vesuBorrowSupplyAdapter) {
116
+ logger.error("vesu borrow supply adapter not found");
117
+ return null;
118
+ }
119
+ return vesuBorrowSupplyAdapter.adapter as VesuBorrowSupplyAdapter;
120
+ }
121
+
122
+ async getVesuAdapter(): Promise<VesuMultiplyAdapter | null> {
123
+ const vesuAdapter = this.metadata.additionalInfo.adapters.find(
124
+ (adapter) => adapter.adapter.name === VesuMultiplyAdapter.name
125
+ );
126
+ if (!vesuAdapter) {
127
+ logger.error("vesu adapter not found");
128
+ return null;
129
+ }
130
+ return vesuAdapter.adapter as VesuMultiplyAdapter;
131
+ }
132
+
133
+ async getAvnuAdapter(): Promise<AvnuAdapter | null> {
134
+ const avnuAdapter = this.metadata.additionalInfo.adapters.find(
135
+ (adapter) => adapter.adapter.name === AvnuAdapter.name
136
+ );
137
+ if (!avnuAdapter) {
138
+ logger.error("avnu adapter not found");
139
+ return null;
140
+ }
141
+ return avnuAdapter.adapter as AvnuAdapter;
142
+ }
143
+
144
+ async getExtendedAdapter(): Promise<ExtendedAdapter | null> {
145
+ const extendedAdapter = this.metadata.additionalInfo.adapters.find(
146
+ (adapter) => adapter.adapter.name === ExtendedAdapter.name
147
+ );
148
+ if (!extendedAdapter) {
149
+ logger.error("extended adapter not found");
150
+ return null;
151
+ }
152
+ return extendedAdapter.adapter as ExtendedAdapter;
153
+ }
154
+
155
+ async moveAssetsToVaultAllocator(amount: Web3Number, extendedAdapter: ExtendedAdapter): Promise<Call[]> {
156
+ try {
157
+ const usdceToken = Global.getDefaultTokens().find(
158
+ (token) => token.symbol === "USDCe"
159
+ )!;
160
+ const approveCall = new ERC20(this.config).approve(
161
+ usdceToken.address,
162
+ this.metadata.additionalInfo.vaultAllocator,
163
+ amount
164
+ );
165
+ const transferCall = new ERC20(this.config).transfer(
166
+ usdceToken.address,
167
+ this.metadata.additionalInfo.vaultAllocator,
168
+ amount
169
+ );
170
+ const proofsInfo = extendedAdapter.getProofsForFromLegacySwap(
171
+ this.getMerkleTree()
172
+ );
173
+ const proofGroups = proofsInfo.proofs;
174
+ const call = this.getManageCall(
175
+ proofGroups,
176
+ await proofsInfo.callConstructor({ amount: amount })
177
+ );
178
+ return [approveCall, transferCall, call];
179
+ } catch (err) {
180
+ logger.error(`error moving assets to vault allocator: ${err}`);
181
+ return [];
182
+ }
183
+ }
184
+
185
+ async shouldInvest(): Promise<{
186
+ shouldInvest: boolean;
187
+ collateralPrice: number;
188
+ wbtcAmountForStage1: Web3Number;
189
+ } | null> {
190
+ try {
191
+ const vesuAdapter = await this.getVesuAdapter();
192
+ if (!vesuAdapter) {
193
+ logger.error(
194
+ `Vesu adapter not configured in metadata. This is a configuration issue, not a temporary failure.`
195
+ );
196
+ return null;
197
+ }
198
+ logger.info(`${BtcFiVesuExtendedStrategy.name}::shouldInvest calling getUnusedBalance`);
199
+ const balance = await this.getUnusedBalance();
200
+ if (!Number.isFinite(balance.usdValue) || balance.usdValue < 0) {
201
+ logger.error(
202
+ `Invalid balance.usdValue: ${balance.usdValue}. Expected a finite, non-negative number.`
203
+ );
204
+ return {
205
+ shouldInvest: false,
206
+ collateralPrice: 0,
207
+ wbtcAmountForStage1: new Web3Number(0, 0),
208
+ };
209
+ }
210
+ const wbtcValueInUSD = new Web3Number(balance.usdValue.toFixed(2), 2);
211
+ const wbtcBalanceInWBTCUnits = new Web3Number(balance.amount.toFixed(WBTC_TOKEN_DECIMALS), WBTC_TOKEN_DECIMALS);
212
+ logger.info(`wbtcValueInUSD: ${wbtcValueInUSD}`);
213
+ // 95% of the total btc in vault allocator is invested
214
+ const amountToInvest = balance.amount;
215
+ //s.multipliedBy(AMOUNT_BUFFER_TO_KEEP_IN_INVESTING_CYCLE); right now full 100% of the btc in vault allocator is invested
216
+ logger.info(`amountToInvest: ${amountToInvest}`);
217
+ const {
218
+ collateralPrice,
219
+ } = await this.getAssetPrices();
220
+ /**
221
+ * If the amount is less than $10, then don't invest
222
+ */
223
+ const limitBalanceValue = new Web3Number(LIMIT_BALANCE_VALUE, 2);
224
+ if (amountToInvest.lessThan(limitBalanceValue.dividedBy(collateralPrice.price))) {
225
+ logger.info(`amountToInvest is less than limitBalanceValue, not investing`);
226
+ return null;
227
+ }
228
+ const assetsUnderVaultAllocator = await this.getAUM();
229
+ const assetsUnderVaultAllocatorInWBTCUnits = new Web3Number(assetsUnderVaultAllocator.net.amount.toFixed(WBTC_TOKEN_DECIMALS), WBTC_TOKEN_DECIMALS);
230
+ logger.info(`assetsUnderVaultAllocatorInWBTC: ${assetsUnderVaultAllocatorInWBTCUnits}`);
231
+ return {
232
+ shouldInvest: true,
233
+ collateralPrice: collateralPrice.price,
234
+ wbtcAmountForStage1: wbtcBalanceInWBTCUnits,
235
+ }
236
+ } catch (err) {
237
+ logger.error("error deciding invest", err);
238
+ return null
239
+ }
240
+ }
241
+
242
+ // boht the amounts are in USDC terms only
243
+ async shouldMoveAssets(vesuAmount: Web3Number, extendedAmount: Web3Number): Promise<Call[]> {
244
+ try {
245
+ const vesuAdapter = await this.getVesuAdapter();
246
+ const extendedAdapter = await this.getExtendedAdapter();
247
+ if (!vesuAdapter || !extendedAdapter || !extendedAdapter.client) {
248
+ logger.error(
249
+ `vesu or extended adapter not found: vesuAdapter=${vesuAdapter}, extendedAdapter=${extendedAdapter}`
250
+ );
251
+ return [];
252
+ }
253
+ const extendedHoldings = await extendedAdapter.getExtendedDepositAmount();
254
+ if (!extendedHoldings) {
255
+ logger.error(`error getting extended holdings: ${extendedHoldings}`);
256
+ return [];
257
+ }
258
+ const usdcAmountInWallet = (await this.getUnusedBalanceUSDC()).amount;
259
+ const usdcAmountOnExtendedAvailableForWithdrawal = parseFloat(
260
+ extendedHoldings.availableForWithdrawal
261
+ );
262
+ logger.info(`usdcAmountInWallet: ${usdcAmountInWallet}`);
263
+ logger.info(`${BtcFiVesuExtendedStrategy.name}::shouldMoveAssets calculating movements - Extended current: ${usdcAmountOnExtendedAvailableForWithdrawal}, Wallet: ${usdcAmountInWallet.toNumber()}, Target Extended: ${extendedAmount.toNumber()}, Target Vesu: ${vesuAmount.toNumber()}`);
264
+ let totalExtendedWithdrawal = new Web3Number(0, USDC_TOKEN_DECIMALS);
265
+ let totalExtendedDeposit = new Web3Number(0, USDC_TOKEN_DECIMALS);
266
+
267
+ if (extendedAmount.isNegative() && extendedAmount.abs().greaterThan(extendedAdapter.minimumExtendedMovementAmount)) {
268
+ totalExtendedWithdrawal = totalExtendedWithdrawal.plus(extendedAmount.abs());
269
+ }
270
+
271
+ // Calculate remaining Extended difference (target vs current)
272
+ // If extendedAmount was negative, we've already accounted for that withdrawal
273
+ // So we calculate based on what Extended will be after that withdrawal
274
+ const extendedTargetAmount = extendedAmount.abs(); // Use absolute value as target
275
+ // Convert to Web3Number to maintain precision throughout calculations
276
+ const projectedExtendedBalance = new Web3Number(usdcAmountOnExtendedAvailableForWithdrawal, USDC_TOKEN_DECIMALS);
277
+
278
+ let adjustedProjectedBalance = projectedExtendedBalance;
279
+ if (extendedAmount.isNegative()) {
280
+ adjustedProjectedBalance = projectedExtendedBalance.minus(extendedAmount.abs());
281
+ }
282
+
283
+ const extendedAmountDifference = extendedTargetAmount.minus(adjustedProjectedBalance);
284
+ console.log("extendedAmountDifference", extendedAmountDifference)
285
+ const extendedAmountDifferenceAbs = extendedAmountDifference.abs();
286
+ console.log("extendedAmountDifferenceAbs", extendedAmountDifferenceAbs)
287
+ // Track additional Extended movements
288
+ if (extendedAmountDifference.lessThan(0)) {
289
+ totalExtendedWithdrawal = totalExtendedWithdrawal.plus(extendedAmountDifferenceAbs);
290
+ } else if (extendedAmountDifference.greaterThan(0)) {
291
+ totalExtendedDeposit = totalExtendedDeposit.plus(extendedAmountDifference);
292
+ }
293
+
294
+ const vesuTargetAmount = vesuAmount.abs();
295
+ const projectedWalletBalance = usdcAmountInWallet
296
+ .plus(totalExtendedWithdrawal)
297
+ .minus(totalExtendedDeposit);
298
+
299
+ let vesuAmountDifference = vesuTargetAmount.minus(projectedWalletBalance);
300
+ const vesuAmountDifferenceAbs = vesuAmountDifference.abs();
301
+
302
+ logger.info(`${BtcFiVesuExtendedStrategy.name}::shouldMoveAssets calculated movements - Extended withdrawal: ${totalExtendedWithdrawal.toNumber()}, Extended deposit: ${totalExtendedDeposit.toNumber()}, Extended diff: ${extendedAmountDifference.toNumber()}, Projected wallet: ${projectedWalletBalance.toNumber()}, Vesu diff: ${vesuAmountDifference.toNumber()}`);
303
+ let calls: Call[] = [];
304
+
305
+ // Handle negative extendedAmount (initial withdrawal needed)
306
+ if (extendedAmount.isNegative() && extendedAmount.abs().greaterThan(extendedAdapter.minimumExtendedMovementAmount)) {
307
+ try {
308
+ const { calls: extendedCalls, status: extendedStatus } = await this.moveAssets(
309
+ {
310
+ to: Protocols.VAULT.name,
311
+ from: Protocols.EXTENDED.name,
312
+ amount: extendedAmount.abs(),
313
+ },
314
+ extendedAdapter,
315
+ vesuAdapter
316
+ );
317
+ if (extendedStatus) {
318
+ calls.push(...extendedCalls);
319
+ } else {
320
+ return [];
321
+ }
322
+ } catch (err) {
323
+ logger.error(`Failed moving assets to vault: ${err}`);
324
+ }
325
+ }
326
+
327
+ if (vesuAmount.isNegative() && vesuAmount.abs().greaterThan(vesuAdapter.minimumVesuMovementAmount)) {
328
+ try {
329
+ const { calls: vesuCalls, status: vesuStatus } = await this.moveAssets(
330
+ {
331
+ to: Protocols.EXTENDED.name,
332
+ from: Protocols.VESU.name,
333
+ amount: vesuAmount.abs(),
334
+ },
335
+ extendedAdapter,
336
+ vesuAdapter
337
+ );
338
+ calls.push(...vesuCalls);
339
+ if (!vesuStatus) {
340
+ return [];
341
+ }
342
+ } catch (err) {
343
+ logger.error(`Failed moving assets to vault: ${err}`);
344
+ }
345
+ }
346
+
347
+ // Handle Extended adjustments based on calculated difference
348
+ if (extendedAmountDifferenceAbs.greaterThan(extendedAdapter.minimumExtendedMovementAmount)) {
349
+ if (extendedAmountDifference.greaterThan(0)) {
350
+ try {
351
+ const { calls: extendedCalls, status: extendedStatus } = await this.moveAssets(
352
+ {
353
+ to: Protocols.EXTENDED.name,
354
+ from: Protocols.VAULT.name,
355
+ amount: extendedAmountDifference,
356
+ },
357
+ extendedAdapter,
358
+ vesuAdapter
359
+ );
360
+ if (extendedStatus) {
361
+ calls.push(...extendedCalls);
362
+ } else {
363
+ logger.error(`Failed to move assets to extended - operation returned false status`);
364
+ return [];
365
+ }
366
+ } catch (err) {
367
+ logger.error(`Failed moving assets to extended: ${err}`);
368
+ return [];
369
+ }
370
+ } else if (extendedAmountDifference.lessThan(0)) {
371
+ try {
372
+ const { calls: extendedCalls, status: extendedStatus } = await this.moveAssets(
373
+ {
374
+ to: Protocols.VAULT.name,
375
+ from: Protocols.EXTENDED.name,
376
+ amount: extendedAmountDifferenceAbs,
377
+ },
378
+ extendedAdapter,
379
+ vesuAdapter
380
+ );
381
+ if (extendedStatus) {
382
+ calls.push(...extendedCalls);
383
+ } else {
384
+ logger.error(`Failed to withdraw from extended - operation returned false status`);
385
+ return [];
386
+ }
387
+ } catch (err) {
388
+ logger.error(`Failed moving assets from extended to vault: ${err}`);
389
+ return [];
390
+ }
391
+ }
392
+ }
393
+
394
+ // Handle Vesu adjustments based on calculated difference (already adjusted for Extended movements)
395
+ if (vesuAmountDifferenceAbs.greaterThan(vesuAdapter.minimumVesuMovementAmount)) {
396
+ if (vesuAmountDifference.lessThanOrEqualTo(0)) {
397
+ logger.warn(
398
+ `Vesu amount difference is negative or zero: ${vesuAmountDifference.toNumber()}. Skipping operation.`
399
+ );
400
+ } else {
401
+ // Move assets from Extended to Vault (which will then go to Vesu)
402
+ try {
403
+ const { calls: vesuCalls, status: vesuStatus } = await this.moveAssets(
404
+ {
405
+ to: Protocols.VAULT.name,
406
+ from: Protocols.EXTENDED.name,
407
+ amount: vesuAmountDifference,
408
+ },
409
+ extendedAdapter,
410
+ vesuAdapter
411
+ );
412
+ if (!vesuStatus) {
413
+ logger.error(`Failed to move assets to vesu - operation returned false status`);
414
+ return [];
415
+ }
416
+ calls.push(...vesuCalls);
417
+ } catch (err) {
418
+ logger.error(`Failed moving assets to vault: ${err}`);
419
+ return [];
420
+ }
421
+ }
422
+ }
423
+ return calls;
424
+ } catch (err) {
425
+ logger.error(`Failed moving assets to vesu: ${err}`);
426
+ return [];
427
+ }
428
+ }
429
+
430
+ async moveAssetsFromVesuToVault(amountInWbtc: Web3Number, vesuBorrowSupplyAdapter: VesuBorrowSupplyAdapter, vesuMultiplyAdapter: VesuMultiplyAdapter): Promise<{
431
+ calls: Call[];
432
+ status: boolean;
433
+ }>{
434
+ try{
435
+ let calls: Call[] = [];
436
+ const aum = await this.getAUM();
437
+ logger.info(`aum: ${aum.net.amount}`);
438
+ const wbtcInVaultAllocator = await this.getUnusedBalance();
439
+ logger.info(`wbtcInVaultAllocator: ${wbtcInVaultAllocator}`);
440
+ const {
441
+ collateralTokenAmount,
442
+ debtTokenAmount
443
+ } = await vesuMultiplyAdapter.vesuAdapter.getAssetPrices();
444
+ logger.info(`collateralTokenAmount: ${collateralTokenAmount}`);
445
+ logger.info(`debtTokenAmount: ${debtTokenAmount}`);
446
+ const {
447
+ collateralPrice,
448
+ debtPrice
449
+ } = await this.getAssetPrices();
450
+ logger.info(`collateralPrice: ${collateralPrice}`);
451
+ logger.info(`debtPrice: ${debtPrice}`);
452
+ const vesuLeverage = calculateVesuLeverage()
453
+ const debtAmountToReduce = calculateDebtAmountForMainatiningLtvOnVesu(
454
+ debtTokenAmount.multipliedBy(-1),
455
+ collateralTokenAmount,
456
+ amountInWbtc.multipliedBy(-1).multipliedBy(vesuLeverage), // as the amount is being reduced from the position
457
+ collateralPrice.price,
458
+ debtPrice.price
459
+ )
460
+ if (!debtAmountToReduce) {
461
+ logger.error(`error calculating debt amount to reduce ${debtAmountToReduce} in moveAssetsFromVesuToVault`);
462
+ return {
463
+ calls: [],
464
+ status: false
465
+ };
466
+ }
467
+ logger.info(`debtAmountToReduce: ${debtAmountToReduce} in moveAssetsFromVesuToVault`);
468
+ const proofs = vesuBorrowSupplyAdapter.getProofsForMultiply(this.getMerkleTree());
469
+ const proofGroups = proofs.proofs;
470
+ const call = this.getManageCall(
471
+ proofGroups,
472
+ await proofs.callConstructor({
473
+ amount: amountInWbtc,
474
+ debtAmount: debtAmountToReduce,
475
+ isWithdraw: true,
476
+ closePosition: false
477
+ })
478
+ )
479
+ calls.push(call);
480
+ return {
481
+ calls: calls,
482
+ status: true
483
+ };
484
+ }catch(err){
485
+ logger.error(`error moving assets from vesu to vault: ${err}`);
486
+ return {
487
+ calls: [],
488
+ status: false
489
+ };
490
+ }
491
+ }
492
+
493
+ /**
494
+ * This function returns the calls used to move assets from one protocol to another
495
+ * @param params - The parameters for the move assets operation
496
+ * @param extendedAdapter - The extended adapter
497
+ * @param vesuAdapter - The vesu adapter
498
+ * @returns The calls to be made to the protocols
499
+ */
500
+ async moveAssets(params: {
501
+ from: string;
502
+ to: string;
503
+ amount: Web3Number;
504
+ }, extendedAdapter: ExtendedAdapter, vesuAdapter: VesuMultiplyAdapter): Promise<{
505
+ calls: Call[];
506
+ status: boolean;
507
+ }> {
508
+ try {
509
+ const avnuAdapter = await this.getAvnuAdapter();
510
+ const vesuSupplyBorrowAdapter = await this.getVesuBorrowSupplyAdapter();
511
+ if (!vesuSupplyBorrowAdapter) {
512
+ logger.error(`vesu supply borrow adapter not found: ${vesuSupplyBorrowAdapter}`);
513
+ return {
514
+ calls: [],
515
+ status: false
516
+ };
517
+ }
518
+ if (!avnuAdapter) {
519
+ logger.error(`avnu adapter not found: ${avnuAdapter}`);
520
+ return {
521
+ calls: [],
522
+ status: false
523
+ };
524
+ }
525
+ logger.info(`moveAssets params, ${JSON.stringify(params)}`);
526
+ const {
527
+ collateralPrice,
528
+ } = await this.getAssetPrices();
529
+
530
+ if (params.to === Protocols.EXTENDED.name && params.from === Protocols.VAULT.name) {
531
+ const proofsInfo = extendedAdapter.getProofs(
532
+ true,
533
+ this.getMerkleTree()
534
+ );
535
+ const calls = [];
536
+ const proofGroups = proofsInfo.proofs;
537
+ const call = this.getManageCall(
538
+ proofGroups,
539
+ await proofsInfo.callConstructor({ amount: params.amount })
540
+ );
541
+ calls.push(call);
542
+ return {
543
+ calls: [call],
544
+ status: true
545
+ };
546
+ } else if (params.to === Protocols.VAULT.name && params.from === Protocols.EXTENDED.name) {
547
+ const extendedLeverage = calculateExtendedLevergae();
548
+ const extendedHoldings = await extendedAdapter.getExtendedDepositAmount();
549
+ if (!extendedHoldings) {
550
+ logger.error(`error getting extended holdings: ${extendedHoldings}`);
551
+ return {
552
+ calls: [],
553
+ status: false
554
+ };
555
+ }
556
+ const extendedHoldingAmount = new Web3Number(
557
+ extendedHoldings.availableForWithdrawal,
558
+ USDC_TOKEN_DECIMALS
559
+ );
560
+ logger.info(`${BtcFiVesuExtendedStrategy.name}::moveAssets extendedHoldingAmount: ${extendedHoldingAmount.toNumber()}`);
561
+ if (params.amount.abs().greaterThan(extendedHoldingAmount)) {
562
+ const leftAmountAfterWithdrawalAmountInAccount = params.amount.abs().minus(extendedHoldingAmount);
563
+ logger.info(`${BtcFiVesuExtendedStrategy.name}::moveAssets leftAmountAfterWithdrawalAmountInAccount: ${leftAmountAfterWithdrawalAmountInAccount.toNumber()}`);
564
+ const btcAmount = leftAmountAfterWithdrawalAmountInAccount.dividedBy(collateralPrice.price);
565
+ const openLongPosition = btcAmount.multipliedBy(3).greaterThan(MINIMUM_EXTENDED_POSITION_SIZE) ? await extendedAdapter.createOrder(
566
+ extendedLeverage.toString(),
567
+ btcAmount.toNumber(),
568
+ OrderSide.BUY
569
+ ) : await extendedAdapter.createOrder(
570
+ extendedLeverage.toString(),
571
+ 0.000034, // just in case amount falls short then we need to create a withdrawal
572
+ OrderSide.BUY
573
+ )
574
+ if (!openLongPosition) {
575
+ logger.error(`error opening long position: ${openLongPosition}`);
576
+ return {
577
+ calls: [],
578
+ status: false
579
+ };
580
+ }
581
+ await new Promise(resolve => setTimeout(resolve, 5000));
582
+ }
583
+ const withdrawalFromExtended =
584
+ await extendedAdapter.withdrawFromExtended(params.amount);
585
+ if (withdrawalFromExtended) {
586
+ /**
587
+ * We need to move assets from my wallet back to vault contract
588
+ */
589
+ const extendedHoldings = await extendedAdapter.getExtendedDepositAmount();
590
+ logger.info(`extendedHoldings after withdrawal ${extendedHoldings?.availableForWithdrawal}`);
591
+ const calls = await this.moveAssetsToVaultAllocator(params.amount, extendedAdapter);
592
+ if (calls.length > 0) {
593
+ return {
594
+ calls: calls,
595
+ status: true
596
+ };
597
+ }
598
+ } else {
599
+ logger.error("withdrawal from extended failed");
600
+ return {
601
+ calls: [],
602
+ status: false
603
+ };
604
+ }
605
+ } else if (params.to === Protocols.VAULT.name && params.from === Protocols.VESU.name) {
606
+ //withdraw from vesu
607
+ /**
608
+ * Amount for this movement is in wbtc terms only
609
+ */
610
+ let calls: Call[] = [];
611
+ const {
612
+ calls:vesuWithdrawalCalls,
613
+ status:vesuWithdrawalStatus
614
+ } = await this.moveAssetsFromVesuToVault(
615
+ params.amount,
616
+ vesuSupplyBorrowAdapter,
617
+ vesuAdapter
618
+ )
619
+ if (!vesuWithdrawalStatus) {
620
+ logger.error("error withdrawing from vesu");
621
+ return {
622
+ calls: [],
623
+ status: false
624
+ };
625
+ }
626
+ calls.push(...vesuWithdrawalCalls);
627
+ return {
628
+ calls: calls,
629
+ status: true
630
+ };
631
+ }
632
+ return {
633
+ calls: [],
634
+ status: false
635
+ };
636
+ } catch (err) {
637
+ logger.error(`error moving assets: ${err}`);
638
+ return {
639
+ calls: [],
640
+ status: false
641
+ };
642
+ }
643
+ }
644
+
645
+
646
+ /**
647
+ * amountInWbtc is the amount of wbtc tokens on vault allocator deposited by the user, to be used as collateral for borrowing usdc from vesu
648
+ * This function is used to first lend wbtc and then borrow usdc from vesu
649
+ * @returns { extendedAmountInBTC: Web3Number; calls: Call[]; }
650
+ * extendedAmountInBTC: The amount of BTC that is needed to be deposited to the extended contract
651
+ * calls: The calls to be made to the extended contract
652
+ */
653
+ async handleLendingBorrowingForStage1(amountInWBTC: Web3Number): Promise<{
654
+ calls: Call[];
655
+ debtTaken: Web3Number;
656
+ }> {
657
+ try {
658
+ const vesuBorrowSupplyAdapter = await this.getVesuBorrowSupplyAdapter();
659
+ if (!vesuBorrowSupplyAdapter) {
660
+ logger.error("vesu borrow supply adapter not found");
661
+ return {
662
+ calls: [],
663
+ debtTaken: new Web3Number(0, USDC_TOKEN_DECIMALS)
664
+ }
665
+ }
666
+ const supplyToken = vesuBorrowSupplyAdapter.config.supplyToken;
667
+ const borrowToken = vesuBorrowSupplyAdapter.config.borrowToken;
668
+ let calls: Call[] = [];
669
+ if (!vesuBorrowSupplyAdapter) {
670
+ logger.error("vesu borrow supply adapter not found");
671
+ return {
672
+ calls: [],
673
+ debtTaken: new Web3Number(0, USDC_TOKEN_DECIMALS)
674
+ }
675
+ }
676
+ const collateralPrice = await vesuBorrowSupplyAdapter.config.pricer?.getPrice(
677
+ supplyToken.symbol
678
+ );
679
+ const debtPrice = await vesuBorrowSupplyAdapter.config.pricer?.getPrice(borrowToken.symbol);
680
+ const existingPosition = await vesuBorrowSupplyAdapter.vesuAdapter.getPositions(
681
+ vesuBorrowSupplyAdapter.config.networkConfig
682
+ );
683
+ const existingCollateralAmount = existingPosition[0].amount;
684
+ /**
685
+ * Aum is the assets under vault allocator in wbtc, when user adds lets say 0.5 btc then that also is already counted inside this aum
686
+ */
687
+ const additionalCollateralForLendingBorrowing = amountInWBTC;
688
+ const assetsUnderVaultAllocator = await this.getAUM();
689
+ const vesuCollateralAmountWithoutMultiplier = new Web3Number(existingCollateralAmount.minus(assetsUnderVaultAllocator.net.amount).plus(amountInWBTC).toFixed(WBTC_TOKEN_DECIMALS), WBTC_TOKEN_DECIMALS);
690
+ logger.info(`vesuCollateralAmountWithoutMultiplier :: ${vesuCollateralAmountWithoutMultiplier}`);
691
+
692
+ const debtAmountStage1 = calculateBorrowAmountForStage1(
693
+ TARGET_HF, // not the target health factor, but the percentage of collateral we borrow back
694
+ MAX_LTV_BTC_USDC,
695
+ vesuCollateralAmountWithoutMultiplier, // WBTC terms, keep original decimals
696
+ additionalCollateralForLendingBorrowing, // WBTC terms, keep original decimals
697
+ collateralPrice.price,
698
+ debtPrice.price
699
+ );
700
+ logger.info(`debtAmountStage1: ${debtAmountStage1}`);
701
+ if (debtAmountStage1?.eq(0) || !debtAmountStage1) {
702
+ logger.info(`${BtcFiVesuExtendedStrategy.name}::handleLendingBorrowingForStage1 debt amount is 0, not lending or borrowing`);
703
+ return {
704
+ calls: [],
705
+ debtTaken: new Web3Number(0, USDC_TOKEN_DECIMALS)
706
+ }
707
+ }
708
+ if (debtAmountStage1.lessThan(0)) {
709
+ const proofs = vesuBorrowSupplyAdapter.getProofsForDebtPayment(this.getMerkleTree());
710
+ const debtToPay = debtAmountStage1.abs();
711
+ const proofGroups = proofs.proofs;
712
+ const call = this.getManageCall(
713
+ proofGroups,
714
+ await proofs.callConstructor({
715
+ debtAmount: debtToPay,
716
+ supplyAmount: new Web3Number(0, WBTC_TOKEN_DECIMALS)
717
+ })
718
+ );
719
+ calls.push(call);
720
+ return {
721
+ calls: calls as Call[],
722
+ debtTaken: debtAmountStage1
723
+ }
724
+ }
725
+ logger.info(`The amount of debt is positive, ${debtAmountStage1}`);
726
+ const proofs = vesuBorrowSupplyAdapter.getProofsForBorrowing(this.getMerkleTree());
727
+ const proofGroups = proofs.proofs;
728
+ const call = this.getManageCall(
729
+ proofGroups,
730
+ await proofs.callConstructor({
731
+ supplyAmount: amountInWBTC,
732
+ debtAmount: debtAmountStage1
733
+ })
734
+ );
735
+ calls.push(call);
736
+ logger.info(`calls: ${calls}`);
737
+ return {
738
+ calls: calls as Call[],
739
+ debtTaken: debtAmountStage1
740
+ }
741
+ } catch (err) {
742
+ logger.error(`error handling lending borrowing for stage 1: ${err}`);
743
+ return {
744
+ calls: [],
745
+ debtTaken: new Web3Number(0, USDC_TOKEN_DECIMALS)
746
+ }
747
+ }
748
+ }
749
+
750
+ /**
751
+ * This function is used to withdraw the assets from the vault positions to the vault allocator
752
+ * @param amount is the amount of wbtc to be arranged
753
+ * @returns { calls: Call[]; status: boolean; }
754
+ * calls: The calls to be made to the avnu contract
755
+ * status: The status of the withdrawal
756
+ */
757
+ async handleWithdraw(amount: Web3Number): Promise<{
758
+ calls: Call[];
759
+ status: boolean;
760
+ }> {
761
+ try {
762
+ let calls: Call[] = [];
763
+ let status: boolean = true;
764
+ const wbtcAmountInVaultAllocator = await this.getUnusedBalance();
765
+ const wbtcAmountDifference = amount.minus(wbtcAmountInVaultAllocator.amount);
766
+ if (wbtcAmountDifference.lessThan(0)) {
767
+ const withdrawCall = await this.getBringLiquidityCall({ amount: amount });
768
+ calls.push(withdrawCall);
769
+ return {
770
+ calls: calls,
771
+ status: true
772
+ }
773
+ }
774
+ const vesuAdapter = await this.getVesuAdapter();
775
+ const extendedAdapter = await this.getExtendedAdapter();
776
+ if (!vesuAdapter || !extendedAdapter || !extendedAdapter.client) {
777
+ status = false;
778
+ logger.error(
779
+ `vesu or extended adapter not found: vesuAdapter=${vesuAdapter}, extendedAdapter=${extendedAdapter}`
780
+ );
781
+ return {
782
+ calls: calls,
783
+ status: status
784
+ };
785
+ }
786
+ const { collateralTokenAmount } =
787
+ await vesuAdapter.vesuAdapter.getAssetPrices();
788
+ const aum = await this.getAUM();
789
+ const assetsUnderVaultAllocatorInWBTC = aum.net.amount;
790
+ const {
791
+ collateralPrice,
792
+ } = await this.getAssetPrices();
793
+ const extendedPositon = await extendedAdapter.getAllOpenPositions();
794
+ if (!extendedPositon) {
795
+ status = false;
796
+ logger.error("error getting extended position", extendedPositon);
797
+ return {
798
+ calls: calls,
799
+ status: status
800
+ }
801
+ }
802
+ const vesuExposureInMultiplier = calculateVesuExposureExtraToDepositedCollateral(
803
+ assetsUnderVaultAllocatorInWBTC,
804
+ wbtcAmountInVaultAllocator.amount,
805
+ collateralTokenAmount
806
+ )
807
+ if (!vesuExposureInMultiplier) {
808
+ status = false;
809
+ logger.error("error calculating vesu exposure in multiplier");
810
+ return {
811
+ calls: calls,
812
+ status: status
813
+ }
814
+ }
815
+ const amountDistributionForWithdrawal = calculateAmountDistributionForWithdrawalInWbtcStrategy(
816
+ amount,
817
+ vesuExposureInMultiplier,
818
+ extendedPositon
819
+ )
820
+ if (!amountDistributionForWithdrawal) {
821
+ status = false;
822
+ logger.error("error calculating amount distribution for withdrawal");
823
+ return {
824
+ calls: calls,
825
+ status: status
826
+ }
827
+ }
828
+ const {
829
+ vesuAmount,
830
+ extendedAmount
831
+ } = amountDistributionForWithdrawal;
832
+
833
+ if (status && vesuAmount.greaterThan(0)) {
834
+ /**
835
+ * Vesu amount is in wbtc terms only
836
+ */
837
+ const { calls: vesuCalls, status: vesuStatus } = await this.moveAssets(
838
+ {
839
+ amount: vesuAmount,
840
+ from: Protocols.VESU.name,
841
+ to: Protocols.VAULT.name,
842
+ },
843
+ extendedAdapter,
844
+ vesuAdapter
845
+ );
846
+ status = vesuStatus;
847
+ calls.push(...vesuCalls);
848
+ }
849
+ if (status && extendedAmount.greaterThan(0)) {
850
+ const extendedAmountInUsdc = new Web3Number(extendedAmount.multipliedBy(collateralPrice.price).toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS);
851
+ const { calls: extendedCalls, status: extendedStatus } = await this.moveAssets(
852
+ {
853
+ amount: extendedAmountInUsdc,
854
+ from: Protocols.EXTENDED.name,
855
+ to: Protocols.VAULT.name,
856
+ },
857
+ extendedAdapter,
858
+ vesuAdapter
859
+ );
860
+ status = extendedStatus;
861
+ if (status) {
862
+ calls.push(...extendedCalls);
863
+ } else {
864
+ logger.error("error moving assets to vault: extendedStatus: ${extendedStatus}");
865
+ return {
866
+ calls: [],
867
+ status: status
868
+ };
869
+ }
870
+ }
871
+ const withdrawCall = await this.getBringLiquidityCall({
872
+ amount: amount
873
+ })
874
+ logger.info("withdraw call", withdrawCall);
875
+ calls.push(withdrawCall);
876
+ return {
877
+ calls: calls,
878
+ status: true
879
+ }
880
+ } catch (err) {
881
+ logger.error(`error handling withdraw: ${err}`);
882
+ return {
883
+ calls: [],
884
+ status: false
885
+ }
886
+ }
887
+ }
888
+
889
+ /**
890
+ * This function is used to handle the stage 2 positions, that is opening leveraged positions on extended and vesu
891
+ * @returns { decisiveFactor: Web3Number; vesuAmountInUSDC: Web3Number; extendedAmountInUSDC: Web3Number; }
892
+ * decisiveFactor: The decisive factor for the stage 2 positions
893
+ * vesuAmountInUSDC: The amount of usdc to be invested in vesu
894
+ * extendedAmountInUSDC: The amount of usdc to be invested in extended
895
+ */
896
+ async handleStage2Positions(): Promise<{
897
+ decisiveFactor: Web3Number;
898
+ vesuAmountInUSDC: Web3Number;
899
+ extendedAmountInUSDC: Web3Number;
900
+ }> {
901
+ try {
902
+ const extendedAdapter = await this.getExtendedAdapter();
903
+ let decisiveFactor = new Web3Number(0, 3);
904
+ let vesuAmountInUSDC = new Web3Number(0, USDC_TOKEN_DECIMALS);
905
+ let extendedAmountInUSDC = new Web3Number(0, USDC_TOKEN_DECIMALS);
906
+ if (!extendedAdapter) {
907
+ logger.error("extended adapter not found");
908
+ return {
909
+ decisiveFactor,
910
+ vesuAmountInUSDC,
911
+ extendedAmountInUSDC
912
+ }
913
+ }
914
+ const extendedPosition = await extendedAdapter.getAllOpenPositions();
915
+ if (!extendedPosition) {
916
+ logger.error("error getting extended position");
917
+ return {
918
+ decisiveFactor,
919
+ vesuAmountInUSDC,
920
+ extendedAmountInUSDC
921
+ }
922
+ }
923
+ const extendedExposureWBTC = extendedPosition.length > 0 ? new Web3Number(extendedPosition[0].size || 0, WBTC_TOKEN_DECIMALS) : new Web3Number(0, WBTC_TOKEN_DECIMALS);
924
+ const vesuAdapter = await this.getVesuAdapter();
925
+ if (!vesuAdapter) {
926
+ logger.error("vesu adapter not found");
927
+ return {
928
+ decisiveFactor,
929
+ vesuAmountInUSDC,
930
+ extendedAmountInUSDC
931
+ }
932
+ }
933
+ let {
934
+ collateralPrice,
935
+ debtPrice
936
+ } = await vesuAdapter.vesuAdapter.getAssetPrices();
937
+ if (!collateralPrice || !debtPrice) {
938
+ logger.error("error getting collateral price or debt price");
939
+ return {
940
+ decisiveFactor,
941
+ vesuAmountInUSDC,
942
+ extendedAmountInUSDC
943
+ }
944
+ }
945
+ /**
946
+ * Making sure debt price is positive,
947
+ */
948
+ const debtPriceInUsdc = new Web3Number(debtPrice.price, USDC_TOKEN_DECIMALS).abs();
949
+ const vesuPosition = await vesuAdapter.vesuAdapter.getPositions(vesuAdapter.config.networkConfig);
950
+ const vesuCollateralAmount = vesuPosition[0].amount;
951
+ logger.info(`vesuCollateralAmount: ${vesuCollateralAmount}`);
952
+ const vesuDebtAmount = vesuPosition[1].amount.abs();
953
+ logger.info(`vesuDebtAmount: ${vesuDebtAmount}`);
954
+ const unusedBalanceUSDC = await this.getUnusedBalanceUSDC();
955
+ logger.info(`unusedBalanceUSDC: ${unusedBalanceUSDC}`);
956
+ const unusedBalanceUSDCe = (await extendedAdapter.getExtendedDepositAmount())?.availableForWithdrawal || new Web3Number(0, USDC_TOKEN_DECIMALS);
957
+ const totalBalanceUSDC = unusedBalanceUSDC.amount.plus(unusedBalanceUSDCe);
958
+ const wbtcBalance = await this.getUnusedBalance();
959
+ const assetsUnderVaultAllocator = await this.getAUM();
960
+ const assetsUnderVaultAllocatorInWBTC = new Web3Number(assetsUnderVaultAllocator.net.amount.toFixed(WBTC_TOKEN_DECIMALS), WBTC_TOKEN_DECIMALS);
961
+ const decisiveFactorF = calculateDecisiveFactorF(
962
+ extendedExposureWBTC,
963
+ assetsUnderVaultAllocatorInWBTC,
964
+ wbtcBalance.amount,
965
+ vesuCollateralAmount,
966
+ totalBalanceUSDC,
967
+ vesuDebtAmount,
968
+ collateralPrice.price,
969
+ debtPriceInUsdc.toNumber(),
970
+ )
971
+
972
+ if (!decisiveFactorF) {
973
+ logger.error("error calculating decisive factor f");
974
+ return {
975
+ decisiveFactor,
976
+ vesuAmountInUSDC,
977
+ extendedAmountInUSDC
978
+ }
979
+ }
980
+ decisiveFactor = decisiveFactorF;
981
+ vesuAmountInUSDC = new Web3Number(decisiveFactor.multipliedBy(totalBalanceUSDC.toWei()).toFixed(0), 0);
982
+ extendedAmountInUSDC = new Web3Number(totalBalanceUSDC.toWei(), 0).minus(vesuAmountInUSDC);
983
+ logger.info(`vesuAmountInUSDC: ${vesuAmountInUSDC}`);
984
+ logger.info(`extendedAmountInUSDC: ${extendedAmountInUSDC}`);
985
+ logger.info(`decisiveFactor: ${decisiveFactor}`);
986
+ return {
987
+ decisiveFactor,
988
+ vesuAmountInUSDC: vesuAmountInUSDC.dividedBy(10 ** USDC_TOKEN_DECIMALS),
989
+ extendedAmountInUSDC: extendedAmountInUSDC.dividedBy(10 ** USDC_TOKEN_DECIMALS)
990
+ }
991
+ } catch (err) {
992
+ logger.error("error handling stage 2 positions", err);
993
+ return {
994
+ decisiveFactor: new Web3Number(0, 3),
995
+ vesuAmountInUSDC: new Web3Number(0, USDC_TOKEN_DECIMALS),
996
+ extendedAmountInUSDC: new Web3Number(0, USDC_TOKEN_DECIMALS)
997
+ }
998
+ }
999
+ }
1000
+
1001
+ /**
1002
+ * This function is used to check the price difference between avnu and extended
1003
+ * @param extendedAdapter - The extended adapter
1004
+ * @param vesuAdapter - The vesu adapter
1005
+ * @param avnuAdapter - The avnu adapter
1006
+ * @param positionType - The position type
1007
+ * @returns { boolean } - The result of the check
1008
+ * true if the price difference is greater than the minimum price difference for swap opening
1009
+ * false if the price difference is less than the minimum price difference for swap opening
1010
+ */
1011
+ async checkPriceDifferenceBetweenAvnuAndExtended(extendedAdapter: ExtendedAdapter, vesuAdapter: VesuMultiplyAdapter, avnuAdapter: AvnuAdapter, positionType: PositionTypeAvnuExtended): Promise<boolean> {
1012
+ const {
1013
+ ask, bid
1014
+ } = await extendedAdapter.fetchOrderBookBTCUSDC();
1015
+ const price = ask.plus(bid).dividedBy(2);
1016
+ const btcToken = vesuAdapter.config.supportedPositions[0].asset;
1017
+ const btcPriceAvnu = await avnuAdapter.getPriceOfToken(btcToken.address.toString());
1018
+
1019
+ if (!btcPriceAvnu) {
1020
+ logger.error(`error getting btc price avnu: ${btcPriceAvnu}`);
1021
+ return false;
1022
+ }
1023
+ logger.info(`price: ${price}`);
1024
+ logger.info(`btcPriceAvnu: ${btcPriceAvnu}`);
1025
+ const priceDifference = new Web3Number(price.minus(btcPriceAvnu).toFixed(2), 0);
1026
+ if (priceDifference.isNegative()) {
1027
+ return false;
1028
+ }
1029
+ if (positionType === PositionTypeAvnuExtended.OPEN) {
1030
+ logger.info(`price difference between avnu and extended for open position: ${priceDifference.toNumber()}, minimumExtendedPriceDifferenceForSwapOpen: ${avnuAdapter.config.minimumExtendedPriceDifferenceForSwapOpen}`);
1031
+ const result = priceDifference.greaterThanOrEqualTo(avnuAdapter.config.minimumExtendedPriceDifferenceForSwapOpen); // 500 for now
1032
+ logger.info(`result: ${result}`);
1033
+ return result;
1034
+ } else {
1035
+ logger.info(`price difference between avnu and extended for close position: ${priceDifference.toNumber()}, maximumExtendedPriceDifferenceForSwapClosing: ${avnuAdapter.config.maximumExtendedPriceDifferenceForSwapClosing}`);
1036
+ const result = priceDifference.lessThanOrEqualTo(avnuAdapter.config.maximumExtendedPriceDifferenceForSwapClosing); // 1000 for now
1037
+ logger.info(`result: ${result}`);
1038
+ return result;
1039
+ }
1040
+ }
1041
+
1042
+ async getAUM(): Promise<{ net: SingleTokenInfo, prevAum: Web3Number, splits: PositionInfo[] }> {
1043
+ const allPositions: PositionInfo[] = [];
1044
+ const wbtcToken = Global.getDefaultTokens().find(
1045
+ (token) => token.symbol === this.asset().symbol
1046
+ )!;
1047
+ const usdcToken = Global.getDefaultTokens().find(
1048
+ (token) => token.symbol === "USDC"
1049
+ )!;
1050
+
1051
+ for (let adapter of this.metadata.additionalInfo.adapters) {
1052
+ /**
1053
+ * Make sure that the position from supplyLend adapter doesnt recount the positon from multiply adapter
1054
+ */
1055
+ if (adapter.id !== `${VesuBorrowSupplyAdapter.name}_${wbtcToken.symbol}_${usdcToken.symbol}`) {
1056
+ const positions = await adapter.adapter.getPositions();
1057
+ allPositions.push(...positions);
1058
+ }
1059
+ }
1060
+ const assetPrice = await this.pricer.getPrice(this.asset().symbol);
1061
+ let netAUM = new Web3Number(0, this.asset().decimals);
1062
+ for (let position of allPositions) {
1063
+ if (position.tokenInfo.address.eq(this.asset().address)) {
1064
+ netAUM = netAUM.plus(position.amount);
1065
+ } else {
1066
+ netAUM = netAUM.plus(position.usdValue / assetPrice.price);
1067
+ }
1068
+ }
1069
+
1070
+ const prevAum = await this.getPrevAUM();
1071
+ const realAUM: PositionInfo = {
1072
+ tokenInfo: this.asset(),
1073
+ amount: netAUM,
1074
+ usdValue: netAUM.toNumber() * assetPrice.price,
1075
+ apy: { apy: netAUM.toNumber() * assetPrice.price, type: APYType.BASE },
1076
+ remarks: AUMTypes.FINALISED,
1077
+ protocol: Protocols.NONE // just placeholder
1078
+ };
1079
+
1080
+ const estimatedAUMDelta: PositionInfo = {
1081
+ tokenInfo: this.asset(),
1082
+ amount: Web3Number.fromWei('0', this.asset().decimals),
1083
+ usdValue: 0,
1084
+ apy: { apy: 0, type: APYType.BASE },
1085
+ remarks: AUMTypes.DEFISPRING,
1086
+ protocol: Protocols.NONE // just placeholder
1087
+ };
1088
+
1089
+ return {
1090
+ net: {
1091
+ tokenInfo: this.asset(),
1092
+ amount: netAUM,
1093
+ usdValue: netAUM.toNumber() * assetPrice.price
1094
+ }, prevAum: prevAum, splits: [realAUM, estimatedAUMDelta]
1095
+ };
1096
+ }
1097
+
1098
+ async getFundingRateHistory(startDate: Date, endDate: Date, page: number) {
1099
+ try {
1100
+ const extendedAdapter = await this.getExtendedAdapter();
1101
+ if (!extendedAdapter) {
1102
+ logger.error("extended adapter not found");
1103
+ return [];
1104
+ }
1105
+ const fundingRateHistory = await extendedAdapter.client.getFundingRates("BTC-USD", "LONG", {
1106
+ startTime: startDate.toISOString(),
1107
+ endTime: endDate.toISOString(),
1108
+ page: page
1109
+ });
1110
+ return fundingRateHistory;
1111
+ } catch (error) {
1112
+ console.error("error getting funding rate history", error);
1113
+ return [];
1114
+ }
1115
+ }
1116
+
1117
+ async getVesuExposureWithoutUserDepositedCollateral(): Promise<Web3Number> {
1118
+ try {
1119
+ const extendedAdapter = await this.getExtendedAdapter();
1120
+ const vesuAdapter = await this.getVesuAdapter();
1121
+ if (!extendedAdapter || !vesuAdapter) {
1122
+ logger.error("extended adapter or vesu adapter not found");
1123
+ return new Web3Number(0, USDC_TOKEN_DECIMALS);
1124
+ }
1125
+ const balance = await this.getUnusedBalance();
1126
+ if (!Number.isFinite(balance.usdValue) || balance.usdValue < 0) {
1127
+ logger.error(
1128
+ `Invalid balance.usdValue: ${balance.usdValue}. Expected a finite, non-negative number.`
1129
+ );
1130
+ return new Web3Number(0, USDC_TOKEN_DECIMALS);
1131
+ }
1132
+ const amountToInvest = balance.amount;
1133
+ const extendedPositon = await extendedAdapter.getAllOpenPositions();
1134
+ if (!extendedPositon) {
1135
+ logger.error("error getting extended position to decide move assets");
1136
+ return new Web3Number(0, USDC_TOKEN_DECIMALS)
1137
+ }
1138
+ const { collateralTokenAmount } =
1139
+ await vesuAdapter.vesuAdapter.getAssetPrices();
1140
+ const assetsUnderVaultAllocator = await this.getAUM();
1141
+ const assetsUnderVaultAllocatorInWBTC = new Web3Number(assetsUnderVaultAllocator.net.amount.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS);
1142
+ logger.info(`assetsUnderVaultAllocatorInWBTC: ${assetsUnderVaultAllocatorInWBTC}`);
1143
+ const addedCollateralNet = new Web3Number(amountToInvest.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS);
1144
+ logger.info(`addedCollateralNet: ${addedCollateralNet}`);
1145
+ const collateralExposureVesuInMultiplier = calculateVesuExposureExtraToDepositedCollateral(
1146
+ assetsUnderVaultAllocatorInWBTC,
1147
+ addedCollateralNet,
1148
+ collateralTokenAmount
1149
+ );
1150
+ if (collateralExposureVesuInMultiplier === null) {
1151
+ logger.error("error calculating collateral exposure vesu in multiplier");
1152
+ return new Web3Number(0, USDC_TOKEN_DECIMALS);
1153
+ }
1154
+ return collateralExposureVesuInMultiplier;
1155
+ } catch (err) {
1156
+ logger.error("error getting vesu exposure without user deposits", err);
1157
+ return new Web3Number(0, USDC_TOKEN_DECIMALS);
1158
+ }
1159
+ }
1160
+
1161
+ async getExtendedPositionExposure(): Promise<Web3Number> {
1162
+ try {
1163
+ const extendedAdapter = await this.getExtendedAdapter();
1164
+ if (!extendedAdapter) {
1165
+ logger.error("extended adapter not found");
1166
+ return new Web3Number(0, USDC_TOKEN_DECIMALS);
1167
+ }
1168
+ const extendedPositon = await extendedAdapter.getAllOpenPositions();
1169
+ if (!extendedPositon) {
1170
+ logger.error("error getting extended position to decide move assets");
1171
+ return new Web3Number(0, USDC_TOKEN_DECIMALS)
1172
+ }
1173
+ const extendedExposureWBTC = extendedPositon.length > 0 ? new Web3Number(extendedPositon[0].size || 0, WBTC_TOKEN_DECIMALS) : new Web3Number(0, WBTC_TOKEN_DECIMALS);
1174
+ return extendedExposureWBTC;
1175
+ } catch (err) {
1176
+ logger.error("error getting extended position exposure", err);
1177
+ return new Web3Number(0, USDC_TOKEN_DECIMALS)
1178
+ }
1179
+ }
1180
+
1181
+ async getBringLiquidityCall(params: { amount: Web3Number; }): Promise<Call> {
1182
+ const { amount } = params;
1183
+ const avnuAdapter = await this.getAvnuAdapter();
1184
+ if (!avnuAdapter) {
1185
+ logger.error("avnu adapter not found");
1186
+ return {} as Call;
1187
+ }
1188
+ const usdcToWbtcProofsInfo = avnuAdapter.getProofs(true, this.getMerkleTree());
1189
+ const callUsdcToWbtc = this.getManageCall(
1190
+ usdcToWbtcProofsInfo.proofs,
1191
+ await usdcToWbtcProofsInfo.callConstructor({ amount: amount })
1192
+ );
1193
+ return callUsdcToWbtc;
1194
+ }
1195
+ }
1196
+
1197
+
1198
+ function getLooperSettings(
1199
+ lstSymbol: string,
1200
+ underlyingSymbol: string,
1201
+ vaultSettings: BtcFiVesuExtendedStrategySettings,
1202
+ pool1: ContractAddr,
1203
+ extendedBackendUrl: string,
1204
+ extendedApiKey: string,
1205
+ vaultIdExtended: number,
1206
+ minimumExtendedMovementAmount: number,
1207
+ minimumVesuMovementAmount: number,
1208
+ minimumExtendedRetriesDelayForOrderStatus: number,
1209
+ minimumExtendedPriceDifferenceForSwapOpen: number,
1210
+ maximumExtendedPriceDifferenceForSwapClosing: number,
1211
+ ) {
1212
+ vaultSettings.leafAdapters = [];
1213
+
1214
+ const wbtcToken = Global.getDefaultTokens().find(
1215
+ (token) => token.symbol === underlyingSymbol
1216
+ )!;
1217
+ const usdcToken = Global.getDefaultTokens().find(
1218
+ (token) => token.symbol === lstSymbol
1219
+ )!;
1220
+
1221
+ const baseAdapterConfig: BaseAdapterConfig = {
1222
+ baseToken: wbtcToken,
1223
+ supportedPositions: [
1224
+ { asset: usdcToken, isDebt: true },
1225
+ { asset: wbtcToken, isDebt: false },
1226
+ ],
1227
+ //Since we open 2 positions, we need to add both positions, one is debt another is collateral
1228
+ networkConfig: getMainnetConfig(),
1229
+ pricer: new PricerFromApi(getMainnetConfig(), Global.getDefaultTokens()),
1230
+ vaultAllocator: vaultSettings.vaultAllocator,
1231
+ vaultAddress: vaultSettings.vaultAddress,
1232
+ };
1233
+
1234
+ const avnuAdapter = new AvnuAdapter({
1235
+ ...baseAdapterConfig,
1236
+ avnuContract: AVNU_MIDDLEWARE,
1237
+ slippage: 0.001,
1238
+ baseUrl: AVNU_QUOTE_URL,
1239
+ minimumExtendedPriceDifferenceForSwapOpen: minimumExtendedPriceDifferenceForSwapOpen,
1240
+ maximumExtendedPriceDifferenceForSwapClosing: maximumExtendedPriceDifferenceForSwapClosing,
1241
+ });
1242
+ const extendedAdapter = new ExtendedAdapter({
1243
+ ...baseAdapterConfig,
1244
+ supportedPositions: [
1245
+ { asset: usdcToken, isDebt: true },
1246
+ ],
1247
+ vaultIdExtended: vaultIdExtended,
1248
+ extendedContract: EXTENDED_CONTRACT,
1249
+ extendedBackendUrl: extendedBackendUrl,
1250
+ extendedApiKey: extendedApiKey,
1251
+ extendedTimeout: 30000,
1252
+ extendedRetries: 3,
1253
+ extendedBaseUrl: "https://api.starknet.extended.exchange",
1254
+ extendedMarketName: "BTC-USD",
1255
+ extendedPrecision: 5,
1256
+ avnuAdapter: avnuAdapter,
1257
+ minimumExtendedMovementAmount: minimumExtendedMovementAmount,
1258
+ retryDelayForOrderStatus: minimumExtendedRetriesDelayForOrderStatus,
1259
+ });
1260
+
1261
+ const vesuMultiplyAdapter = new VesuMultiplyAdapter({
1262
+ poolId: pool1,
1263
+ collateral: wbtcToken,
1264
+ debt: usdcToken,
1265
+ targetHealthFactor: vaultSettings.targetHealthFactor,
1266
+ minHealthFactor: vaultSettings.minHealthFactor,
1267
+ quoteAmountToFetchPrice: vaultSettings.quoteAmountToFetchPrice,
1268
+ ...baseAdapterConfig,
1269
+ supportedPositions: [
1270
+ { asset: wbtcToken, isDebt: false },
1271
+ { asset: usdcToken, isDebt: true },
1272
+ ],
1273
+ minimumVesuMovementAmount: minimumVesuMovementAmount,
1274
+ });
1275
+
1276
+ const vesuSupplyBorrowAdapter = new VesuBorrowSupplyAdapter({
1277
+ poolId: pool1,
1278
+ supplyToken: wbtcToken,
1279
+ borrowToken: usdcToken,
1280
+ targetHealthFactor: vaultSettings.targetHealthFactor,
1281
+ minHealthFactor: vaultSettings.minHealthFactor,
1282
+ quoteAmountToFetchPrice: vaultSettings.quoteAmountToFetchPrice,
1283
+ ...baseAdapterConfig,
1284
+ supportedPositions: [
1285
+ { asset: wbtcToken, isDebt: false },
1286
+ { asset: usdcToken, isDebt: true },
1287
+ ],
1288
+ vaultIdExtended: vaultIdExtended,
1289
+ extendedContract: EXTENDED_CONTRACT,
1290
+ extendedBackendUrl: extendedBackendUrl,
1291
+ extendedApiKey: extendedApiKey,
1292
+ extendedTimeout: 30000,
1293
+ extendedRetries: 3,
1294
+ extendedBaseUrl: "https://api.starknet.extended.exchange",
1295
+ extendedMarketName: "BTC-USD",
1296
+ extendedPrecision: 5,
1297
+ extendedAdapter: extendedAdapter,
1298
+ avnuAdapter: avnuAdapter,
1299
+ minimumExtendedMovementAmount: minimumExtendedMovementAmount,
1300
+ retryDelayForOrderStatus: minimumExtendedRetriesDelayForOrderStatus,
1301
+ })
1302
+
1303
+ const unusedBalanceAdapter = new UnusedBalanceAdapter({
1304
+ ...baseAdapterConfig,
1305
+ });
1306
+
1307
+ vaultSettings.adapters.push({
1308
+ id: `${vesuMultiplyAdapter.name}_${wbtcToken.symbol}_${usdcToken.symbol}`,
1309
+ adapter: vesuMultiplyAdapter,
1310
+ });
1311
+
1312
+ vaultSettings.adapters.push({
1313
+ id: `${vesuSupplyBorrowAdapter.name}_${wbtcToken.symbol}_${usdcToken.symbol}`,
1314
+ adapter: vesuSupplyBorrowAdapter,
1315
+ });
1316
+
1317
+ vaultSettings.adapters.push({
1318
+ id: `${unusedBalanceAdapter.name}_${wbtcToken.symbol}`,
1319
+ adapter: unusedBalanceAdapter,
1320
+ });
1321
+
1322
+ vaultSettings.adapters.push({
1323
+ id: `${extendedAdapter.name}_${wbtcToken.symbol}`,
1324
+ adapter: extendedAdapter,
1325
+ });
1326
+
1327
+ vaultSettings.adapters.push({
1328
+ id: `${avnuAdapter.name}_${wbtcToken.symbol}`,
1329
+ adapter: avnuAdapter,
1330
+ });
1331
+
1332
+ const commonAdapter = new CommonAdapter({
1333
+ id: UNIVERSAL_MANAGE_IDS.FLASH_LOAN,
1334
+ vaultAddress: vaultSettings.vaultAddress,
1335
+ vaultAllocator: vaultSettings.vaultAllocator,
1336
+ manager: vaultSettings.manager,
1337
+ asset: wbtcToken.address,
1338
+ });
1339
+
1340
+ vaultSettings.leafAdapters.push(() => vesuMultiplyAdapter.getDepositLeaf());
1341
+ vaultSettings.leafAdapters.push(() =>
1342
+ vesuMultiplyAdapter.getWithdrawLeaf()
1343
+ );
1344
+ vaultSettings.leafAdapters.push(() => vesuSupplyBorrowAdapter.getDepositLeaf());
1345
+ vaultSettings.leafAdapters.push(() => vesuSupplyBorrowAdapter.getBorrowLeaf());
1346
+ vaultSettings.leafAdapters.push(() => vesuSupplyBorrowAdapter.getWithdrawLeaf());
1347
+ vaultSettings.leafAdapters.push(() => vesuSupplyBorrowAdapter.getPayDebtLeaf());
1348
+ vaultSettings.leafAdapters.push(() => vesuSupplyBorrowAdapter.getMultiplyLeaf());
1349
+ vaultSettings.leafAdapters.push(() => extendedAdapter.getDepositLeaf());
1350
+ vaultSettings.leafAdapters.push(() => extendedAdapter.getSwapFromLegacyLeaf());
1351
+ vaultSettings.leafAdapters.push(() => avnuAdapter.getDepositLeaf());
1352
+ vaultSettings.leafAdapters.push(() => avnuAdapter.getWithdrawLeaf());
1353
+ // Doubt here, should this be usdcToken.address, or wbtcToken.address?
1354
+ vaultSettings.leafAdapters.push(
1355
+ commonAdapter
1356
+ .getApproveAdapter(
1357
+ wbtcToken.address,
1358
+ vaultSettings.vaultAddress,
1359
+ UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY
1360
+ )
1361
+ .bind(commonAdapter)
1362
+ );
1363
+
1364
+ vaultSettings.leafAdapters.push(
1365
+ commonAdapter
1366
+ .getBringLiquidityAdapter(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY)
1367
+ .bind(commonAdapter)
1368
+ );
1369
+ return vaultSettings;
1370
+ }
1371
+
1372
+ function getDescription(tokenSymbol: string, underlyingSymbol: string) {
1373
+ return VaultDescription(tokenSymbol, underlyingSymbol);
1374
+ }
1375
+
1376
+ export default function VaultDescription(
1377
+ lstSymbol: string,
1378
+ underlyingSymbol: string
1379
+ ) {
1380
+ const containerStyle = {
1381
+ maxWidth: "800px",
1382
+ margin: "0 auto",
1383
+ backgroundColor: "#111",
1384
+ color: "#eee",
1385
+ fontFamily: "Arial, sans-serif",
1386
+ borderRadius: "12px",
1387
+ };
1388
+
1389
+ return (
1390
+ <div style={containerStyle}>
1391
+ <h1 style={{ fontSize: "18px", marginBottom: "10px" }}>Liquidation risk managed leverged {lstSymbol} Vault</h1>
1392
+ <p style={{ fontSize: "14px", lineHeight: "1.5", marginBottom: "16px" }}>
1393
+ This Levered Endur {lstSymbol} vault is a tokenized leveraged Vault, auto-compounding strategy that takes upto 5x leverage on {lstSymbol} by borrow {underlyingSymbol}. Borrowed amount
1394
+ is swapped to {lstSymbol} to create leverage. Depositors receive vault shares that
1395
+ represent a proportional claim on the underlying assets and accrued yield.
1396
+ </p>
1397
+
1398
+ <p style={{ fontSize: "14px", lineHeight: "1.5", marginBottom: "16px" }}>
1399
+ This vault uses Vesu for lending and borrowing. The oracle used by this pool is a {highlightTextWithLinks("conversion rate oracle", [{ highlight: "conversion rate oracle", link: "https://docs.pragma.build/starknet/development#conversion-rate" }])}
1400
+ {" "}which is resilient to liquidity issues and price volatility, hence reducing the risk of liquidation. However, overtime, if left un-monitored, debt can increase enough to trigger a liquidation. But no worries, our continuous monitoring systems look for situations with reduced health factor and balance collateral/debt to bring it back to safe levels. With Troves, you can have a peaceful sleep.
1401
+ </p>
1402
+
1403
+ <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
1404
+ <p style={{ fontSize: "13px", color: "#ccc" }}>
1405
+ <strong>Withdrawals:</strong> Requests can take up to <strong>1-2 hours</strong> to process as the vault unwinds and settles routing.
1406
+ </p>
1407
+ </div>
1408
+ <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
1409
+ <p style={{ fontSize: "13px", color: "#ccc" }}>
1410
+ <strong>Debt limits:</strong> Pools on Vesu have debt caps that are gradually increased over time. Until caps are raised, deposited Tokens remain in the vault, generating a shared net return for all depositors. There is no additional fee taken by Troves on Yield token's APY, its only on added gain.
1411
+ </p>
1412
+ </div>
1413
+ {/* <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
1414
+ <p style={{ fontSize: "13px", color: "#ccc" }}>
1415
+ <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.
1416
+ </p>
1417
+ </div> */}
1418
+ </div>
1419
+ );
1420
+ }
1421
+
1422
+
1423
+ const re7UsdcPrimeDevansh: BtcFiVesuExtendedStrategySettings = {
1424
+ vaultAddress: ContractAddr.from("0x51c668b4c64f94d4dcfe9f5a9c6a8994fa520e5dfed966e7fdc8b42cbf2832"),
1425
+ manager: ContractAddr.from("0x4af6fb4245d7078c941cbbddf2f76cb201732317bd640c092370578e1799d76"),
1426
+ vaultAllocator: ContractAddr.from("0x74313caaf10f7bc60269be922f49b64390cf2540639dfb53a209220df2f29aa"),
1427
+ redeemRequestNFT: ContractAddr.from("0xa2b972b7edffed6960c74f3a57942fdf8d92180ccd076b523e167cb612b53c"),
1428
+ aumOracle: ContractAddr.from("0x6ff943c5ba828be6abf9d70e4d3275ff3d9d3e7aade9ec9e90898e4850a26f8"),
1429
+ leafAdapters: [],
1430
+ adapters: [],
1431
+ targetHealthFactor: 1.4,
1432
+ minHealthFactor: 1.05,
1433
+ underlyingToken: Global.getDefaultTokens().find(
1434
+ (token) => token.symbol === "WBTC"
1435
+ )!,
1436
+ quoteAmountToFetchPrice: new Web3Number(
1437
+ "100",
1438
+ Global.getDefaultTokens().find((token) => token.symbol === "USDC")!.decimals
1439
+ ),
1440
+ borrowable_assets: [Global.getDefaultTokens().find(token => token.symbol === "USDC")!],
1441
+ minimumWBTCDifferenceForAvnuSwap: MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP,
1442
+ }
1443
+
1444
+ export const BtcFiVesuExtendedTestStrategies = (extendedBackendUrl: string, extendedApiKey: string, vaultIdExtended: number, minimumExtendedMovementAmount: number, minimumVesuMovementAmount: number, minimumExtendedRetriesDelayForOrderStatus: number, minimumExtendedPriceDifferenceForSwapOpen: number, maximumExtendedPriceDifferenceForSwapClosing: number): IStrategyMetadata<BtcFiVesuExtendedStrategySettings>[] => {
1445
+ return [
1446
+ getStrategySettingsVesuExtendedBTCFi('USDC', 'WBTC', re7UsdcPrimeDevansh, false, false, extendedBackendUrl, extendedApiKey, vaultIdExtended, minimumExtendedMovementAmount, minimumVesuMovementAmount, minimumExtendedRetriesDelayForOrderStatus, minimumExtendedPriceDifferenceForSwapOpen, maximumExtendedPriceDifferenceForSwapClosing),
1447
+ ]
1448
+ }
1449
+
1450
+
1451
+ function getStrategySettingsVesuExtendedBTCFi(lstSymbol: string, underlyingSymbol: string, addresses: BtcFiVesuExtendedStrategySettings, isPreview: boolean = false, isLST: boolean, extendedBackendUrl: string, extendedApiKey: string, vaultIdExtended: number, minimumExtendedMovementAmount: number, minimumVesuMovementAmount: number, minimumExtendedRetriesDelayForOrderStatus: number, minimumExtendedPriceDifferenceForSwapOpen: number, maximumExtendedPriceDifferenceForSwapClosing: number): IStrategyMetadata<BtcFiVesuExtendedStrategySettings> {
1452
+ return {
1453
+ name: `Extended BTCfi ${underlyingSymbol}`,
1454
+ description: getDescription(lstSymbol, underlyingSymbol),
1455
+ address: addresses.vaultAddress,
1456
+ launchBlock: 0,
1457
+ type: 'Other',
1458
+ depositTokens: [Global.getDefaultTokens().find(token => token.symbol === underlyingSymbol)!],
1459
+ additionalInfo: getLooperSettings(lstSymbol, underlyingSymbol, addresses, VesuPools.Re7USDCCore, extendedBackendUrl, extendedApiKey, vaultIdExtended, minimumExtendedMovementAmount, minimumVesuMovementAmount, minimumExtendedRetriesDelayForOrderStatus, minimumExtendedPriceDifferenceForSwapOpen, maximumExtendedPriceDifferenceForSwapClosing),
1460
+ risk: {
1461
+ riskFactor: _riskFactor,
1462
+ netRisk:
1463
+ _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) /
1464
+ _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
1465
+ notARisks: getNoRiskTags(_riskFactor)
1466
+ },
1467
+ auditUrl: AUDIT_URL,
1468
+ protocols: [Protocols.EXTENDED, Protocols.VESU],
1469
+ maxTVL: Web3Number.fromWei(0, 18),
1470
+ contractDetails: getContractDetails(addresses),
1471
+ faqs: getFAQs(lstSymbol, underlyingSymbol, isLST),
1472
+ investmentSteps: getInvestmentSteps(lstSymbol, underlyingSymbol),
1473
+ isPreview: isPreview,
1474
+ apyMethodology: isLST ? 'Current annualized APY in terms of base asset of the LST. There is no additional fee taken by Troves on LST APY. We charge a 10% performance fee on the additional gain which is already accounted in the APY shown.' : 'Current annualized APY in terms of base asset of the Yield Token. There is no additional fee taken by Troves on yield token APY. We charge a 10% performance fee on the additional gain which is already accounted in the APY shown.'
1475
+ }
1476
+ }
1477
+
1478
+
1479
+
1480
+ // const oldContracts ={
1481
+ // vaultAddress: ContractAddr.from("0x5f9e7967b8ebbf0e19979974895a1e580d51328bcc7ae786c369aa584e5df62"),
1482
+ // manager: ContractAddr.from("0x12e2df5f7c30a2a2c852a77fa20873d21ee5a18855832693daba5b92284cb98"),
1483
+ // vaultAllocator: ContractAddr.from("0xdb3e372e39e94c7f30f9d6246ee3a71b701b5ad4e764d4c35f23b3035d221b"),
1484
+ // redeemRequestNFT: ContractAddr.from("0x48f83a32897a894c8ed3a1821dc2074e489d9cc1422600ac2fdf6861b0a85b3"),
1485
+ // aumOracle: ContractAddr.from("0x4833ebd242fed526f1603fdc98d8faf62d20cadf33297d81670c8f015dd0b0b"),
1486
+ // }