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

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 (51) hide show
  1. package/dist/cli.js +2 -2
  2. package/dist/cli.mjs +2 -2
  3. package/dist/index.browser.global.js +16173 -24081
  4. package/dist/index.browser.mjs +8572 -16661
  5. package/dist/index.d.ts +600 -2666
  6. package/dist/index.js +8660 -16784
  7. package/dist/index.mjs +8585 -16674
  8. package/package.json +3 -3
  9. package/src/data/redeem-request-nft.abi.json +752 -0
  10. package/src/data/universal-vault.abi.json +8 -7
  11. package/src/dataTypes/bignumber.browser.ts +5 -1
  12. package/src/dataTypes/bignumber.node.ts +5 -0
  13. package/src/global.ts +21 -1
  14. package/src/interfaces/common.tsx +39 -4
  15. package/src/modules/avnu.ts +19 -10
  16. package/src/modules/index.ts +1 -1
  17. package/src/strategies/base-strategy.ts +92 -8
  18. package/src/strategies/constants.ts +8 -3
  19. package/src/strategies/ekubo-cl-vault.tsx +150 -16
  20. package/src/strategies/factory.ts +21 -1
  21. package/src/strategies/index.ts +2 -6
  22. package/src/strategies/registry.ts +28 -5
  23. package/src/strategies/sensei.ts +29 -13
  24. package/src/strategies/svk-strategy.ts +29 -4
  25. package/src/strategies/token-boosted-xstrk-carry-strategy.tsx +1057 -0
  26. package/src/strategies/universal-adapters/adapter-utils.ts +2 -0
  27. package/src/strategies/universal-adapters/avnu-adapter.ts +19 -10
  28. package/src/strategies/universal-adapters/index.ts +1 -2
  29. package/src/strategies/universal-adapters/svk-troves-adapter.ts +160 -13
  30. package/src/strategies/universal-adapters/vesu-modify-position-adapter.ts +91 -42
  31. package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +75 -52
  32. package/src/strategies/universal-adapters/vesu-position-common.ts +38 -31
  33. package/src/strategies/universal-lst-muliplier-strategy.tsx +222 -269
  34. package/src/strategies/universal-strategy.tsx +166 -105
  35. package/src/strategies/vesu-rebalance.tsx +3 -6
  36. package/src/strategies/yoloVault.ts +1084 -0
  37. package/src/utils/health-factor-math.ts +29 -0
  38. package/src/modules/ExtendedWrapperSDk/index.ts +0 -62
  39. package/src/modules/ExtendedWrapperSDk/types.ts +0 -334
  40. package/src/modules/ExtendedWrapperSDk/wrapper.ts +0 -611
  41. package/src/strategies/universal-adapters/extended-adapter.ts +0 -835
  42. package/src/strategies/universal-adapters/usdc<>usdce-adapter.ts +0 -200
  43. package/src/strategies/vesu-extended-strategy/services/executionService.ts +0 -2233
  44. package/src/strategies/vesu-extended-strategy/services/extended-vesu-state-manager.ts +0 -4254
  45. package/src/strategies/vesu-extended-strategy/services/ltv-imbalance-rebalance-math.ts +0 -783
  46. package/src/strategies/vesu-extended-strategy/services/operationService.ts +0 -56
  47. package/src/strategies/vesu-extended-strategy/types/transaction-metadata.ts +0 -88
  48. package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +0 -78
  49. package/src/strategies/vesu-extended-strategy/utils/constants.ts +0 -48
  50. package/src/strategies/vesu-extended-strategy/utils/helper.ts +0 -528
  51. package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +0 -1014
@@ -2,6 +2,8 @@ import { ContractAddr } from "@/dataTypes";
2
2
 
3
3
  // Zellic audited
4
4
  export const SIMPLE_SANITIZER = ContractAddr.from('0x5a2e3ceb3da368b983a8717898427ab7b6daf04014b70f321e777f9aad940b4');
5
+ // Above SIMPLE_SANITIZER was not exposing the request_redeem method
6
+ export const SVK_SIMPLE_SANITIZER = ContractAddr.from('0x03dcde04343257c3ce14574676cb9c5b2eda16e332c1b8caf5dc4c95ac568d2f')
5
7
  export const EXTENDED_SANITIZER = ContractAddr.from('0x65891708362b24dcf4c40c8e218cce6e82d1d6b3a3404c9ab00a48f08e2c110');
6
8
  export const AVNU_LEGACY_SANITIZER = ContractAddr.from('0x0656fBE853f116DD53956176a553eDe8fE65632252f8aceB50C1B9B6c8237309');
7
9
  // Without flashloan options
@@ -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,9 +98,10 @@ 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,
104
+ // NOTE : this is a direction flag that we are swapping in this direction
97
105
  usdcToBtc: boolean,
98
106
  ): Promise<ManageCall[]> {
99
107
  const vaultAllocator = ContractAddr.from(this.config.vaultAllocator.address);
@@ -121,13 +129,14 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
121
129
  };
122
130
  logger.verbose(
123
131
  `${AvnuAdapter.name}::buildSwapCalls stored price info: ` +
124
- `${fromAmt} ${fromToken.symbol} → ${toAmt} ${toToken.symbol}, ` +
125
- `effectivePrice=${this.lastSwapPriceInfo.effectivePrice}`,
132
+ `${fromAmt} ${fromToken.symbol} → ${toAmt} ${toToken.symbol}, ` +
133
+ `effectivePrice=${this.lastSwapPriceInfo.effectivePrice}`,
126
134
  );
127
135
 
128
136
  const getCalldata = await this.avnuWrapper.getSwapCallData(
129
137
  quote,
130
138
  vaultAllocator.address,
139
+ params.minAmount,
131
140
  );
132
141
  const swapCallData = getCalldata[0];
133
142
  const approveAmount = uint256.bnToUint256(params.amount.toWei());
@@ -259,7 +268,7 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
259
268
  return [];
260
269
  }
261
270
 
262
- async getDepositCall(params: DepositParams): Promise<ManageCall[]> {
271
+ async getDepositCall(params: AvnuDepositParams): Promise<ManageCall[]> {
263
272
  try {
264
273
  const fromToken = this.config.supportedPositions[0].asset; //usdc
265
274
  const toToken = this.config.supportedPositions[1].asset; //wbtc
@@ -277,7 +286,7 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
277
286
  }
278
287
 
279
288
  //Swap wbtc to usdc
280
- async getWithdrawCall(params: WithdrawParams): Promise<ManageCall[]> {
289
+ async getWithdrawCall(params: AvnuWithdrawParams): Promise<ManageCall[]> {
281
290
  try {
282
291
  const toToken = this.config.supportedPositions[0].asset;
283
292
  const fromToken = this.config.supportedPositions[1].asset;
@@ -321,7 +330,7 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
321
330
  if (attempt === retries - 1) {
322
331
  throw err;
323
332
  }
324
- await new Promise((resolve) => setTimeout(resolve, MAX_DELAY));
333
+ await new Promise((resolve) => setTimeout(resolve, MAX_AVNU_RETRY_DELAY));
325
334
  }
326
335
  }
327
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";
@@ -14,11 +14,12 @@ import {
14
14
  WithdrawParams,
15
15
  PositionAmount,
16
16
  } from "./baseAdapter";
17
- import { SIMPLE_SANITIZER, toBigInt } from "./adapter-utils";
17
+ import { SIMPLE_SANITIZER, SVK_SIMPLE_SANITIZER, toBigInt } from "./adapter-utils";
18
18
  import { hash, uint256, Contract } from "starknet";
19
19
  import { logger } from "@/utils";
20
20
  // Troves SVK universal vault ABI: ERC-4626 + SVK views (e.g. due_assets_from_owner).
21
21
  import universalVaultAbi from "@/data/universal-vault.abi.json";
22
+ import redeemRequestNftAbi from "@/data/redeem-request-nft.abi.json";
22
23
 
23
24
  /** Public Troves strategies feed (APY + metadata). Override in config for tests. */
24
25
  export const DEFAULT_TROVES_STRATEGIES_API = "https://app.troves.fi/api/strategies";
@@ -40,6 +41,11 @@ export interface SvkTrovesAdapterConfig extends BaseAdapterConfig {
40
41
  positionOwner?: ContractAddr;
41
42
  /** Optional APY endpoint (defaults to {@link DEFAULT_TROVES_STRATEGIES_API}). */
42
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;
43
49
  }
44
50
 
45
51
  type TrovesStrategiesApiPayload = {
@@ -164,13 +170,21 @@ export class SvkTrovesAdapter extends BaseAdapter<DepositParams, WithdrawParams>
164
170
  const liquid = Web3Number.fromWei(liquidAssetsRaw.toString(), decimals);
165
171
 
166
172
  let pending = Web3Number.fromWei("0", decimals);
167
- try {
168
- const dueRaw: any = await vault.call("due_assets_from_owner", [owner.address]);
169
- pending = Web3Number.fromWei(dueRaw.toString(), decimals);
170
- } catch (e) {
171
- logger.warn(
172
- `${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,
173
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
+ }
174
188
  }
175
189
 
176
190
  const total = liquid.plus(pending);
@@ -189,6 +203,131 @@ export class SvkTrovesAdapter extends BaseAdapter<DepositParams, WithdrawParams>
189
203
  }
190
204
  }
191
205
 
206
+ async getPendingAssetsFromOwner(
207
+ owner: ContractAddr = this._positionOwner(),
208
+ decimals: number = this.config.baseToken.decimals,
209
+ ): Promise<Web3Number> {
210
+ const vault = new Contract({
211
+ abi: universalVaultAbi as never,
212
+ address: this.config.strategyVault.address,
213
+ providerOrAccount: this.config.networkConfig.provider,
214
+ });
215
+
216
+ try {
217
+ const dueRaw: any = await vault.call("due_assets_from_owner", [owner.address]);
218
+ return Web3Number.fromWei(dueRaw.toString(), decimals);
219
+ } catch (e) {
220
+ logger.warn(
221
+ `${SvkTrovesAdapter.name}::getPendingAssetsFromOwner: due_assets_from_owner failed (treating as 0): ${(e as Error).message}`,
222
+ );
223
+ return Web3Number.fromWei("0", decimals);
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Get pending assets from owner by scanning redeem request NFTs.
229
+ * This method iterates backwards through NFT IDs to find all pending redemptions for a specific owner.
230
+ *
231
+ * @param redeemRequestNFT - The redeem request NFT contract address
232
+ * @param owner - The owner address to check for pending redemptions (defaults to positionOwner)
233
+ * @param decimals - Token decimals for conversion (defaults to baseToken decimals)
234
+ * @returns Total pending assets from all NFTs owned by the specified address
235
+ */
236
+ async getPendingAssetsFromOwnerNFTMethod(
237
+ redeemRequestNFT: ContractAddr,
238
+ owner: ContractAddr = this._positionOwner(),
239
+ decimals: number = this.config.baseToken.decimals,
240
+ ): Promise<Web3Number> {
241
+ try {
242
+ const nftContract = new Contract({
243
+ abi: redeemRequestNftAbi as never,
244
+ address: redeemRequestNFT.address,
245
+ providerOrAccount: this.config.networkConfig.provider,
246
+ });
247
+
248
+ // Step 1: Get the latest NFT ID (the one that is not yet used)
249
+ const idLenRaw: any = await nftContract.id_len();
250
+ const latestId = BigInt(idLenRaw.toString());
251
+
252
+ if (latestId === 0n) {
253
+ logger.info(
254
+ `${SvkTrovesAdapter.name}::getPendingAssetsFromOwnerNFTMethod: No NFTs minted yet`,
255
+ );
256
+ return Web3Number.fromWei("0", decimals);
257
+ }
258
+
259
+ // Step 2: Start from (latestId - 1) and loop backwards to find all pending NFTs
260
+ const matchingIds: bigint[] = [];
261
+ let currentId = latestId - 1n;
262
+
263
+ while (currentId >= 0n) {
264
+ try {
265
+ // Convert currentId to u256 format
266
+ const idU256 = uint256.bnToUint256(currentId);
267
+ const ownerRaw: any = await nftContract.owner_of(idU256);
268
+
269
+ // Step 3: Filter IDs that have the owner matching our parameter
270
+ // Use ContractAddr.from to normalize addresses before comparison
271
+ const nftOwnerAddr = ContractAddr.from(ownerRaw.toString());
272
+ if (nftOwnerAddr.eq(owner)) {
273
+ matchingIds.push(currentId);
274
+ logger.debug(
275
+ `${SvkTrovesAdapter.name}::getPendingAssetsFromOwnerNFTMethod: Found matching NFT ID ${currentId} for owner ${owner.address}`,
276
+ );
277
+ }
278
+
279
+ currentId--;
280
+ } catch (e) {
281
+ // When owner_of throws an error, it means we've reached the last pending NFT
282
+ logger.info(
283
+ `${SvkTrovesAdapter.name}::getPendingAssetsFromOwnerNFTMethod: Reached last pending NFT at id ${currentId}`,
284
+ );
285
+ break;
286
+ }
287
+ }
288
+
289
+ if (matchingIds.length === 0) {
290
+ logger.info(
291
+ `${SvkTrovesAdapter.name}::getPendingAssetsFromOwnerNFTMethod: No matching NFTs found for owner ${owner.address}`,
292
+ );
293
+ return Web3Number.fromWei("0", decimals);
294
+ }
295
+
296
+ // Step 4: For each matching ID, call id_to_info and sum up all nominal values
297
+ let totalNominal = 0n;
298
+
299
+ for (const nftId of matchingIds) {
300
+ try {
301
+ const idU256 = uint256.bnToUint256(nftId);
302
+ const infoRaw: any = await nftContract.id_to_info(idU256);
303
+
304
+ // The response is a struct with { epoch: u256, nominal: u256 }
305
+ const nominal = BigInt(infoRaw.nominal.toString());
306
+ totalNominal += nominal;
307
+
308
+ logger.debug(
309
+ `${SvkTrovesAdapter.name}::getPendingAssetsFromOwnerNFTMethod: NFT ID ${nftId} has nominal ${nominal}`,
310
+ );
311
+ } catch (e) {
312
+ logger.warn(
313
+ `${SvkTrovesAdapter.name}::getPendingAssetsFromOwnerNFTMethod: Failed to get info for NFT ID ${nftId}: ${(e as Error).message}`,
314
+ );
315
+ }
316
+ }
317
+
318
+ logger.info(
319
+ `${SvkTrovesAdapter.name}::getPendingAssetsFromOwnerNFTMethod: Found ${matchingIds.length} NFTs with total nominal ${totalNominal}`,
320
+ );
321
+
322
+ return Web3Number.fromWei(totalNominal.toString(), decimals);
323
+ } catch (error) {
324
+ logger.error(
325
+ `${SvkTrovesAdapter.name}::getPendingAssetsFromOwnerNFTMethod: ${(error as Error).message}`,
326
+ );
327
+ return Web3Number.fromWei("0", decimals);
328
+ }
329
+ }
330
+
192
331
  async maxDeposit(amount?: Web3Number): Promise<PositionInfo> {
193
332
  const baseToken = this.config.baseToken;
194
333
  if (!amount) {
@@ -270,9 +409,9 @@ export class SvkTrovesAdapter extends BaseAdapter<DepositParams, WithdrawParams>
270
409
  return [
271
410
  {
272
411
  target: strategyVault,
273
- method: "withdraw",
412
+ method: "request_redeem",
274
413
  packedArguments: [recv.toBigInt(), owner.toBigInt()],
275
- sanitizer: SIMPLE_SANITIZER,
414
+ sanitizer: SVK_SIMPLE_SANITIZER,
276
415
  id: this._withdrawCallProofReadableId(),
277
416
  },
278
417
  ];
@@ -340,16 +479,24 @@ export class SvkTrovesAdapter extends BaseAdapter<DepositParams, WithdrawParams>
340
479
  const recv = this.config.vaultAllocator;
341
480
  const owner = this.config.vaultAllocator;
342
481
 
482
+ const vault = new Contract({
483
+ abi: universalVaultAbi as never,
484
+ address: strategyVault.address,
485
+ providerOrAccount: this.config.networkConfig.provider,
486
+ });
487
+ const sharesRaw: any = await vault.convert_to_shares(uint256Amount);
488
+ const sharesU256 = uint256.bnToUint256(sharesRaw.toString());
489
+
343
490
  return [
344
491
  {
345
492
  proofReadableId: this._withdrawCallProofReadableId(),
346
- sanitizer: SIMPLE_SANITIZER,
493
+ sanitizer: SVK_SIMPLE_SANITIZER,
347
494
  call: {
348
495
  contractAddress: strategyVault,
349
- selector: hash.getSelectorFromName("withdraw"),
496
+ selector: hash.getSelectorFromName("request_redeem"),
350
497
  calldata: [
351
- toBigInt(uint256Amount.low.toString()),
352
- toBigInt(uint256Amount.high.toString()),
498
+ toBigInt(sharesU256.low.toString()),
499
+ toBigInt(sharesU256.high.toString()),
353
500
  recv.toBigInt(),
354
501
  owner.toBigInt(),
355
502
  ],
@@ -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
  }
@@ -198,9 +202,19 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
198
202
  if (!helperOutput || helperOutput.lessThan(0)) {
199
203
  throw new Error(`Failed to calculate default deposit debt delta: ${helperOutput?.toNumber()}`);
200
204
  }
205
+ // Fixed :
206
+ // getMaxDebtAmount returns the TOTAL target debt at the new collateral level.
207
+ // Subtract current debt to get the incremental borrow delta.
201
208
  const normalizedDebtAmount = this._normalizeDebtAmountFromHelper(
202
209
  helperOutput,
203
- );
210
+ ).minus(state.currentDebt);
211
+ if (normalizedDebtAmount.lessThan(0)) {
212
+ logger.warn(`VesuModifyPositionAdapter: deposit debt delta is negative (${normalizedDebtAmount.toNumber()}), clamping to zero`);
213
+ return {
214
+ collateral: this._toSigned(collateralToAdd, false),
215
+ debt: this._toSigned(Web3Number.fromWei(0, this.config.debt.decimals), false),
216
+ };
217
+ }
204
218
  return {
205
219
  collateral: this._toSigned(collateralToAdd, false),
206
220
  debt: this._toSigned(normalizedDebtAmount, false),
@@ -221,8 +235,8 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
221
235
  state.debtPrice,
222
236
  this.config.debt,
223
237
  );
224
- if (!helperOutput || helperOutput.greaterThan(0)) {
225
- throw new Error(`Failed to calculate default withdraw debt delta: ${helperOutput?.toNumber()}`);
238
+ if (!helperOutput || helperOutput.lessThan(0)) {
239
+ throw new Error(`Failed to calculate max debt amount for withdraw: ${helperOutput?.toNumber()}`);
226
240
  }
227
241
  const normalizedDebtAmount = this._normalizeDebtAmountFromHelper(
228
242
  helperOutput,
@@ -262,21 +276,21 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
262
276
  const call = contract.populate("modify_position", {
263
277
  params: isV2
264
278
  ? {
265
- collateral_asset: this.config.collateral.address.toBigInt(),
266
- debt_asset: this.config.debt.address.toBigInt(),
267
- user: this.config.vaultAllocator.toBigInt(),
268
- collateral: this._amountStruct(collateralDelta),
269
- debt: this._amountStruct(debtDelta),
270
- }
279
+ collateral_asset: this.config.collateral.address.toBigInt(),
280
+ debt_asset: this.config.debt.address.toBigInt(),
281
+ user: this.config.vaultAllocator.toBigInt(),
282
+ collateral: this._amountStruct(collateralDelta),
283
+ debt: this._amountStruct(debtDelta),
284
+ }
271
285
  : {
272
- pool_id: this.config.poolId.toBigInt(),
273
- collateral_asset: this.config.collateral.address.toBigInt(),
274
- debt_asset: this.config.debt.address.toBigInt(),
275
- user: this.config.vaultAllocator.toBigInt(),
276
- collateral: this._amountStruct(collateralDelta),
277
- debt: this._amountStruct(debtDelta),
278
- data: [0],
279
- },
286
+ pool_id: this.config.poolId.toBigInt(),
287
+ collateral_asset: this.config.collateral.address.toBigInt(),
288
+ debt_asset: this.config.debt.address.toBigInt(),
289
+ user: this.config.vaultAllocator.toBigInt(),
290
+ collateral: this._amountStruct(collateralDelta),
291
+ debt: this._amountStruct(debtDelta),
292
+ data: [0],
293
+ },
280
294
  });
281
295
  return {
282
296
  proofReadableId,
@@ -320,11 +334,26 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
320
334
  }
321
335
 
322
336
  async maxDeposit(amount?: Web3Number): Promise<PositionInfo> {
323
- 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
+ };
324
346
  }
325
347
 
326
348
  async maxWithdraw(): Promise<PositionInfo> {
327
- 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
+ };
328
357
  }
329
358
 
330
359
  protected _getDepositLeaf(): {
@@ -337,18 +366,18 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
337
366
  const { addr, isV2 } = getVesuSingletonAddress(this.config.poolId);
338
367
  const modifyPackedArguments = isV2
339
368
  ? [
340
- this.config.collateral.address.toBigInt(),
341
- this.config.debt.address.toBigInt(),
342
- this.config.vaultAllocator.toBigInt(),
343
- ]
369
+ this.config.collateral.address.toBigInt(),
370
+ this.config.debt.address.toBigInt(),
371
+ this.config.vaultAllocator.toBigInt(),
372
+ ]
344
373
  : [
345
- this.config.poolId.toBigInt(),
346
- this.config.collateral.address.toBigInt(),
347
- this.config.debt.address.toBigInt(),
348
- this.config.vaultAllocator.toBigInt(),
349
- 1n,
350
- 0n,
351
- ];
374
+ this.config.poolId.toBigInt(),
375
+ this.config.collateral.address.toBigInt(),
376
+ this.config.debt.address.toBigInt(),
377
+ this.config.vaultAllocator.toBigInt(),
378
+ 1n,
379
+ 0n,
380
+ ];
352
381
 
353
382
  return [
354
383
  {
@@ -378,18 +407,18 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
378
407
  const { addr, isV2 } = getVesuSingletonAddress(this.config.poolId);
379
408
  const modifyPackedArguments = isV2
380
409
  ? [
381
- this.config.collateral.address.toBigInt(),
382
- this.config.debt.address.toBigInt(),
383
- this.config.vaultAllocator.toBigInt(),
384
- ]
410
+ this.config.collateral.address.toBigInt(),
411
+ this.config.debt.address.toBigInt(),
412
+ this.config.vaultAllocator.toBigInt(),
413
+ ]
385
414
  : [
386
- this.config.poolId.toBigInt(),
387
- this.config.collateral.address.toBigInt(),
388
- this.config.debt.address.toBigInt(),
389
- this.config.vaultAllocator.toBigInt(),
390
- 1n,
391
- 0n,
392
- ];
415
+ this.config.poolId.toBigInt(),
416
+ this.config.collateral.address.toBigInt(),
417
+ this.config.debt.address.toBigInt(),
418
+ this.config.vaultAllocator.toBigInt(),
419
+ 1n,
420
+ 0n,
421
+ ];
393
422
 
394
423
  return [
395
424
  {
@@ -473,4 +502,24 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
473
502
  this._prepareVesuAdapter();
474
503
  return this._vesuAdapter.getHealthFactor();
475
504
  }
505
+
506
+ /**
507
+ * Simulates a deposit of `depositAmount` collateral and returns how much
508
+ * debt (STRK) would be incrementally borrowed to reach the target LTV.
509
+ * Used upstream to size the AVNU swap call in the same transaction batch.
510
+ */
511
+ async getExpectedDepositDebtDelta(depositAmount: Web3Number): Promise<Web3Number> {
512
+ const defaults = await this._buildDefaultDepositDeltas({ amount: depositAmount });
513
+ return defaults.debt.amount;
514
+ }
515
+
516
+ /**
517
+ * Simulates a withdrawal of `withdrawAmount` collateral and returns the
518
+ * incremental debt delta needed to keep the target health factor.
519
+ * Positive means borrow, negative means repay.
520
+ */
521
+ async getExpectedWithdrawDebtDelta(withdrawAmount: Web3Number): Promise<Web3Number> {
522
+ const defaults = await this._buildDefaultWithdrawDeltas({ amount: withdrawAmount });
523
+ return defaults.debt.amount;
524
+ }
476
525
  }