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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/cli.js +2 -2
  2. package/dist/cli.mjs +2 -2
  3. package/dist/index.browser.global.js +19596 -28786
  4. package/dist/index.browser.mjs +8559 -17931
  5. package/dist/index.d.ts +578 -2746
  6. package/dist/index.js +8649 -18059
  7. package/dist/index.mjs +8577 -17949
  8. package/package.json +3 -3
  9. package/src/data/universal-vault.abi.json +8 -7
  10. package/src/dataTypes/bignumber.browser.ts +5 -1
  11. package/src/dataTypes/bignumber.node.ts +5 -0
  12. package/src/global.ts +21 -1
  13. package/src/interfaces/common.tsx +39 -4
  14. package/src/modules/avnu.ts +19 -10
  15. package/src/modules/index.ts +1 -1
  16. package/src/strategies/base-strategy.ts +92 -8
  17. package/src/strategies/constants.ts +8 -3
  18. package/src/strategies/ekubo-cl-vault.tsx +150 -16
  19. package/src/strategies/factory.ts +21 -1
  20. package/src/strategies/index.ts +2 -7
  21. package/src/strategies/registry.ts +28 -5
  22. package/src/strategies/sensei.ts +29 -13
  23. package/src/strategies/svk-strategy.ts +26 -2
  24. package/src/strategies/token-boosted-xstrk-carry-strategy.tsx +1223 -0
  25. package/src/strategies/universal-adapters/avnu-adapter.ts +16 -8
  26. package/src/strategies/universal-adapters/index.ts +1 -2
  27. package/src/strategies/universal-adapters/svk-troves-adapter.ts +19 -6
  28. package/src/strategies/universal-adapters/vesu-modify-position-adapter.ts +22 -3
  29. package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +75 -52
  30. package/src/strategies/universal-adapters/vesu-position-common.ts +38 -31
  31. package/src/strategies/universal-lst-muliplier-strategy.tsx +222 -269
  32. package/src/strategies/universal-strategy.tsx +166 -105
  33. package/src/strategies/vesu-rebalance.tsx +3 -6
  34. package/src/strategies/yoloVault.ts +1084 -0
  35. package/src/utils/health-factor-math.ts +29 -0
  36. package/src/modules/ExtendedWrapperSDk/index.ts +0 -62
  37. package/src/modules/ExtendedWrapperSDk/types.ts +0 -334
  38. package/src/modules/ExtendedWrapperSDk/wrapper.ts +0 -611
  39. package/src/strategies/universal-adapters/extended-adapter.ts +0 -860
  40. package/src/strategies/universal-adapters/usdc<>usdce-adapter.ts +0 -200
  41. package/src/strategies/usdc-boosted-strategy.tsx +0 -693
  42. package/src/strategies/vesu-extended-strategy/services/executionService.ts +0 -2234
  43. package/src/strategies/vesu-extended-strategy/services/extended-vesu-state-manager.ts +0 -4254
  44. package/src/strategies/vesu-extended-strategy/services/ltv-imbalance-rebalance-math.ts +0 -783
  45. package/src/strategies/vesu-extended-strategy/services/operationService.ts +0 -56
  46. package/src/strategies/vesu-extended-strategy/types/transaction-metadata.ts +0 -88
  47. package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +0 -78
  48. package/src/strategies/vesu-extended-strategy/utils/constants.ts +0 -48
  49. package/src/strategies/vesu-extended-strategy/utils/helper.ts +0 -528
  50. package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +0 -1014
@@ -7,7 +7,6 @@ import {
7
7
  } from "./baseAdapter";
8
8
  import { toBigInt } from "./adapter-utils";
9
9
  import { Protocols } from "@/interfaces";
10
- import { MAX_DELAY } from "../vesu-extended-strategy/utils/constants";
11
10
  import { SupportedPosition } from "./baseAdapter";
12
11
  import { PositionAPY, APYType, PositionAmount } from "./baseAdapter";
13
12
  import { Web3Number } from "@/dataTypes";
@@ -15,17 +14,25 @@ import { PositionInfo } from "./baseAdapter";
15
14
  import { ManageCall } from "./baseAdapter";
16
15
  import { ContractAddr } from "@/dataTypes";
17
16
  import { AVNU_EXCHANGE } from "./adapter-utils";
18
- import { MAX_RETRIES } from "../vesu-extended-strategy/utils/constants";
19
17
  import { Quote } from "@avnu/avnu-sdk";
20
18
  import { hash, uint256 } from "starknet";
21
19
  import { AvnuWrapper } from "@/modules/avnu";
22
20
  import axios from "axios";
23
21
  import { SIMPLE_SANITIZER } from "./adapter-utils";
24
- import { returnFormattedAmount } from "../vesu-extended-strategy/utils/helper";
25
22
  import { assert, logger } from "@/utils";
26
23
  import { Global } from "@/global";
27
24
  import { TokenInfo } from "@/interfaces";
28
25
  import { ERC20 } from "@/modules";
26
+ import { MAX_AVNU_RETRY_DELAY } from "../constants";
27
+
28
+ export interface AvnuDepositParams extends DepositParams {
29
+ minAmount?: Web3Number;
30
+ }
31
+
32
+ export interface AvnuWithdrawParams extends WithdrawParams {
33
+ minAmount?: Web3Number;
34
+ }
35
+
29
36
  export interface AvnuAdapterConfig extends BaseAdapterConfig {
30
37
  baseUrl: string;
31
38
  avnuContract: ContractAddr; //0x04270219d365d6b017231b52e92b3fb5d7c8378b05e9abc97724537a80e93b0f
@@ -34,7 +41,7 @@ export interface AvnuAdapterConfig extends BaseAdapterConfig {
34
41
  maximumExtendedPriceDifferenceForSwapClosing: number;
35
42
  }
36
43
 
37
- export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
44
+ export class AvnuAdapter extends BaseAdapter<AvnuDepositParams, AvnuWithdrawParams> {
38
45
  readonly config: AvnuAdapterConfig;
39
46
  protected avnuWrapper: AvnuWrapper;
40
47
  lastSwapPriceInfo: SwapPriceInfo | null = null;
@@ -91,7 +98,7 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
91
98
  }
92
99
 
93
100
  private async buildSwapCalls(
94
- params: DepositParams | WithdrawParams,
101
+ params: AvnuDepositParams | AvnuWithdrawParams,
95
102
  fromToken: TokenInfo,
96
103
  toToken: TokenInfo,
97
104
  // NOTE : this is a direction flag that we are swapping in this direction
@@ -129,6 +136,7 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
129
136
  const getCalldata = await this.avnuWrapper.getSwapCallData(
130
137
  quote,
131
138
  vaultAllocator.address,
139
+ params.minAmount,
132
140
  );
133
141
  const swapCallData = getCalldata[0];
134
142
  const approveAmount = uint256.bnToUint256(params.amount.toWei());
@@ -260,7 +268,7 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
260
268
  return [];
261
269
  }
262
270
 
263
- async getDepositCall(params: DepositParams): Promise<ManageCall[]> {
271
+ async getDepositCall(params: AvnuDepositParams): Promise<ManageCall[]> {
264
272
  try {
265
273
  const fromToken = this.config.supportedPositions[0].asset; //usdc
266
274
  const toToken = this.config.supportedPositions[1].asset; //wbtc
@@ -278,7 +286,7 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
278
286
  }
279
287
 
280
288
  //Swap wbtc to usdc
281
- async getWithdrawCall(params: WithdrawParams): Promise<ManageCall[]> {
289
+ async getWithdrawCall(params: AvnuWithdrawParams): Promise<ManageCall[]> {
282
290
  try {
283
291
  const toToken = this.config.supportedPositions[0].asset;
284
292
  const fromToken = this.config.supportedPositions[1].asset;
@@ -322,7 +330,7 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
322
330
  if (attempt === retries - 1) {
323
331
  throw err;
324
332
  }
325
- await new Promise((resolve) => setTimeout(resolve, MAX_DELAY));
333
+ await new Promise((resolve) => setTimeout(resolve, MAX_AVNU_RETRY_DELAY));
326
334
  }
327
335
  }
328
336
  throw new Error("Failed to fetch quote after retries");
@@ -4,8 +4,7 @@ export * from "./vesu-adapter";
4
4
  export * from "./vesu-supply-only-adapter";
5
5
  export * from "./vesu-multiply-adapter";
6
6
  export * from "./vesu-modify-position-adapter";
7
- export * from "./extended-adapter";
8
7
  export * from "./adapter-utils";
9
8
  export * from "./token-transfer-adapter";
10
9
  export * from "./avnu-adapter";
11
- export * from "./svk-troves-adapter";
10
+ export * from "./svk-troves-adapter";
@@ -41,6 +41,11 @@ export interface SvkTrovesAdapterConfig extends BaseAdapterConfig {
41
41
  positionOwner?: ContractAddr;
42
42
  /** Optional APY endpoint (defaults to {@link DEFAULT_TROVES_STRATEGIES_API}). */
43
43
  trovesStrategiesApiUrl?: string;
44
+ /**
45
+ * Optional redeem request NFT contract address. When provided, pending assets are calculated
46
+ * using `getPendingAssetsFromOwnerNFTMethod` instead of `due_assets_from_owner`.
47
+ */
48
+ redeemRequestNFT?: ContractAddr;
44
49
  }
45
50
 
46
51
  type TrovesStrategiesApiPayload = {
@@ -165,13 +170,21 @@ export class SvkTrovesAdapter extends BaseAdapter<DepositParams, WithdrawParams>
165
170
  const liquid = Web3Number.fromWei(liquidAssetsRaw.toString(), decimals);
166
171
 
167
172
  let pending = Web3Number.fromWei("0", decimals);
168
- try {
169
- const dueRaw: any = await vault.call("due_assets_from_owner", [owner.address]);
170
- pending = Web3Number.fromWei(dueRaw.toString(), decimals);
171
- } catch (e) {
172
- logger.warn(
173
- `${SvkTrovesAdapter.name}::getPosition: due_assets_from_owner failed (treating as 0): ${(e as Error).message}`,
173
+ if (this.config.redeemRequestNFT) {
174
+ pending = await this.getPendingAssetsFromOwnerNFTMethod(
175
+ this.config.redeemRequestNFT,
176
+ owner,
177
+ decimals,
174
178
  );
179
+ } else {
180
+ try {
181
+ const dueRaw: any = await vault.call("due_assets_from_owner", [owner.address]);
182
+ pending = Web3Number.fromWei(dueRaw.toString(), decimals);
183
+ } catch (e) {
184
+ logger.warn(
185
+ `${SvkTrovesAdapter.name}::getPosition: due_assets_from_owner failed (treating as 0): ${(e as Error).message}`,
186
+ );
187
+ }
175
188
  }
176
189
 
177
190
  const total = liquid.plus(pending);
@@ -1,6 +1,7 @@
1
1
  import { ContractAddr, Web3Number } from "@/dataTypes";
2
2
  import { IProtocol, Protocols, TokenInfo } from "@/interfaces";
3
3
  import {
4
+ APYType,
4
5
  BaseAdapter,
5
6
  BaseAdapterConfig,
6
7
  ManageCall,
@@ -86,7 +87,10 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
86
87
 
87
88
  private _getTargetHealthFactor(): number {
88
89
  if (this.config.targetLtv <= 0) {
89
- return 1.0;
90
+ throw new Error(`Target LTV is less than or equal to 0: ${this.config.targetLtv}`);
91
+ }
92
+ if (this.config.targetLtv >= this._getEffectiveMaxLtv(this.config.maxLtv)) {
93
+ throw new Error(`Target LTV is greater than or equal to max LTV: ${this.config.targetLtv} >= ${this._getEffectiveMaxLtv(this.config.maxLtv)}`);
90
94
  }
91
95
  return this.config.maxLtv / this.config.targetLtv;
92
96
  }
@@ -330,11 +334,26 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
330
334
  }
331
335
 
332
336
  async maxDeposit(amount?: Web3Number): Promise<PositionInfo> {
333
- return getVesuCommonMaxDeposit(this._getPositionCommonContext(), amount);
337
+ const { collateralPosition, debtPosition: debtPosition } = await getVesuCommonMaxDeposit(this._getPositionCommonContext(), amount);
338
+ // since this is not multiply adapter, max allow collateral is directly max allowed deposit
339
+ const netAPY =
340
+ (collateralPosition.apy.apy * collateralPosition.usdValue - debtPosition.apy.apy * debtPosition.usdValue) /
341
+ (collateralPosition.usdValue - debtPosition.usdValue);
342
+ return {
343
+ ...collateralPosition,
344
+ apy: { apy: netAPY, type: APYType.BASE },
345
+ };
334
346
  }
335
347
 
336
348
  async maxWithdraw(): Promise<PositionInfo> {
337
- return getVesuCommonMaxWithdraw(this._getPositionCommonContext());
349
+ const { collateralPosition, debtPosition } = await getVesuCommonMaxWithdraw(this._getPositionCommonContext());
350
+ const netAPY =
351
+ (collateralPosition.apy.apy * collateralPosition.usdValue - debtPosition.apy.apy * debtPosition.usdValue) /
352
+ (collateralPosition.usdValue - debtPosition.usdValue);
353
+ return {
354
+ ...collateralPosition,
355
+ apy: { apy: netAPY, type: APYType.BASE },
356
+ };
338
357
  }
339
358
 
340
359
  protected _getDepositLeaf(): {
@@ -16,6 +16,7 @@ import {
16
16
  import {
17
17
  SIMPLE_SANITIZER,
18
18
  SIMPLE_SANITIZER_V2,
19
+ SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
19
20
  toBigInt,
20
21
  } from "./adapter-utils";
21
22
  import { hash, uint256, Contract, CairoCustomEnum, num } from "starknet";
@@ -29,9 +30,7 @@ import {
29
30
  import { assert, logger } from "@/utils";
30
31
  import VesuMultiplyAbi from "@/data/vesu-multiple.abi.json";
31
32
  import { EkuboQuote, EkuboQuoter, TokenMarketData } from "@/modules";
32
- import { calculateDebtReductionAmountForWithdrawal } from "../vesu-extended-strategy/utils/helper";
33
33
  import { HealthFactorMath } from "@/utils/health-factor-math";
34
- import { MAX_LIQUIDATION_RATIO } from "../vesu-extended-strategy/utils/constants";
35
34
  import {
36
35
  VesuPositionCommonContext,
37
36
  getVesuCommonAPY,
@@ -139,7 +138,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
139
138
 
140
139
  const delegationOn: ManageCall = {
141
140
  proofReadableId: proofReadableIds.delegationOn,
142
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
141
+ sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
143
142
  call: {
144
143
  contractAddress: vesuSingleton,
145
144
  selector: hash.getSelectorFromName("modify_delegation"),
@@ -151,7 +150,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
151
150
 
152
151
  const modifyLever: ManageCall = {
153
152
  proofReadableId: proofReadableIds.modifyLever,
154
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
153
+ sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
155
154
  call: {
156
155
  contractAddress: vesuMultiply,
157
156
  selector: hash.getSelectorFromName("modify_lever"),
@@ -161,7 +160,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
161
160
 
162
161
  const delegationOff: ManageCall = {
163
162
  proofReadableId: proofReadableIds.delegationOff,
164
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
163
+ sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
165
164
  call: {
166
165
  contractAddress: vesuSingleton,
167
166
  selector: hash.getSelectorFromName("modify_delegation"),
@@ -178,7 +177,8 @@ export class VesuMultiplyAdapter extends BaseAdapter<
178
177
  const collateral = this.config.collateral;
179
178
  const debt = this.config.debt;
180
179
  return {
181
- approve: `amc_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
180
+ approve_collateral: `amc_${this.config.poolId.shortString()}_${collateral.symbol}`,
181
+ approve_margin: `amc_${this.config.poolId.shortString()}_${this.config.marginToken.symbol}`,
182
182
  delegationOn: `sd1_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
183
183
  modifyLever: `vm_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
184
184
  delegationOff: `sd2_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
@@ -321,7 +321,13 @@ export class VesuMultiplyAdapter extends BaseAdapter<
321
321
  const numeratorPart2 = existingDebt
322
322
  .multipliedBy(debtPrice)
323
323
  .multipliedBy(this.config.targetHealthFactor);
324
+ if (dexPrice === 0) {
325
+ throw new Error("_computeTargetDebtDelta::dexPrice is 0");
326
+ }
324
327
  const denominatorPart = this.config.targetHealthFactor - ltv / dexPrice;
328
+ if (denominatorPart === 0) {
329
+ throw new Error("_computeTargetDebtDelta::denominatorPart is 0");
330
+ }
325
331
  const x_debt_usd = numeratorPart1
326
332
  .minus(numeratorPart2)
327
333
  .dividedBy(denominatorPart);
@@ -377,30 +383,42 @@ export class VesuMultiplyAdapter extends BaseAdapter<
377
383
  return 0;
378
384
  }
379
385
 
380
- async maxDeposit(amount?: Web3Number): Promise<PositionInfo> {
381
- // always add vesu modify position adapter, which shall
382
- // return correct max deposit
386
+ private _computeMax(collateralPosition: PositionInfo, debtPosition: PositionInfo): PositionInfo {
387
+ // in multiply, most of the debt also goes as collateral. hence, net addable is collateral - debt.
388
+ if (collateralPosition.usdValue <= 0 || collateralPosition.usdValue <= debtPosition.usdValue) {
389
+ return {
390
+ tokenInfo: this.config.collateral,
391
+ amount: new Web3Number(0, this.config.collateral.decimals),
392
+ usdValue: 0,
393
+ remarks: "Max deposit",
394
+ apy: { apy: 0, type: APYType.BASE },
395
+ protocol: this.protocol,
396
+ };
397
+ }
398
+ const maxCollateralUsd = collateralPosition.usdValue - debtPosition.usdValue;
399
+ const collateralPrice = collateralPosition.usdValue / collateralPosition.amount.toNumber();
400
+ const collateralAmount = new Web3Number(maxCollateralUsd / collateralPrice, this.config.collateral.decimals);
401
+ const netAPY =
402
+ (collateralPosition.apy.apy * collateralPosition.usdValue - debtPosition.apy.apy * debtPosition.usdValue) /
403
+ (maxCollateralUsd);
383
404
  return {
384
- tokenInfo: this.config.baseToken,
385
- amount: new Web3Number(0, this.config.baseToken.decimals),
386
- usdValue: 0,
405
+ tokenInfo: this.config.collateral,
406
+ amount: collateralAmount,
407
+ usdValue: maxCollateralUsd,
387
408
  remarks: "Max deposit",
388
- apy: { apy: 0, type: APYType.BASE },
409
+ apy: { apy: netAPY, type: APYType.BASE },
389
410
  protocol: this.protocol,
390
- }
411
+ };
412
+ }
413
+
414
+ async maxDeposit(amount?: Web3Number): Promise<PositionInfo> {
415
+ const { collateralPosition, debtPosition: debtPosition } = await getVesuCommonMaxDeposit(this._getPositionCommonContext(), amount);
416
+ return this._computeMax(collateralPosition, debtPosition);
391
417
  }
392
418
 
393
419
  async maxWithdraw(): Promise<PositionInfo> {
394
- // always add vesu modify position adapter, which shall
395
- // return correct max withdraw
396
- return {
397
- tokenInfo: this.config.baseToken,
398
- amount: new Web3Number(0, this.config.baseToken.decimals),
399
- usdValue: 0,
400
- remarks: "Max withdraw",
401
- apy: { apy: 0, type: APYType.BASE },
402
- protocol: this.protocol,
403
- }
420
+ const { collateralPosition, debtPosition } = await getVesuCommonMaxWithdraw(this._getPositionCommonContext());
421
+ return this._computeMax(collateralPosition, debtPosition);
404
422
  }
405
423
 
406
424
  // ─── Leaf Configuration ────────────────────────────────────────────────────
@@ -415,20 +433,27 @@ export class VesuMultiplyAdapter extends BaseAdapter<
415
433
  const collateral = this.config.collateral;
416
434
  const debt = this.config.debt;
417
435
  const { vesuMultiply, vesuSingleton, isV2 } = this._getMultiplyContract();
418
- const token = debt;
419
- const idSuffix = `${collateral.symbol}_${debt.symbol}`;
420
436
  const idApprovePrefix = "amc";
421
437
  const idDelegOnPrefix = "sd1";
422
438
  const idMultiplyPrefix = "vm";
423
439
  const idDelegOffPrefix = "sd2";
424
440
 
425
441
  return [
442
+ // allow approval of margin token and collateral to vesu multiply contract
443
+ // depending on strategy, respective token needs to be approved by implementer
426
444
  {
427
- target: token.address,
445
+ target: this.config.marginToken.address,
428
446
  method: "approve",
429
447
  packedArguments: [vesuMultiply.toBigInt()],
430
448
  sanitizer: SIMPLE_SANITIZER,
431
- id: `${idApprovePrefix}_${this.config.poolId.shortString()}_${idSuffix}`,
449
+ id: `${idApprovePrefix}_${this.config.poolId.shortString()}_${this.config.marginToken.symbol}`,
450
+ },
451
+ {
452
+ target: collateral.address,
453
+ method: "approve",
454
+ packedArguments: [vesuMultiply.toBigInt()],
455
+ sanitizer: SIMPLE_SANITIZER,
456
+ id: `${idApprovePrefix}_${this.config.poolId.shortString()}_${collateral.symbol}`,
432
457
  },
433
458
  {
434
459
  target: vesuSingleton,
@@ -436,7 +461,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
436
461
  packedArguments: isV2
437
462
  ? [vesuMultiply.toBigInt()]
438
463
  : [this.config.poolId.toBigInt(), vesuMultiply.toBigInt()],
439
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
464
+ sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
440
465
  id: `${idDelegOnPrefix}_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
441
466
  },
442
467
  {
@@ -457,7 +482,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
457
482
  packedArguments: isV2
458
483
  ? [vesuMultiply.toBigInt()]
459
484
  : [this.config.poolId.toBigInt(), vesuMultiply.toBigInt()],
460
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
485
+ sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
461
486
  id: `${idDelegOffPrefix}_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
462
487
  },
463
488
  ];
@@ -481,7 +506,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
481
506
  packedArguments: isV2
482
507
  ? [vesuMultiply.toBigInt()]
483
508
  : [this.config.poolId.toBigInt(), vesuMultiply.toBigInt()],
484
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
509
+ sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
485
510
  id: `sdow_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
486
511
  },
487
512
  {
@@ -493,7 +518,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
493
518
  this.config.debt.address.toBigInt(),
494
519
  this.config.vaultAllocator.toBigInt(),
495
520
  ],
496
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
521
+ sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
497
522
  id: `vmw_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
498
523
  },
499
524
  {
@@ -502,7 +527,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
502
527
  packedArguments: isV2
503
528
  ? [vesuMultiply.toBigInt()]
504
529
  : [this.config.poolId.toBigInt(), vesuMultiply.toBigInt()],
505
- sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
530
+ sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
506
531
  id: `sdofw_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
507
532
  },
508
533
  ];
@@ -557,7 +582,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
557
582
  const uint256Amount = uint256.bnToUint256(approveAmount.toWei());
558
583
 
559
584
  const approveCall: ManageCall = {
560
- proofReadableId: proofReadableIds.approve,
585
+ proofReadableId: approveToken.address.eq(this.config.collateral.address) ? proofReadableIds.approve_collateral : proofReadableIds.approve_margin,
561
586
  sanitizer: SIMPLE_SANITIZER,
562
587
  call: {
563
588
  contractAddress: approveToken.address,
@@ -699,11 +724,6 @@ export class VesuMultiplyAdapter extends BaseAdapter<
699
724
  let leverSwap: Swap[] = [];
700
725
  let leverSwapLimitAmount = Web3Number.fromWei(0, debtToken.decimals);
701
726
 
702
- const isIncrease = debtAmount.greaterThanOrEqualTo(0);
703
- if (!isIncrease && debtAmount.greaterThan(0)) {
704
- debtAmount = Web3Number.fromWei(0, this.config.debt.decimals);
705
- }
706
-
707
727
  if (!debtAmount.isZero() && debtAmount.greaterThan(0)) {
708
728
  try {
709
729
  let swapQuote: EkuboQuote;
@@ -914,10 +934,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
914
934
  throw new Error(`VesuMultiplyAdapter: Lever swap price impact too high (${leverSwapQuote.price_impact})`);
915
935
  }
916
936
 
917
- leverCollateralUsed = Web3Number.fromWei(
918
- leverSwapQuote.total_calculated,
919
- collateralToken.decimals
920
- ).abs();
937
+ leverCollateralUsed = collateralToSwap;
921
938
  }
922
939
 
923
940
  let withdrawSwap: Swap[] = [];
@@ -962,6 +979,8 @@ export class VesuMultiplyAdapter extends BaseAdapter<
962
979
  estimatedOutput.decimals = params.outputToken.decimals;
963
980
  withdrawSwapLimitAmount = estimatedOutput
964
981
  .multipliedBy(1 - this.maxSlippage);
982
+ } else {
983
+ throw new Error(`VesuMultiplyAdapter: Withdraw swap price impact too high (${withdrawQuote.price_impact})`);
965
984
  }
966
985
  }
967
986
  }
@@ -1029,24 +1048,27 @@ export class VesuMultiplyAdapter extends BaseAdapter<
1029
1048
  );
1030
1049
  const debtPrice = await this.config.pricer.getPrice(debtToken.symbol);
1031
1050
 
1051
+ const maxLTV = await this._vesuAdapter.getLTVConfig(
1052
+ this.config.networkConfig
1053
+ );
1054
+
1032
1055
  const { deltadebtAmountUnits: debtAmountToRepay } =
1033
- calculateDebtReductionAmountForWithdrawal(
1056
+ HealthFactorMath.calculateDebtReductionAmountForWithdrawal(
1034
1057
  existingDebtInfo.amount,
1035
1058
  existingCollateralInfo.amount,
1036
- MAX_LIQUIDATION_RATIO,
1059
+ maxLTV,
1060
+ this.config.targetHealthFactor,
1037
1061
  params.amount,
1038
1062
  collateralPrice.price,
1039
1063
  debtPrice.price,
1040
- debtToken.decimals
1064
+ collateralToken,
1065
+ debtToken
1041
1066
  );
1042
1067
  if (!debtAmountToRepay) {
1043
1068
  throw new Error("error calculating debt amount to repay");
1044
1069
  }
1045
1070
 
1046
- const debtToRepayAbs = new Web3Number(
1047
- debtAmountToRepay,
1048
- debtToken.decimals
1049
- ).abs();
1071
+ const debtToRepayAbs = debtAmountToRepay.abs();
1050
1072
  const existingDebtAbs = existingDebtInfo.amount.abs();
1051
1073
 
1052
1074
  const shouldCloseByDebt = debtToRepayAbs.greaterThanOrEqualTo(existingDebtAbs);
@@ -1155,7 +1177,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
1155
1177
  sqrt_ratio_limit: uint256.bnToUint256(
1156
1178
  route.sqrt_ratio_limit.toWei()
1157
1179
  ),
1158
- skip_ahead: BigInt(0),
1180
+ skip_ahead: BigInt(100),
1159
1181
  })),
1160
1182
  token_amount: {
1161
1183
  token: swap.token_amount.token.toBigInt(),
@@ -1254,12 +1276,13 @@ export class VesuMultiplyAdapter extends BaseAdapter<
1254
1276
  );
1255
1277
  const allZero = positions.every((p) => p.usdValue === 0);
1256
1278
 
1257
- if (allZero) {
1279
+ if (allZero && positions.length == 2) {
1258
1280
  const collateralUSD = 1000;
1259
1281
  const maxLTV = await this._vesuAdapter.getLTVConfig(
1260
1282
  this.config.networkConfig
1261
1283
  );
1262
1284
  const targetHF = this.config.targetHealthFactor;
1285
+ // though we are passing in token terms, but by simulating with price 1, usd terms == token terms
1263
1286
  const maxDebt = HealthFactorMath.getMaxDebtAmountOnLooping(
1264
1287
  new Web3Number(collateralUSD, this.config.collateral.decimals),
1265
1288
  1,
@@ -113,7 +113,7 @@ export async function getVesuCommonPosition(
113
113
  position.amount = position.amount.multipliedBy(-1);
114
114
  position.usdValue = position.usdValue * -1;
115
115
  }
116
- const result = { amount: position.amount, remarks: supportedPosition.isDebt ? "Vesu debt" : "Vesu collateral" };
116
+ const result = { amount: position.amount, remarks: position.remarks };
117
117
  ctx.setCache(cacheKey, result, 60000);
118
118
  return result;
119
119
  } catch (error) {
@@ -132,10 +132,11 @@ export async function getVesuCommonMaxBorrowableAPY(
132
132
  return collateralAPY.apy * 0.8;
133
133
  }
134
134
 
135
+ /// @dev this function is used to calculate the max deposit amount based on the available debt capacity
135
136
  export async function getVesuCommonMaxDeposit(
136
137
  ctx: VesuPositionCommonContext,
137
138
  amount?: Web3Number,
138
- ): Promise<PositionInfo> {
139
+ ): Promise<{collateralPosition: PositionInfo, debtPosition: PositionInfo}> {
139
140
  const collateral = ctx.collateral;
140
141
  const debt = ctx.debt;
141
142
  try {
@@ -169,15 +170,20 @@ export async function getVesuCommonMaxDeposit(
169
170
  throw new Error("Debt price is 0");
170
171
  }
171
172
 
172
- const maxCollateralFromDebt = HealthFactorMath.getMinCollateralRequiredOnLooping(
173
- actualMaxBorrowable,
173
+ /**
174
+ * We use existing collateral and debt also in the calculations.
175
+ * This will ensure any new collateral requirement also considers low ltv possibilities + new debt being created.
176
+ */
177
+ const maxCollateralFromDebt = HealthFactorMath.getCollateralRequired(
178
+ actualMaxBorrowable.plus(debtPosition.amount), // net new debt
174
179
  debtPrice.price,
175
180
  ctx.targetHealthFactor,
176
181
  maxLTV,
177
182
  collateralPrice.price,
178
183
  collateral,
179
184
  );
180
- const maxDepositAmount = amount ? amount.minimum(maxCollateralFromDebt) : maxCollateralFromDebt;
185
+ const maxNewCollateral = maxCollateralFromDebt.minus(collateralPosition.amount).minimum(0); // net new collateral
186
+ const maxDepositAmount = amount ? amount.minimum(maxNewCollateral) : maxNewCollateral;
181
187
  const usdValue = await ctx.getUSDValue(collateral, maxDepositAmount);
182
188
  const apys = await Promise.all([
183
189
  getVesuCommonAPY(ctx, { asset: collateral, isDebt: false }),
@@ -185,19 +191,22 @@ export async function getVesuCommonMaxDeposit(
185
191
  ]);
186
192
 
187
193
  const borrowAmountUSD = actualMaxBorrowable.multipliedBy(debtPrice.price);
188
- const netCollateralUSD = usdValue + borrowAmountUSD.toNumber();
189
- const netAPY =
190
- (apys[0].apy * netCollateralUSD + apys[1].apy * borrowAmountUSD.toNumber()) /
191
- usdValue;
192
194
 
193
- return {
195
+ return {collateralPosition: {
194
196
  tokenInfo: collateral,
195
197
  amount: maxDepositAmount,
196
198
  usdValue,
197
199
  remarks: "Max deposit based on available debt capacity",
198
- apy: { apy: netAPY, type: APYType.BASE },
200
+ apy: { apy: apys[0].apy, type: APYType.BASE },
199
201
  protocol: ctx.protocol,
200
- };
202
+ }, debtPosition: {
203
+ tokenInfo: debt,
204
+ amount: actualMaxBorrowable,
205
+ usdValue: borrowAmountUSD.toNumber(),
206
+ remarks: "Max borrow based on available debt capacity",
207
+ apy: { apy: apys[1].apy, type: APYType.BASE },
208
+ protocol: ctx.protocol,
209
+ }};
201
210
  } catch (error) {
202
211
  logger.error(`${ctx.adapterName}: Error calculating max deposit:`, error);
203
212
  throw error;
@@ -206,7 +215,7 @@ export async function getVesuCommonMaxDeposit(
206
215
 
207
216
  export async function getVesuCommonMaxWithdraw(
208
217
  ctx: VesuPositionCommonContext,
209
- ): Promise<PositionInfo> {
218
+ ): Promise<{collateralPosition: PositionInfo, debtPosition: PositionInfo}> {
210
219
  const collateral = ctx.collateral;
211
220
  const debt = ctx.debt;
212
221
  try {
@@ -219,30 +228,28 @@ export async function getVesuCommonMaxWithdraw(
219
228
  throw new Error("Could not find current positions");
220
229
  }
221
230
 
222
- const collateralPrice = collateralPosition.usdValue / collateralPosition.amount.toNumber();
223
- const debtInCollateral = debtPosition.usdValue / collateralPrice;
224
- const maxWithdrawable = collateralPosition.amount.minus(debtInCollateral);
225
- const result = maxWithdrawable.greaterThan(0)
226
- ? maxWithdrawable
227
- : new Web3Number("0", collateral.decimals);
228
- const usdValue = await ctx.getUSDValue(collateral, result);
229
- const debtUSD = debtPosition.usdValue;
230
231
  const apys = await Promise.all([
231
232
  getVesuCommonAPY(ctx, { asset: collateral, isDebt: false }),
232
233
  getVesuCommonAPY(ctx, { asset: debt, isDebt: true }),
233
234
  ]);
234
- const netAPY =
235
- usdValue - debtUSD > 0
236
- ? (apys[0].apy * usdValue + apys[1].apy * debtUSD) / (usdValue - debtUSD)
237
- : 0;
238
235
 
239
236
  return {
240
- tokenInfo: collateral,
241
- amount: result,
242
- usdValue,
243
- remarks: "Max withdraw based on health factor",
244
- apy: { apy: netAPY, type: APYType.BASE },
245
- protocol: ctx.protocol,
237
+ collateralPosition: {
238
+ tokenInfo: collateral,
239
+ amount: collateralPosition.amount,
240
+ usdValue: collateralPosition.usdValue,
241
+ remarks: "Max withdraw based on health factor",
242
+ apy: { apy: apys[0].apy, type: APYType.BASE },
243
+ protocol: ctx.protocol,
244
+ },
245
+ debtPosition: {
246
+ tokenInfo: debt,
247
+ amount: debtPosition.amount,
248
+ usdValue: debtPosition.usdValue,
249
+ remarks: "Max withdraw based on health factor",
250
+ apy: { apy: apys[1].apy, type: APYType.BASE },
251
+ protocol: ctx.protocol,
252
+ },
246
253
  };
247
254
  } catch (error) {
248
255
  logger.error(`${ctx.adapterName}: Error calculating max withdraw:`, error);