@strkfarm/sdk 2.0.0-dev.9 → 2.0.0-staging.2

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 (64) hide show
  1. package/dist/index.browser.global.js +111371 -93151
  2. package/dist/index.browser.mjs +27815 -32690
  3. package/dist/index.d.ts +1095 -2011
  4. package/dist/index.js +27425 -32309
  5. package/dist/index.mjs +27590 -32452
  6. package/package.json +6 -5
  7. package/src/data/ekubo-price-fethcer.abi.json +265 -0
  8. package/src/data/universal-vault.abi.json +20 -135
  9. package/src/dataTypes/address.ts +0 -7
  10. package/src/dataTypes/index.ts +3 -2
  11. package/src/dataTypes/mynumber.ts +141 -0
  12. package/src/global.ts +296 -288
  13. package/src/index.browser.ts +6 -5
  14. package/src/interfaces/common.tsx +324 -184
  15. package/src/modules/apollo-client-config.ts +28 -0
  16. package/src/modules/avnu.ts +4 -17
  17. package/src/modules/ekubo-pricer.ts +79 -0
  18. package/src/modules/ekubo-quoter.ts +11 -88
  19. package/src/modules/erc20.ts +21 -67
  20. package/src/modules/harvests.ts +26 -15
  21. package/src/modules/index.ts +11 -13
  22. package/src/modules/lst-apr.ts +0 -36
  23. package/src/modules/pragma.ts +23 -8
  24. package/src/modules/pricer-from-api.ts +150 -14
  25. package/src/modules/pricer.ts +2 -1
  26. package/src/modules/pricerBase.ts +2 -1
  27. package/src/node/deployer.ts +36 -1
  28. package/src/node/pricer-redis.ts +2 -1
  29. package/src/strategies/autoCompounderStrk.ts +1 -1
  30. package/src/strategies/base-strategy.ts +5 -22
  31. package/src/strategies/ekubo-cl-vault.tsx +2904 -2175
  32. package/src/strategies/factory.ts +165 -0
  33. package/src/strategies/index.ts +10 -11
  34. package/src/strategies/registry.ts +268 -0
  35. package/src/strategies/sensei.ts +416 -292
  36. package/src/strategies/universal-adapters/adapter-utils.ts +1 -5
  37. package/src/strategies/universal-adapters/baseAdapter.ts +153 -181
  38. package/src/strategies/universal-adapters/common-adapter.ts +77 -98
  39. package/src/strategies/universal-adapters/index.ts +1 -5
  40. package/src/strategies/universal-adapters/vesu-adapter.ts +218 -220
  41. package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +51 -58
  42. package/src/strategies/universal-lst-muliplier-strategy.tsx +1952 -992
  43. package/src/strategies/universal-strategy.tsx +1713 -1150
  44. package/src/strategies/vesu-rebalance.tsx +1189 -986
  45. package/src/utils/health-factor-math.ts +5 -11
  46. package/src/utils/index.ts +8 -9
  47. package/src/utils/strategy-utils.ts +57 -0
  48. package/src/data/extended-deposit.abi.json +0 -3613
  49. package/src/modules/ExtendedWrapperSDk/index.ts +0 -62
  50. package/src/modules/ExtendedWrapperSDk/types.ts +0 -311
  51. package/src/modules/ExtendedWrapperSDk/wrapper.ts +0 -395
  52. package/src/modules/midas.ts +0 -159
  53. package/src/modules/token-market-data.ts +0 -202
  54. package/src/strategies/svk-strategy.ts +0 -247
  55. package/src/strategies/universal-adapters/adapter-optimizer.ts +0 -65
  56. package/src/strategies/universal-adapters/avnu-adapter.ts +0 -413
  57. package/src/strategies/universal-adapters/extended-adapter.ts +0 -972
  58. package/src/strategies/universal-adapters/unused-balance-adapter.ts +0 -109
  59. package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +0 -1306
  60. package/src/strategies/vesu-extended-strategy/services/operationService.ts +0 -34
  61. package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +0 -77
  62. package/src/strategies/vesu-extended-strategy/utils/constants.ts +0 -49
  63. package/src/strategies/vesu-extended-strategy/utils/helper.ts +0 -370
  64. package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +0 -1379
@@ -1,1306 +0,0 @@
1
- import { ContractAddr, Web3Number } from "@/dataTypes";
2
- import { IConfig, Protocols, TokenInfo } from "@/interfaces";
3
- import { PricerBase } from "@/modules/pricerBase";
4
- import {
5
- BaseAdapter,
6
- BaseAdapterConfig,
7
- SupportedPosition,
8
- PositionInfo,
9
- PositionAPY,
10
- APYType,
11
- ManageCall,
12
- AdapterLeafType,
13
- GenerateCallFn,
14
- DepositParams,
15
- WithdrawParams,
16
- PositionAmount,
17
- } from "./baseAdapter";
18
- import {
19
- SIMPLE_SANITIZER,
20
- SIMPLE_SANITIZER_V2,
21
- toBigInt,
22
- VESU_SINGLETON,
23
- VESU_V2_MODIFY_POSITION_SANITIZER,
24
- } from "./adapter-utils";
25
- import { hash, uint256, Contract, CairoCustomEnum, num } from "starknet";
26
- import {
27
- VesuAdapter,
28
- VesuMultiplyCallParams,
29
- VesuModifyDelegationCallParams,
30
- getVesuSingletonAddress,
31
- VesuPools,
32
- Swap,
33
- IncreaseLeverParams,
34
- DecreaseLeverParams,
35
- } from "./vesu-adapter";
36
- import { logger } from "@/utils";
37
- import { WALLET_ADDRESS } from "../vesu-extended-strategy/utils/constants";
38
- import VesuMultiplyAbi from "@/data/vesu-multiple.abi.json";
39
- import VesuSingletonAbi from "../../data/vesu-singleton.abi.json";
40
- import VesuPoolV2Abi from "@/data/vesu-pool-v2.abi.json";
41
- import { EkuboQuoter, TokenMarketData } from "@/modules";
42
- import { calculateDebtReductionAmountForWithdrawal } from "../vesu-extended-strategy/utils/helper";
43
- import { HealthFactorMath } from "@/utils/health-factor-math";
44
- import { MAX_LIQUIDATION_RATIO } from "../vesu-extended-strategy/utils/constants";
45
-
46
- export interface VesuMultiplyAdapterConfig extends BaseAdapterConfig {
47
- poolId: ContractAddr;
48
- collateral: TokenInfo;
49
- debt: TokenInfo;
50
- targetHealthFactor: number;
51
- minHealthFactor: number;
52
- quoteAmountToFetchPrice: Web3Number;
53
- minimumVesuMovementAmount: number;
54
- }
55
-
56
- export class VesuMultiplyAdapter extends BaseAdapter<
57
- DepositParams,
58
- WithdrawParams
59
- > {
60
- readonly config: VesuMultiplyAdapterConfig;
61
- readonly vesuAdapter: VesuAdapter;
62
- readonly tokenMarketData: TokenMarketData;
63
- readonly minimumVesuMovementAmount: number;
64
- constructor(config: VesuMultiplyAdapterConfig) {
65
- super(config, VesuMultiplyAdapter.name, Protocols.VESU);
66
- this.config = config;
67
- this.vesuAdapter = new VesuAdapter({
68
- poolId: config.poolId,
69
- collateral: config.collateral,
70
- debt: config.debt,
71
- vaultAllocator: config.vaultAllocator,
72
- id: "",
73
- });
74
- this.minimumVesuMovementAmount = config.minimumVesuMovementAmount ?? 5; //5 usdc
75
- this.tokenMarketData = new TokenMarketData(
76
- this.config.pricer,
77
- this.config.networkConfig
78
- );
79
- }
80
-
81
- protected async getAPY(
82
- supportedPosition: SupportedPosition
83
- ): Promise<PositionAPY> {
84
- const CACHE_KEY = `apy_${this.config.poolId.address}_${supportedPosition.asset.symbol}`;
85
- const cacheData = this.getCache<PositionAPY>(CACHE_KEY);
86
- console.log(
87
- `${VesuMultiplyAdapter.name}::getAPY cacheData: ${JSON.stringify(
88
- cacheData
89
- )}`,
90
- this.vesuAdapter.config.poolId.shortString(),
91
- this.vesuAdapter.config.collateral.symbol,
92
- this.vesuAdapter.config.debt.symbol
93
- );
94
- if (cacheData) {
95
- return cacheData;
96
- }
97
- try {
98
- // Get Vesu pools to find APY for the asset
99
- const allVesuPools = await VesuAdapter.getVesuPools();
100
- const asset = supportedPosition.asset;
101
- const pool = allVesuPools.pools.find((p) =>
102
- this.vesuAdapter.config.poolId.eqString(num.getHexString(p.id))
103
- );
104
- if (!pool) {
105
- logger.warn(
106
- `VesuMultiplyAdapter: Pool not found for token ${asset.symbol}`
107
- );
108
- return {
109
- apy: 0,
110
- type: APYType.BASE,
111
- };
112
- }
113
- // Find the asset stats for our token
114
- const assetStats = pool.assets.find(
115
- (a: any) => a.symbol.toLowerCase() === asset.symbol.toLowerCase()
116
- )?.stats;
117
-
118
- if (!assetStats) {
119
- logger.warn(
120
- `VesuMultiplyAdapter: Asset stats not found for token ${asset.symbol}`
121
- );
122
- return {
123
- apy: 0,
124
- type: APYType.BASE,
125
- };
126
- }
127
- // Get appropriate APY based on position type
128
- let apy = 0;
129
- if (supportedPosition.isDebt) {
130
- // For debt positions, use borrow APY
131
- apy = Number(assetStats.borrowApr?.value || 0) / 1e18;
132
-
133
- // todo
134
- // Account for rewards on debt token
135
- } else {
136
- // For collateral positions, use supply APY
137
- const isAssetBTC = asset.symbol.toLowerCase().includes("btc");
138
- const baseAPY =
139
- Number(
140
- isAssetBTC
141
- ? assetStats.btcFiSupplyApr?.value + assetStats.supplyApy?.value
142
- : assetStats.supplyApy?.value || 0
143
- ) / 1e18;
144
-
145
- // account for reward yield (like STRK rewards)
146
- const rewardAPY =
147
- Number(assetStats.defiSpringSupplyApr?.value || "0") / 1e18;
148
-
149
- // account for base yield of LST
150
- const isSupported = this.tokenMarketData.isAPYSupported(asset);
151
- apy = baseAPY + rewardAPY;
152
- if (isSupported) {
153
- const tokenAPY = await this.tokenMarketData.getAPY(asset);
154
- apy += tokenAPY;
155
- }
156
- }
157
- const result = {
158
- apy,
159
- type: supportedPosition.isDebt ? APYType.BASE : APYType.BASE,
160
- };
161
-
162
- this.setCache(CACHE_KEY, result, 300000); // Cache for 5 minutes
163
- return result;
164
- } catch (error) {
165
- logger.error(
166
- `VesuMultiplyAdapter: Error getting APY for ${supportedPosition.asset.symbol}:`,
167
- error
168
- );
169
- // return {
170
- // apy: 0,
171
- // type: APYType.BASE
172
- // };
173
- throw error;
174
- }
175
- }
176
-
177
- protected async getPosition(
178
- supportedPosition: SupportedPosition
179
- ): Promise<PositionAmount> {
180
- const CACHE_KEY = `position_${this.config.poolId.address}_${supportedPosition.asset.symbol}`;
181
- const cacheData = this.getCache<PositionAmount>(CACHE_KEY);
182
- if (cacheData) {
183
- return cacheData;
184
- }
185
-
186
- try {
187
- // Use VesuAdapter to get positions
188
- this.vesuAdapter.networkConfig = this.config.networkConfig;
189
- this.vesuAdapter.pricer = this.config.pricer;
190
-
191
- const positions = await this.vesuAdapter.getPositions(
192
- this.config.networkConfig
193
- );
194
-
195
- // Find the position for our asset
196
- let position = positions.find((p) =>
197
- p.token.address.eq(supportedPosition.asset.address)
198
- );
199
-
200
- if (!position) {
201
- logger.warn(
202
- `VesuMultiplyAdapter: Position not found for token ${supportedPosition.asset.symbol}`
203
- );
204
- return {
205
- amount: new Web3Number("0", supportedPosition.asset.decimals),
206
- remarks: "Position not found",
207
- };
208
- }
209
-
210
- if (supportedPosition.isDebt) {
211
- position.amount = position.amount.multipliedBy(-1);
212
- position.usdValue = position.usdValue * -1;
213
- }
214
-
215
- this.setCache(CACHE_KEY, position, 60000); // Cache for 1 minute
216
- return position;
217
- } catch (error) {
218
- logger.error(
219
- `VesuMultiplyAdapter: Error getting position for ${supportedPosition.asset.symbol}:`,
220
- error
221
- );
222
- // return new Web3Number('0', supportedPosition.asset.decimals);
223
- throw error;
224
- }
225
- }
226
-
227
- async maxBorrowableAPY(): Promise<number> {
228
- // get collateral APY
229
- const collateralAPY = await this.getAPY({
230
- asset: this.config.collateral,
231
- isDebt: false,
232
- });
233
- const apy = collateralAPY.apy * 0.8;
234
- return apy;
235
- }
236
-
237
- async maxDeposit(amount?: Web3Number): Promise<PositionInfo> {
238
- const collateral = this.config.collateral;
239
- const debt = this.config.debt;
240
-
241
- try {
242
- // Get current positions
243
- this.vesuAdapter.networkConfig = this.config.networkConfig;
244
- this.vesuAdapter.pricer = this.config.pricer;
245
-
246
- const positions = await this.vesuAdapter.getPositions(
247
- this.config.networkConfig
248
- );
249
- const collateralPosition = positions.find((p) =>
250
- p.token.address.eq(collateral.address)
251
- );
252
- const debtPosition = positions.find((p) =>
253
- p.token.address.eq(debt.address)
254
- );
255
-
256
- if (!collateralPosition || !debtPosition) {
257
- throw new Error("Could not find current positions");
258
- }
259
-
260
- // Calculate max borrowable amount
261
- const maxBorrowableAPY = await this.maxBorrowableAPY();
262
- const maxBorrowable =
263
- await this.vesuAdapter.getMaxBorrowableByInterestRate(
264
- this.config.networkConfig,
265
- debt,
266
- maxBorrowableAPY
267
- );
268
- logger.verbose(
269
- `VesuMultiplyAdapter: Max borrowable: ${maxBorrowable.toNumber()}`
270
- );
271
- const debtCap = await this.vesuAdapter.getDebtCap(
272
- this.config.networkConfig
273
- );
274
- logger.verbose(`VesuMultiplyAdapter: Debt cap: ${debtCap.toNumber()}`);
275
- const actualMaxBorrowable = maxBorrowable.minimum(debtCap);
276
- logger.verbose(
277
- `VesuMultiplyAdapter: Actual max borrowable: ${actualMaxBorrowable.toNumber()}`
278
- );
279
-
280
- // Calculate max collateral that can be deposited based on LTV
281
- const maxLTV = await this.vesuAdapter.getLTVConfig(
282
- this.config.networkConfig
283
- );
284
- const collateralPrice = await this.config.pricer.getPrice(
285
- collateral.symbol
286
- );
287
- if (collateralPrice.price === 0) {
288
- throw new Error("Collateral price is 0");
289
- }
290
- const debtPrice = await this.config.pricer.getPrice(debt.symbol);
291
- if (debtPrice.price === 0) {
292
- throw new Error("Debt price is 0");
293
- }
294
- const maxCollateralFromDebt =
295
- HealthFactorMath.getMinCollateralRequiredOnLooping(
296
- actualMaxBorrowable,
297
- debtPrice.price,
298
- this.config.targetHealthFactor,
299
- maxLTV,
300
- collateralPrice.price,
301
- collateral
302
- );
303
-
304
- const maxDepositAmount = amount
305
- ? amount.minimum(maxCollateralFromDebt)
306
- : maxCollateralFromDebt;
307
- const usdValue = await this.getUSDValue(collateral, maxDepositAmount);
308
- logger.verbose(
309
- `VesuMultiplyAdapter: Max deposit::USD value: ${usdValue}, amount: ${maxDepositAmount.toNumber()}`
310
- );
311
- const apys = await Promise.all([
312
- this.getAPY({ asset: collateral, isDebt: false }),
313
- this.getAPY({ asset: debt, isDebt: true }),
314
- ]);
315
- logger.verbose(
316
- `VesuMultiplyAdapter: Apys: ${apys[0].apy}, ${apys[1].apy}`
317
- );
318
-
319
- const borrowAmountUSD = actualMaxBorrowable.multipliedBy(debtPrice.price);
320
- logger.verbose(
321
- `VesuMultiplyAdapter: Borrow amount: ${actualMaxBorrowable.toNumber()}, borrow amount USD: ${borrowAmountUSD.toNumber()}`
322
- );
323
- const netCollateralUSD = usdValue + borrowAmountUSD.toNumber();
324
- const netAPY =
325
- (apys[0].apy * netCollateralUSD +
326
- apys[1].apy * borrowAmountUSD.toNumber()) /
327
- usdValue;
328
- logger.verbose(
329
- `VesuMultiplyAdapter: Max deposit amount: ${maxDepositAmount.toNumber()}, netAPY: ${netAPY}`
330
- );
331
- return {
332
- tokenInfo: collateral,
333
- amount: maxDepositAmount,
334
- usdValue,
335
- remarks: "Max deposit based on available debt capacity",
336
- apy: {
337
- apy: netAPY,
338
- type: APYType.BASE,
339
- },
340
- protocol: this.protocol,
341
- };
342
- } catch (error) {
343
- logger.error(
344
- `VesuMultiplyAdapter: Error calculating max deposit:`,
345
- error
346
- );
347
- throw error;
348
- }
349
- }
350
-
351
- async maxWithdraw(): Promise<PositionInfo> {
352
- const collateral = this.config.collateral;
353
- const debt = this.config.debt;
354
-
355
- try {
356
- // Calculate how much can be withdrawn without affecting health factor too much
357
- this.vesuAdapter.networkConfig = this.config.networkConfig;
358
- this.vesuAdapter.pricer = this.config.pricer;
359
-
360
- const positions = await this.vesuAdapter.getPositions(
361
- this.config.networkConfig
362
- );
363
- const collateralPosition = positions.find((p) =>
364
- p.token.address.eq(collateral.address)
365
- );
366
- const debtPosition = positions.find((p) =>
367
- p.token.address.eq(this.config.debt.address)
368
- );
369
-
370
- if (!collateralPosition || !debtPosition) {
371
- throw new Error("Could not find current positions");
372
- }
373
-
374
- // Calculate max withdrawable (conservative approach)
375
- const collateralPrice =
376
- collateralPosition.usdValue / collateralPosition.amount.toNumber();
377
- const debtInCollateral = debtPosition.usdValue / collateralPrice;
378
- const maxWithdrawable = collateralPosition.amount.minus(debtInCollateral);
379
-
380
- const result = maxWithdrawable.greaterThan(0)
381
- ? maxWithdrawable
382
- : new Web3Number("0", collateral.decimals);
383
- const usdValue = await this.getUSDValue(collateral, result);
384
- const debtUSD = debtPosition.usdValue;
385
- logger.verbose(
386
- `VesuMultiplyAdapter: Debt USD: ${debtUSD}, collateral USD: ${usdValue}`
387
- );
388
- const apys = await Promise.all([
389
- this.getAPY({ asset: collateral, isDebt: false }),
390
- this.getAPY({ asset: debt, isDebt: true }),
391
- ]);
392
- logger.verbose(
393
- `VesuMultiplyAdapter: Apys: ${apys[0].apy}, ${apys[1].apy}`
394
- );
395
- const netAPY =
396
- usdValue - debtUSD > 0
397
- ? (apys[0].apy * usdValue + apys[1].apy * debtUSD) /
398
- (usdValue - debtUSD)
399
- : 0;
400
- logger.verbose(
401
- `VesuMultiplyAdapter: Max withdraw amount: ${result.toNumber()}, netAPY: ${netAPY}`
402
- );
403
- return {
404
- tokenInfo: collateral,
405
- amount: result,
406
- usdValue,
407
- remarks: "Max withdraw based on health factor",
408
- apy: {
409
- apy: netAPY,
410
- type: APYType.BASE,
411
- },
412
- protocol: this.protocol,
413
- };
414
- } catch (error) {
415
- logger.error(
416
- `VesuMultiplyAdapter: Error calculating max withdraw:`,
417
- error
418
- );
419
- throw error;
420
- }
421
- }
422
-
423
- protected _getDepositLeaf(): {
424
- target: ContractAddr;
425
- method: string;
426
- packedArguments: bigint[];
427
- sanitizer: ContractAddr;
428
- id: string;
429
- }[] {
430
- const collateral = this.config.collateral;
431
- const debt = this.config.debt;
432
- const { addr: vesuSingleton, isV2 } = getVesuSingletonAddress(
433
- this.config.poolId
434
- );
435
- const vesuMultiply = isV2
436
- ? this.vesuAdapter.VESU_MULTIPLY
437
- : this.vesuAdapter.VESU_MULTIPLY_V1;
438
-
439
- return [
440
- // Approval step for collateral
441
- {
442
- target: collateral.address,
443
- method: "approve",
444
- packedArguments: [
445
- vesuMultiply.toBigInt(), // spender
446
- ],
447
- sanitizer: SIMPLE_SANITIZER,
448
- // amc = approve multiply collateral
449
- id: `amc_${this.config.poolId.shortString()}_${collateral.symbol}_${
450
- debt.symbol
451
- }`,
452
- },
453
- // Switch delegation on
454
- {
455
- target: vesuSingleton,
456
- method: "modify_delegation",
457
- packedArguments: isV2
458
- ? [
459
- vesuMultiply.toBigInt(), // delegatee
460
- ]
461
- : [
462
- this.config.poolId.toBigInt(),
463
- vesuMultiply.toBigInt(), // delegatee
464
- ],
465
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
466
- // sd1 = switch delegation on
467
- id: `sd1_${this.config.poolId.shortString()}_${collateral.symbol}_${
468
- debt.symbol
469
- }`,
470
- },
471
- // Vesu multiply call
472
- {
473
- target: vesuMultiply,
474
- method: "modify_lever",
475
- packedArguments: [
476
- this.config.poolId.toBigInt(),
477
- collateral.address.toBigInt(),
478
- this.config.debt.address.toBigInt(),
479
- this.config.vaultAllocator.toBigInt(),
480
- ],
481
- sanitizer: SIMPLE_SANITIZER_V2,
482
- // vm = vesu multiply
483
- id: `vm_${this.config.poolId.shortString()}_${collateral.symbol}_${
484
- debt.symbol
485
- }`,
486
- },
487
- // Switch delegation off
488
- {
489
- target: vesuSingleton,
490
- method: "modify_delegation",
491
- packedArguments: isV2
492
- ? [
493
- vesuMultiply.toBigInt(), // delegatee
494
- ]
495
- : [
496
- this.config.poolId.toBigInt(),
497
- vesuMultiply.toBigInt(), // delegatee
498
- ],
499
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
500
- // sd2 = switch delegation off
501
- id: `sd2_${this.config.poolId.shortString()}_${collateral.symbol}_${
502
- debt.symbol
503
- }`,
504
- },
505
- ];
506
- }
507
-
508
- protected _getWithdrawLeaf(): {
509
- target: ContractAddr;
510
- method: string;
511
- packedArguments: bigint[];
512
- sanitizer: ContractAddr;
513
- id: string;
514
- }[] {
515
- const { addr: vesuSingleton, isV2 } = getVesuSingletonAddress(
516
- this.config.poolId
517
- );
518
- const vesuMultiply = isV2
519
- ? this.vesuAdapter.VESU_MULTIPLY
520
- : this.vesuAdapter.VESU_MULTIPLY_V1;
521
- const collateral = this.config.collateral;
522
- const debt = this.config.debt;
523
-
524
- return [
525
- // Switch delegation on
526
- {
527
- target: vesuSingleton,
528
- method: "modify_delegation",
529
- packedArguments: isV2
530
- ? [
531
- vesuMultiply.toBigInt(), // delegatee
532
- ]
533
- : [
534
- this.config.poolId.toBigInt(),
535
- vesuMultiply.toBigInt(), // delegatee
536
- ],
537
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
538
- // sdow = switch delegation on withdraw
539
- id: `sdow_${this.config.poolId.shortString()}_${collateral.symbol}_${
540
- debt.symbol
541
- }`,
542
- },
543
- // Vesu multiply call
544
- {
545
- target: vesuMultiply,
546
- method: "modify_lever",
547
- packedArguments: [
548
- this.config.poolId.toBigInt(),
549
- this.config.collateral.address.toBigInt(),
550
- this.config.debt.address.toBigInt(),
551
- this.config.vaultAllocator.toBigInt(),
552
- ],
553
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
554
- // vmw = vesu multiply withdraw
555
- id: `vmw_${this.config.poolId.shortString()}_${collateral.symbol}_${
556
- debt.symbol
557
- }`,
558
- },
559
- // Switch delegation off
560
- {
561
- target: vesuSingleton,
562
- method: "modify_delegation",
563
- packedArguments: isV2
564
- ? [
565
- vesuMultiply.toBigInt(), // delegatee
566
- ]
567
- : [
568
- this.config.poolId.toBigInt(),
569
- vesuMultiply.toBigInt(), // delegatee
570
- ],
571
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
572
- // sdofw = switch delegation off withdraw
573
- id: `sdofw_${this.config.poolId.shortString()}_${collateral.symbol}_${
574
- debt.symbol
575
- }`,
576
- },
577
- ];
578
- }
579
-
580
- getDepositAdapter(): AdapterLeafType<DepositParams> {
581
- const leafConfigs = this._getDepositLeaf();
582
- const leaves = leafConfigs.map((config) => {
583
- const { target, method, packedArguments, sanitizer, id } = config;
584
- const leaf = this.constructSimpleLeafData(
585
- {
586
- id: id,
587
- target,
588
- method,
589
- packedArguments,
590
- },
591
- sanitizer
592
- );
593
- return leaf;
594
- });
595
- return {
596
- leaves,
597
- callConstructor: this.getDepositCall.bind(
598
- this
599
- ) as unknown as GenerateCallFn<DepositParams>,
600
- };
601
- }
602
-
603
- getWithdrawAdapter(): AdapterLeafType<WithdrawParams> {
604
- const leafConfigs = this._getWithdrawLeaf();
605
- const leaves = leafConfigs.map((config) => {
606
- const { target, method, packedArguments, sanitizer, id } = config;
607
- const leaf = this.constructSimpleLeafData(
608
- {
609
- id: id,
610
- target,
611
- method,
612
- packedArguments,
613
- },
614
- sanitizer
615
- );
616
- return leaf;
617
- });
618
- return {
619
- leaves,
620
- callConstructor: this.getWithdrawCall.bind(
621
- this
622
- ) as unknown as GenerateCallFn<WithdrawParams>,
623
- };
624
- }
625
-
626
- async getDepositCall(params: DepositParams): Promise<ManageCall[]> {
627
- const collateral = this.config.collateral;
628
- const { addr: vesuSingleton, isV2 } = getVesuSingletonAddress(
629
- this.config.poolId
630
- );
631
- const vesuMultiply = isV2
632
- ? this.vesuAdapter.VESU_MULTIPLY
633
- : this.vesuAdapter.VESU_MULTIPLY_V1;
634
-
635
- const uint256MarginAmount = uint256.bnToUint256(params.amount.toWei());
636
-
637
- return [
638
- // Approval call
639
- {
640
- sanitizer: SIMPLE_SANITIZER,
641
- call: {
642
- contractAddress: collateral.address,
643
- selector: hash.getSelectorFromName("approve"),
644
- calldata: [
645
- vesuMultiply.toBigInt(), // spender
646
- toBigInt(uint256MarginAmount.low.toString()), // amount low
647
- toBigInt(uint256MarginAmount.high.toString()), // amount high
648
- ],
649
- },
650
- },
651
- // Switch delegation on
652
- {
653
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
654
- call: {
655
- contractAddress: vesuSingleton,
656
- selector: hash.getSelectorFromName("modify_delegation"),
657
- calldata: isV2
658
- ? [
659
- vesuMultiply.toBigInt(), // delegatee
660
- BigInt(1), // delegation: true
661
- ]
662
- : [
663
- this.config.poolId.toBigInt(),
664
- vesuMultiply.toBigInt(), // delegatee
665
- BigInt(1), // delegation: true
666
- ],
667
- },
668
- },
669
- // Vesu multiply call
670
- {
671
- sanitizer: SIMPLE_SANITIZER_V2,
672
- call: {
673
- contractAddress: vesuMultiply,
674
- selector: hash.getSelectorFromName("modify_lever"),
675
- calldata: await this.getMultiplyCallCalldata(params, true),
676
- },
677
- },
678
- // Switch delegation off
679
- {
680
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
681
- call: {
682
- contractAddress: vesuSingleton,
683
- selector: hash.getSelectorFromName("modify_delegation"),
684
- calldata: isV2
685
- ? [
686
- vesuMultiply.toBigInt(), // delegatee
687
- BigInt(0), // delegation: false
688
- ]
689
- : [
690
- this.config.poolId.toBigInt(),
691
- vesuMultiply.toBigInt(), // delegatee
692
- BigInt(0), // delegation: false
693
- ],
694
- },
695
- },
696
- ];
697
- }
698
-
699
- async getWithdrawCall(params: WithdrawParams): Promise<ManageCall[]> {
700
- const { addr: vesuSingleton, isV2 } = getVesuSingletonAddress(
701
- this.config.poolId
702
- );
703
- const vesuMultiply = isV2
704
- ? this.vesuAdapter.VESU_MULTIPLY
705
- : this.vesuAdapter.VESU_MULTIPLY_V1;
706
-
707
- return [
708
- // Switch delegation on
709
- {
710
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
711
- call: {
712
- contractAddress: vesuSingleton,
713
- selector: hash.getSelectorFromName("modify_delegation"),
714
- calldata: isV2
715
- ? [
716
- vesuMultiply.toBigInt(), // delegatee
717
- BigInt(1), // delegation: true
718
- ]
719
- : [
720
- this.config.poolId.toBigInt(),
721
- vesuMultiply.toBigInt(), // delegatee
722
- BigInt(1), // delegation: true
723
- ],
724
- },
725
- },
726
- // Vesu multiply call
727
- {
728
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
729
- call: {
730
- contractAddress: vesuMultiply,
731
- selector: hash.getSelectorFromName("modify_lever"),
732
- calldata: await this.getWithdrawalCalldata(params),
733
- },
734
- },
735
- // Switch delegation off
736
- {
737
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
738
- call: {
739
- contractAddress: vesuSingleton,
740
- selector: hash.getSelectorFromName("modify_delegation"),
741
- calldata: isV2
742
- ? [
743
- vesuMultiply.toBigInt(), // delegatee
744
- BigInt(0), // delegation: false
745
- ]
746
- : [
747
- this.config.poolId.toBigInt(),
748
- vesuMultiply.toBigInt(), // delegatee
749
- BigInt(0), // delegation: false
750
- ],
751
- },
752
- },
753
- ];
754
- }
755
-
756
- private async getMultiplyCallCalldata(
757
- params: DepositParams | WithdrawParams,
758
- isDeposit: boolean
759
- ): Promise<bigint[]> {
760
- logger.verbose(
761
- `${
762
- VesuMultiplyAdapter.name
763
- }::getMultiplyCallCalldata params: ${JSON.stringify(
764
- params
765
- )}, isDeposit: ${isDeposit}, collateral: ${
766
- this.config.collateral.symbol
767
- }, debt: ${this.config.debt.symbol}`
768
- );
769
- const { isV2 } = getVesuSingletonAddress(this.config.poolId);
770
- const vesuMultiply = isV2
771
- ? this.vesuAdapter.VESU_MULTIPLY
772
- : this.vesuAdapter.VESU_MULTIPLY_V1;
773
-
774
- // Create a temporary contract instance to populate the call
775
- const multiplyContract = new Contract({
776
- abi: VesuMultiplyAbi,
777
- address: vesuMultiply.address,
778
- providerOrAccount: this.config.networkConfig.provider,
779
- });
780
-
781
- // Configure swaps based on the operation
782
- let leverSwap: Swap[] = [];
783
- let leverSwapLimitAmount = Web3Number.fromWei(0, this.config.debt.decimals);
784
-
785
- const existingPositions = await this.vesuAdapter.getPositions(
786
- this.config.networkConfig
787
- );
788
- const collateralisation = await this.vesuAdapter.getCollateralization(
789
- this.config.networkConfig
790
- );
791
- const existingCollateralInfo = existingPositions[0];
792
- const existingDebtInfo = existingPositions[1];
793
- const isDexPriceRequired = existingDebtInfo.token.symbol !== "USDC";
794
- logger.debug(`${
795
- VesuMultiplyAdapter.name
796
- }::getVesuMultiplyCall existingCollateralInfo: ${JSON.stringify(
797
- existingCollateralInfo
798
- )},
799
- existingDebtInfo: ${JSON.stringify(
800
- existingDebtInfo
801
- )}, collateralisation: ${JSON.stringify(collateralisation)}`);
802
-
803
- // - Prices as seen by Vesu contracts, ideal for HF math
804
- // Price 1 is ok as fallback bcz that would relatively price the
805
- // collateral and debt as equal.
806
- const collateralPrice =
807
- collateralisation[0].usdValue > 0
808
- ? collateralisation[0].usdValue /
809
- existingCollateralInfo.amount.toNumber()
810
- : (await this.config.pricer.getPrice(this.config.collateral.symbol))
811
- .price;
812
- const debtPrice =
813
- collateralisation[1].usdValue > 0
814
- ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber()
815
- : (await this.config.pricer.getPrice(this.config.debt.symbol)).price;
816
- logger.debug(
817
- `${VesuMultiplyAdapter.name}::getVesuMultiplyCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`
818
- );
819
-
820
- const legLTV = await this.vesuAdapter.getLTVConfig(
821
- this.config.networkConfig
822
- );
823
- const ekuboQuoter = new EkuboQuoter(
824
- this.config.networkConfig,
825
- this.config.pricer
826
- );
827
- const dexPrice = isDexPriceRequired
828
- ? await ekuboQuoter.getDexPrice(
829
- this.config.collateral,
830
- this.config.debt,
831
- this.config.quoteAmountToFetchPrice
832
- )
833
- : 1;
834
- logger.verbose(
835
- `${VesuMultiplyAdapter.name}::getVesuMultiplyCall dexPrice: ${dexPrice}, ltv: ${legLTV}`
836
- );
837
-
838
- // compute optimal amount of collateral and debt post addition/removal
839
- // target hf = collateral * collateralPrice * ltv / debt * debtPrice
840
- // assuming X to be the usd amount of debt borrowed or repaied (negative).
841
- // target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv / (debt * debtPrice + X)
842
- // => X * target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv - (debt * debtPrice * target hf)
843
- // => X * (target hf - ltv)= ((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)
844
- // => X = (((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)) / (target hf - ltv)
845
-
846
- const addedCollateral = params.amount.multipliedBy(isDeposit ? 1 : -1);
847
- logger.verbose(
848
- `${VesuMultiplyAdapter.name}::getVesuMultiplyCall addedCollateral: ${addedCollateral}`
849
- );
850
- const numeratorPart1 = existingCollateralInfo.amount
851
- .plus(addedCollateral)
852
- .multipliedBy(collateralPrice)
853
- .multipliedBy(legLTV);
854
- logger.verbose(
855
- `${VesuMultiplyAdapter.name}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}`
856
- );
857
- const numeratorPart2 = existingDebtInfo.amount
858
- .multipliedBy(debtPrice)
859
- .multipliedBy(this.config.targetHealthFactor);
860
- logger.verbose(
861
- `${VesuMultiplyAdapter.name}::getVesuMultiplyCall numeratorPart2: ${numeratorPart2}`
862
- );
863
- const denominatorPart = this.config.targetHealthFactor - legLTV / dexPrice; // TODO Write reason for this. this dexPrice is some custom thing. this dexPrice is probably exchange rate (1 xWBTC in WBTC terms)
864
- logger.verbose(
865
- `${VesuMultiplyAdapter.name}::getVesuMultiplyCall denominatorPart: ${denominatorPart}`
866
- );
867
- const x_debt_usd = numeratorPart1
868
- .minus(numeratorPart2)
869
- .dividedBy(denominatorPart);
870
- logger.verbose(
871
- `${VesuMultiplyAdapter.name}::getVesuMultiplyCall x_debt_usd: ${x_debt_usd}`
872
- );
873
- logger.debug(
874
- `${VesuMultiplyAdapter.name}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}, numeratorPart2: ${numeratorPart2}, denominatorPart: ${denominatorPart}`
875
- );
876
-
877
- // both in underlying
878
- // debtAmount in debt units
879
- let debtAmount = new Web3Number(
880
- x_debt_usd.dividedBy(debtPrice).toFixed(this.config.debt.decimals),
881
- this.config.debt.decimals
882
- );
883
- const marginAmount = addedCollateral;
884
- const collateralToken = this.config.collateral;
885
- const debtToken = this.config.debt;
886
- const debtAmountInCollateralUnits = new Web3Number(
887
- debtAmount
888
- .multipliedBy(debtPrice)
889
- .dividedBy(collateralPrice)
890
- .multipliedBy(10 ** collateralToken.decimals)
891
- .toFixed(0),
892
- collateralToken.decimals
893
- );
894
-
895
- // increase multiply lever or not
896
- const isIncrease = debtAmount.greaterThanOrEqualTo(0);
897
-
898
- // due to directional limitations in multiply contract
899
- if (isIncrease && debtAmount.lessThan(0)) {
900
- // we are increasing lever but math says reduce debt
901
- // - this is ok
902
- } else if (!isIncrease && debtAmount.greaterThan(0)) {
903
- // we are decreasing level but math says increase debt
904
- // - such actions must be done with zero margin amount
905
- // - so just set debt 0
906
- debtAmount = Web3Number.fromWei(0, this.config.debt.decimals);
907
- }
908
- logger.verbose(
909
- `${VesuMultiplyAdapter.name}::getVesuMultiplyCall debtAmount: ${debtAmount}, marginAmount: ${marginAmount}`
910
- );
911
- if (!debtAmount.isZero()) {
912
- // Get swap quote for leverage operation
913
- // Determine swap direction based on operation type
914
-
915
- try {
916
- const swapQuote = await ekuboQuoter.getQuote(
917
- collateralToken.address.address,
918
- debtToken.address.address,
919
- debtAmountInCollateralUnits.multipliedBy(-1) // negative for exact amount out
920
- );
921
-
922
- // todo add better slip checks
923
- // Check price impact
924
- if (swapQuote.price_impact < 0.01) {
925
- // 1% max price impact
926
- // from and toToken param position reversed, to fetch the required quote and keep things generalised
927
-
928
- leverSwap = debtAmount.isNegative()
929
- ? ekuboQuoter.getVesuMultiplyQuote(
930
- swapQuote,
931
- collateralToken,
932
- debtToken
933
- )
934
- : ekuboQuoter.getVesuMultiplyQuote(
935
- swapQuote,
936
- debtToken,
937
- collateralToken
938
- );
939
- //console.log("leverSwap", leverSwap[-1].token_amount);
940
- //console.log(JSON.stringify(leverSwap));
941
- // Calculate limit amount with slippage protection
942
- const MAX_SLIPPAGE = 0.002; // 0.2% slippage
943
- if (debtAmount.greaterThan(0)) {
944
- // For increase: minimum amount of collateral received
945
- // from debt token to collateral token
946
- //console.log("debtAmountInCollateralUnits", debtAmountInCollateralUnits.toNumber());
947
- //leverSwapLimitAmount = await ekuboQuoter.getSwapLimitAmount(debtToken, collateralToken, debtAmount, MAX_SLIPPAGE);
948
- leverSwapLimitAmount = debtAmount.multipliedBy(1 + MAX_SLIPPAGE);
949
- //console.log("anotherleverSwapLimitAmount", anotherleverSwapLimitAmount, leverSwapLimitAmount);
950
- } else if (debtAmount.lessThan(0)) {
951
- // For decrease: maximum amount of collateral used
952
- // from collateral token to debt token
953
- //leverSwapLimitAmount = await ekuboQuoter.getSwapLimitAmount(collateralToken, debtToken, debtAmountInCollateralUnits.multipliedBy(-1), MAX_SLIPPAGE);
954
- leverSwapLimitAmount = debtAmount
955
- .abs()
956
- .multipliedBy(1 - MAX_SLIPPAGE);
957
- //console.log("anotherleverSwapLimitAmount", anotherleverSwapLimitAmount, leverSwapLimitAmount);
958
- } else {
959
- leverSwapLimitAmount = Web3Number.fromWei(
960
- 0,
961
- this.config.debt.decimals
962
- );
963
- }
964
- await new Promise((resolve) => setTimeout(resolve, 10000));
965
- //console.log("leverSwapLimitAmount", leverSwapLimitAmount);
966
- } else {
967
- throw new Error(
968
- `VesuMultiplyAdapter: Price impact too high (${swapQuote.price_impact}), skipping swap`
969
- );
970
- }
971
- } catch (error) {
972
- throw new Error(
973
- `VesuMultiplyAdapter: Failed to get swap quote: ${error}`
974
- );
975
- }
976
- }
977
-
978
- const multiplyParams = await this.getLeverParams(
979
- isIncrease,
980
- params,
981
- leverSwap,
982
- leverSwapLimitAmount
983
- );
984
- const call = multiplyContract.populate("modify_lever", {
985
- modify_lever_params: this.formatMultiplyParams(
986
- isIncrease,
987
- multiplyParams
988
- ),
989
- });
990
-
991
- return call.calldata as bigint[];
992
- }
993
-
994
- private async getLeverParams(
995
- isIncrease: boolean,
996
- params: DepositParams | WithdrawParams,
997
- leverSwap: Swap[],
998
- leverSwapLimitAmount: Web3Number
999
- ): Promise<IncreaseLeverParams | DecreaseLeverParams> {
1000
- const multiplyParams: IncreaseLeverParams | DecreaseLeverParams = isIncrease
1001
- ? {
1002
- user: this.config.vaultAllocator,
1003
- pool_id: this.config.poolId,
1004
- collateral_asset: this.config.collateral.address,
1005
- debt_asset: this.config.debt.address,
1006
- recipient: this.config.vaultAllocator,
1007
- add_margin: params.amount, // multiplied by collateral decimals in format
1008
- margin_swap: [],
1009
- margin_swap_limit_amount: Web3Number.fromWei(
1010
- 0,
1011
- this.config.collateral.decimals
1012
- ),
1013
- lever_swap: leverSwap,
1014
- lever_swap_limit_amount: leverSwapLimitAmount,
1015
- }
1016
- : {
1017
- user: this.config.vaultAllocator,
1018
- pool_id: this.config.poolId,
1019
- collateral_asset: this.config.collateral.address,
1020
- debt_asset: this.config.debt.address,
1021
- recipient: this.config.vaultAllocator,
1022
- sub_margin: params.amount,
1023
- lever_swap: leverSwap,
1024
- lever_swap_limit_amount: leverSwapLimitAmount,
1025
- lever_swap_weights: [],
1026
- withdraw_swap: [],
1027
- withdraw_swap_limit_amount: Web3Number.fromWei(
1028
- 0,
1029
- this.config.collateral.decimals
1030
- ),
1031
- withdraw_swap_weights: [],
1032
- close_position: false,
1033
- };
1034
- return multiplyParams;
1035
- }
1036
-
1037
- private async getWithdrawalCalldata(
1038
- params: WithdrawParams
1039
- ): Promise<bigint[]> {
1040
- //params.amount must be in btc here
1041
- const { isV2 } = getVesuSingletonAddress(this.config.poolId);
1042
- const vesuMultiply = isV2
1043
- ? this.vesuAdapter.VESU_MULTIPLY
1044
- : this.vesuAdapter.VESU_MULTIPLY_V1;
1045
- const multiplyContract = new Contract({
1046
- abi: VesuMultiplyAbi,
1047
- address: vesuMultiply.address,
1048
- providerOrAccount: this.config.networkConfig.provider,
1049
- });
1050
- let leverSwap: Swap[] = [];
1051
- let leverSwapLimitAmount = Web3Number.fromWei(0, this.config.debt.decimals);
1052
- const existingPositions = await this.vesuAdapter.getPositions(
1053
- this.config.networkConfig
1054
- );
1055
- const existingCollateralInfo = existingPositions[0];
1056
- const existingDebtInfo = existingPositions[1];
1057
- const collateralToken = this.config.collateral;
1058
- const debtToken = this.config.debt;
1059
- const collateralPrice = await this.config.pricer.getPrice(
1060
- collateralToken.symbol
1061
- );
1062
- const debtPrice = await this.config.pricer.getPrice(debtToken.symbol);
1063
- // the debt amount is negative as we are reducing debt to withdraw
1064
- const { deltadebtAmountUnits: debtAmountToRepay } =
1065
- calculateDebtReductionAmountForWithdrawal(
1066
- existingDebtInfo.amount,
1067
- existingCollateralInfo.amount,
1068
- MAX_LIQUIDATION_RATIO,
1069
- params.amount,
1070
- collateralPrice.price,
1071
- debtPrice.price,
1072
- debtToken.decimals
1073
- );
1074
- //console.log("debtAmountToRepay", debtAmountToRepay);
1075
- if (!debtAmountToRepay) {
1076
- throw new Error("error calculating debt amount to repay");
1077
- }
1078
- const ekuboQuoter = new EkuboQuoter(
1079
- this.config.networkConfig,
1080
- this.config.pricer
1081
- );
1082
- const debtInDebtUnits = new Web3Number(
1083
- debtAmountToRepay,
1084
- debtToken.decimals
1085
- )
1086
- .dividedBy(debtPrice.price)
1087
- .multipliedBy(10 ** debtToken.decimals);
1088
- const swapQuote = await ekuboQuoter.getQuote(
1089
- debtToken.address.address,
1090
- collateralToken.address.address,
1091
- debtInDebtUnits
1092
- );
1093
- const MAX_SLIPPAGE = 0.002; // 0.2% slippag
1094
- if (swapQuote.price_impact < 0.0025) {
1095
- leverSwap = ekuboQuoter.getVesuMultiplyQuote(
1096
- swapQuote,
1097
- collateralToken,
1098
- debtToken
1099
- );
1100
- } else {
1101
- logger.error(
1102
- `VesuMultiplyAdapter: Price impact too high (${swapQuote.price_impact}), skipping swap`
1103
- );
1104
- }
1105
-
1106
- leverSwapLimitAmount = new Web3Number(debtAmountToRepay, debtToken.decimals)
1107
- .abs()
1108
- .multipliedBy(1 + MAX_SLIPPAGE);
1109
- //leverSwapLimitAmount = await ekuboQuoter.getSwapLimitAmount(debtToken, collateralToken, debtInCollateralUnits, MAX_SLIPPAGE);
1110
- const multiplyParams = await this.getLeverParams(
1111
- false,
1112
- params,
1113
- leverSwap,
1114
- leverSwapLimitAmount
1115
- );
1116
- const call = multiplyContract.populate("modify_lever", {
1117
- modify_lever_params: this.formatMultiplyParams(false, multiplyParams),
1118
- });
1119
- return call.calldata as bigint[];
1120
- }
1121
-
1122
- formatMultiplyParams(
1123
- isIncrease: boolean,
1124
- params: IncreaseLeverParams | DecreaseLeverParams
1125
- ) {
1126
- if (isIncrease) {
1127
- const _params = params as IncreaseLeverParams;
1128
- return {
1129
- action: new CairoCustomEnum({
1130
- IncreaseLever: {
1131
- pool_id: _params.pool_id.toBigInt(),
1132
- collateral_asset: _params.collateral_asset.toBigInt(),
1133
- debt_asset: _params.debt_asset.toBigInt(),
1134
- user: _params.user.toBigInt(),
1135
- add_margin: BigInt(_params.add_margin.toWei()),
1136
- margin_swap: _params.margin_swap.map((swap) => ({
1137
- route: swap.route.map((route) => ({
1138
- pool_key: {
1139
- token0: route.pool_key.token0.toBigInt(),
1140
- token1: route.pool_key.token1.toBigInt(),
1141
- fee: route.pool_key.fee,
1142
- tick_spacing: route.pool_key.tick_spacing,
1143
- extension: BigInt(
1144
- num.hexToDecimalString(route.pool_key.extension)
1145
- ),
1146
- },
1147
- sqrt_ratio_limit: uint256.bnToUint256(
1148
- route.sqrt_ratio_limit.toWei()
1149
- ),
1150
- skip_ahead: BigInt(100),
1151
- })),
1152
- token_amount: {
1153
- token: swap.token_amount.token.toBigInt(),
1154
- amount: swap.token_amount.amount.toI129(),
1155
- },
1156
- })),
1157
- margin_swap_limit_amount: BigInt(
1158
- _params.margin_swap_limit_amount.toWei()
1159
- ),
1160
- lever_swap: _params.lever_swap.map((swap) => ({
1161
- route: swap.route.map((route) => ({
1162
- pool_key: {
1163
- token0: route.pool_key.token0.toBigInt(),
1164
- token1: route.pool_key.token1.toBigInt(),
1165
- fee: route.pool_key.fee,
1166
- tick_spacing: route.pool_key.tick_spacing,
1167
- extension: BigInt(
1168
- num.hexToDecimalString(route.pool_key.extension)
1169
- ),
1170
- },
1171
- sqrt_ratio_limit: uint256.bnToUint256(
1172
- route.sqrt_ratio_limit.toWei()
1173
- ),
1174
- skip_ahead: BigInt(0),
1175
- })),
1176
- token_amount: {
1177
- token: swap.token_amount.token.toBigInt(),
1178
- amount: swap.token_amount.amount.toI129(),
1179
- },
1180
- })),
1181
- lever_swap_limit_amount: BigInt(
1182
- _params.lever_swap_limit_amount.toWei()
1183
- ),
1184
- },
1185
- }),
1186
- };
1187
- }
1188
-
1189
- const _params = params as DecreaseLeverParams;
1190
- return {
1191
- action: new CairoCustomEnum({
1192
- DecreaseLever: {
1193
- pool_id: _params.pool_id.toBigInt(),
1194
- collateral_asset: _params.collateral_asset.toBigInt(),
1195
- debt_asset: _params.debt_asset.toBigInt(),
1196
- user: _params.user.toBigInt(),
1197
- sub_margin: BigInt(_params.sub_margin.toWei()),
1198
- recipient: _params.recipient.toBigInt(),
1199
- lever_swap: _params.lever_swap.map((swap) => ({
1200
- route: swap.route.map((route) => ({
1201
- pool_key: {
1202
- token0: route.pool_key.token0.toBigInt(),
1203
- token1: route.pool_key.token1.toBigInt(),
1204
- fee: route.pool_key.fee,
1205
- tick_spacing: route.pool_key.tick_spacing,
1206
- extension: ContractAddr.from(
1207
- route.pool_key.extension
1208
- ).toBigInt(),
1209
- },
1210
- sqrt_ratio_limit: uint256.bnToUint256(
1211
- route.sqrt_ratio_limit.toWei()
1212
- ),
1213
- skip_ahead: BigInt(route.skip_ahead.toWei()),
1214
- })),
1215
- token_amount: {
1216
- token: swap.token_amount.token.toBigInt(),
1217
- amount: swap.token_amount.amount.toI129(),
1218
- },
1219
- })),
1220
- lever_swap_limit_amount: BigInt(
1221
- _params.lever_swap_limit_amount.toWei()
1222
- ),
1223
- lever_swap_weights: _params.lever_swap_weights.map((weight) =>
1224
- BigInt(weight.toWei())
1225
- ),
1226
- withdraw_swap: _params.withdraw_swap.map((swap) => ({
1227
- route: swap.route.map((route) => ({
1228
- pool_key: {
1229
- token0: route.pool_key.token0.toBigInt(),
1230
- token1: route.pool_key.token1.toBigInt(),
1231
- fee: route.pool_key.fee,
1232
- tick_spacing: route.pool_key.tick_spacing,
1233
- extension: ContractAddr.from(
1234
- route.pool_key.extension
1235
- ).toBigInt(),
1236
- },
1237
- sqrt_ratio_limit: uint256.bnToUint256(
1238
- route.sqrt_ratio_limit.toWei()
1239
- ),
1240
- skip_ahead: BigInt(route.skip_ahead.toWei()),
1241
- })),
1242
- token_amount: {
1243
- token: swap.token_amount.token.toBigInt(),
1244
- amount: swap.token_amount.amount.toI129(),
1245
- },
1246
- })),
1247
- withdraw_swap_limit_amount: BigInt(
1248
- _params.withdraw_swap_limit_amount.toWei()
1249
- ),
1250
- withdraw_swap_weights: _params.withdraw_swap_weights.map((weight) =>
1251
- BigInt(weight.toWei())
1252
- ),
1253
- close_position: _params.close_position,
1254
- },
1255
- }),
1256
- };
1257
- }
1258
-
1259
- async getHealthFactor(): Promise<number> {
1260
- const healthFactor = await this.vesuAdapter.getHealthFactor();
1261
- return healthFactor;
1262
- }
1263
-
1264
- async getNetAPY(): Promise<number> {
1265
- const positions = await this.getPositions();
1266
- logger.verbose(
1267
- `${this.name}::getNetAPY: positions: ${JSON.stringify(positions)}`
1268
- );
1269
- const allZero = positions.every((p) => p.usdValue === 0);
1270
-
1271
- // in case of zero positions, apy will come zero/NaN
1272
- // bcz of net 0 zero weights
1273
- if (allZero) {
1274
- // use approx dummy usd values to compute netAPY
1275
- const collateralUSD = 1000;
1276
- const maxLTV = await this.vesuAdapter.getLTVConfig(
1277
- this.config.networkConfig
1278
- );
1279
- const targetHF = this.config.targetHealthFactor;
1280
- const maxDebt = HealthFactorMath.getMaxDebtAmountOnLooping(
1281
- new Web3Number(collateralUSD, this.config.collateral.decimals),
1282
- 1, // assume price 1 for simplicity
1283
- maxLTV,
1284
- targetHF,
1285
- 1, // assume price 1 for simplicity
1286
- this.config.debt
1287
- );
1288
-
1289
- // debt is also added to collateral bcz, we assume debt is swapped to collateral
1290
- const debtUSD = maxDebt.multipliedBy(1); // assume price 1 for simplicity
1291
- const netAPY =
1292
- (positions[0].apy.apy * (collateralUSD + debtUSD.toNumber()) +
1293
- positions[1].apy.apy * debtUSD.toNumber()) /
1294
- collateralUSD;
1295
- return netAPY;
1296
- }
1297
-
1298
- // Return true APY
1299
- const netAmount = positions.reduce((acc, curr) => acc + curr.usdValue, 0);
1300
- const netAPY =
1301
- positions.reduce((acc, curr) => acc + curr.apy.apy * curr.usdValue, 0) /
1302
- netAmount;
1303
- logger.verbose(`${this.name}::getNetAPY: netAPY: ${netAPY}`);
1304
- return netAPY;
1305
- }
1306
- }