@carrot-protocol/clend-vaults-rpc 0.0.30-execution-fee1-dev-c23f9d0 → 0.0.30-execution-fee1-dev-e23928b

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.
package/dist/rpc.js CHANGED
@@ -16,13 +16,14 @@ const utils_1 = require("./utils");
16
16
  const cache_1 = require("./cache");
17
17
  const clend_rpc_2 = require("@carrot-protocol/clend-rpc");
18
18
  const utils_2 = require("./utils");
19
+ const addresses_1 = require("./addresses");
19
20
  class ClendVaultsClient {
20
- constructor(provider, skipPreflight = false, jupApiKey) {
21
+ constructor(provider, skipPreflight = false, swapperConfig) {
21
22
  this.connection = provider.connection;
22
23
  this.program = new program_1.ClendVaultsProgram(provider);
23
24
  this.skipPreflight = skipPreflight;
24
25
  this.clendClient = new clend_rpc_1.ClendClient(provider.connection, provider.wallet, skipPreflight, 0);
25
- this.swapper = new swapper_1.ClendVaultsJupSwapper(this.program, jupApiKey);
26
+ this.swapper = new swapper_1.ClendVaultsJupSwapper(this.program, swapperConfig?.jupApiKey, swapperConfig?.excludeDexes);
26
27
  this.vaultCache = new cache_1.Cache();
27
28
  }
28
29
  address() {
@@ -59,12 +60,13 @@ class ClendVaultsClient {
59
60
  const clendAccountStates = await this.clendClient.getClendAccounts(vault.clendAccounts.map((ca) => ca.address));
60
61
  // calculate equity for each clend account
61
62
  for (const clendAccountState of clendAccountStates) {
62
- const lendingAccountEquity = (0, math_1.calculateLendingAccountEquity)(clendAccountState.lendingAccount.balances);
63
+ const { equity, leverage } = (0, math_1.calculateLendingAccountEquityAndLeverage)(clendAccountState.lendingAccount.balances);
63
64
  clendAccountEquity.push({
64
65
  address: clendAccountState.key,
65
- equity: lendingAccountEquity,
66
+ equity,
67
+ leverage,
66
68
  });
67
- vaultEquity += lendingAccountEquity;
69
+ vaultEquity += equity;
68
70
  }
69
71
  // calculate equity for each reserve
70
72
  const reserveEquity = [];
@@ -74,11 +76,17 @@ class ClendVaultsClient {
74
76
  reserveEquity.push({ address: asset.reserve.address, equity: rEquity });
75
77
  vaultEquity += rEquity;
76
78
  }
79
+ const sharesSupplyUi = (0, math_1.amountToUi)(vault.sharesSupply, vault.sharesDecimals);
80
+ const adjustedSharesSupplyUi = (0, math_1.amountToUi)(vault.adjustedSharesSupply, vault.sharesDecimals);
81
+ const preFeeNav = (0, math_1.calculateVaultNav)(vaultEquity, sharesSupplyUi);
82
+ const postFeeNav = (0, math_1.calculateVaultNav)(vaultEquity, adjustedSharesSupplyUi);
77
83
  return {
78
84
  address: vault.address,
79
85
  vaultEquity,
80
- clendAccountEquity,
81
- reserveEquity,
86
+ preFeeNav,
87
+ postFeeNav,
88
+ clendAccounts: clendAccountEquity,
89
+ reserves: reserveEquity,
82
90
  };
83
91
  }
84
92
  async initVault(sharesMint, managementFeeBps) {
@@ -128,29 +136,55 @@ class ClendVaultsClient {
128
136
  const txSig = await this.send([ix]);
129
137
  return txSig;
130
138
  }
131
- async issue(vault, assetMint, amount, minAmountOut) {
132
- const ixns = await this.prepareIssueIxns(this.address(), vault, assetMint, amount, minAmountOut);
133
- const txSig = await this.send(ixns);
139
+ async issue(vault, assetMint, amount, minAmountOut, vaultManager) {
140
+ const ixns = await this.prepareIssueIxns(this.address(), vault, assetMint, amount, minAmountOut, 2);
141
+ const additionalSigners = vaultManager ? [vaultManager] : [];
142
+ const txSig = await this.send(ixns, additionalSigners);
134
143
  return txSig;
135
144
  }
136
- async prepareIssueIxns(user, vault, assetMint, amount, minAmountOut) {
145
+ async prepareIssueIxns(user, vault, assetMint, amount, minAmountOut, additionalIxnCount) {
137
146
  // get vault and asset data
138
147
  const vaultState = await this.getVault(vault);
139
148
  const assetData = vaultState.assets.find((a) => a.mint.address.equals(assetMint));
140
149
  const remainingAccounts = await buildEquityRemainingAccounts(vaultState, this.clendClient);
141
- const ixns = await this.program.issue(vault, vaultState.sharesMint, user, assetMint, assetData.oracle.address, amount, minAmountOut, assetData.mint.tokenProgram, spl_token_1.TOKEN_2022_PROGRAM_ID, remainingAccounts);
150
+ const { issueStartIxns, issueEndIxns } = await this.program.issue(vault, vaultState.sharesMint, user, assetMint, assetData.oracle.address, amount, minAmountOut, assetData.mint.tokenProgram, spl_token_1.TOKEN_2022_PROGRAM_ID, remainingAccounts);
151
+ const endSessionIxIndex = additionalIxnCount +
152
+ 1 + // userSessionStartIx
153
+ issueStartIxns.length +
154
+ issueEndIxns.length;
155
+ const { startIx: userSessionStartIx, endIx: userSessionEndIx } = await this.program.userSession(vault, vaultState.manager, user, "issue", endSessionIxIndex);
156
+ const ixns = [
157
+ userSessionStartIx,
158
+ ...issueStartIxns,
159
+ ...issueEndIxns,
160
+ userSessionEndIx,
161
+ ];
142
162
  return ixns;
143
163
  }
144
- async redeem(vault, assetMint, sharesAmount, minAmountOut) {
145
- const ixns = await this.prepareRedeemIxns(this.address(), vault, assetMint, sharesAmount, minAmountOut);
146
- const txSig = await this.send(ixns);
164
+ async redeem(vault, assetMint, sharesAmount, minAmountOut, vaultManager, additionalIxnCount = 0) {
165
+ let vaultManagerKey = this.address();
166
+ if (vaultManager) {
167
+ vaultManagerKey = vaultManager.publicKey;
168
+ }
169
+ const redeemIxns = await this.prepareRedeemIxns(this.address(), vault, assetMint, sharesAmount, minAmountOut);
170
+ const endIndex = redeemIxns.length + additionalIxnCount + 1; // start session;
171
+ const { startIx: userSessionStartIx, endIx: userSessionEndIx } = await this.program.userSession(vault, vaultManagerKey, this.address(), "redeem", endIndex);
172
+ const ixns = [
173
+ userSessionStartIx,
174
+ ...redeemIxns,
175
+ userSessionEndIx,
176
+ ];
177
+ const additionalSigners = vaultManager ? [vaultManager] : [];
178
+ const txSig = await this.send(ixns, additionalSigners);
147
179
  return txSig;
148
180
  }
149
181
  async prepareRedeemIxns(user, vault, assetMint, amount, minAmountOut) {
150
182
  const vaultState = await this.getVault(vault);
151
183
  const assetData = vaultState.assets.find((a) => a.mint.address.equals(assetMint));
152
184
  const remainingAccounts = await buildEquityRemainingAccounts(vaultState, this.clendClient);
153
- return this.program.redeem(vault, vaultState.sharesMint, user, assetMint, assetData.oracle.address, amount, minAmountOut, assetData.mint.tokenProgram, spl_token_1.TOKEN_2022_PROGRAM_ID, remainingAccounts);
185
+ const redeemIxns = await this.program.redeem(vault, vaultState.sharesMint, user, assetMint, assetData.oracle.address, amount, minAmountOut, assetData.mint.tokenProgram, spl_token_1.TOKEN_2022_PROGRAM_ID, remainingAccounts);
186
+ const ixns = [...redeemIxns];
187
+ return ixns;
154
188
  }
155
189
  async clendAccountDeposit(vault, clendAccount, assetMint, amount) {
156
190
  // fetch clend account data
@@ -219,7 +253,7 @@ class ClendVaultsClient {
219
253
  }
220
254
  const activeBankData = await this.clendClient.getBanks(clendAccountActiveBanks);
221
255
  const ra = (0, clend_rpc_1.getClendAccountRemainingAccounts)(activeBankData);
222
- const ixns = await this.program.clendAccountBorrow(vault, this.address(), clendAccountData.group, clendAccount, assetMint, assetData.mint.tokenProgram, amount, ra);
256
+ const ixns = await this.program.clendAccountBorrow(vault, this.address(), clendAccountData.group, clendAccount, assetMint, assetData.oracle.address, assetData.mint.tokenProgram, null, amount, ra);
223
257
  const txSig = await this.send(ixns);
224
258
  return txSig;
225
259
  }
@@ -409,9 +443,9 @@ class ClendVaultsClient {
409
443
  if (isIncrease && finalDebtDeltaUi > 0) {
410
444
  // how much debt is actually borrowable in UI terms
411
445
  const availableDebtUiRaw = (0, math_1.amountToUi)(debtBankData.availableSupply, debtDecimals);
412
- // leave a tiny buffer so we don't try to consume the very last lamport
413
- const MIN_REMAINING_UI = 0.01; // e.g. 1 cent of USDC
414
- const maxUsableDebtUi = Math.max(0, availableDebtUiRaw - MIN_REMAINING_UI);
446
+ // calculate the max usable debt based on the target pool utilization
447
+ const targetUtilFactor = 0.85; // target pool utilization is 85%
448
+ const maxUsableDebtUi = availableDebtUiRaw * targetUtilFactor;
415
449
  if (maxUsableDebtUi <= 0) {
416
450
  // error if there's literally no debt available to borrow
417
451
  throw new Error("Not enough debt available to adjust leverage");
@@ -501,9 +535,13 @@ class ClendVaultsClient {
501
535
  slippageBps,
502
536
  swapMode: "ExactIn",
503
537
  });
504
- const swapIxns = await this.swapper.getSwapIxns(swapQuote);
538
+ const swapIxns = await this.swapper.getSwapIxns(swapQuote, null);
505
539
  const debtTokenProgram = await (0, clend_rpc_1.getTokenProgramForMintFromRpc)(this.program.program.provider.connection, debtBankData.mint);
506
- const borrowIxns = await this.program.clendAccountBorrow(vault, vaultManager, clendAccountState.group, clendAccount, debtBankData.mint, debtTokenProgram, additionalDebtAmount, remainingAccounts);
540
+ const debtVaultAssetState = vaultState.assets.find((a) => a.mint.address.equals(debtBankData.mint));
541
+ if (!debtVaultAssetState) {
542
+ throw new Error(`Vault asset not found: ${debtBankData.mint.toString()}`);
543
+ }
544
+ const borrowIxns = await this.program.clendAccountBorrow(vault, vaultManager, clendAccountState.group, clendAccount, debtBankData.mint, debtVaultAssetState.oracle.address, debtTokenProgram, null, additionalDebtAmount, remainingAccounts);
507
545
  const additionalCollateralAmount = swapQuote.outAmount;
508
546
  const depositIxns = await this.program.clendAccountDeposit(vault, vaultManager, clendAccountState.group, clendAccount, collateralBankData.mint, collateralTokenProgram, additionalCollateralAmount, userCollateralAta, remainingAccounts);
509
547
  instructions.push(...borrowIxns, ...swapIxns.ixns, ...depositIxns);
@@ -526,7 +564,7 @@ class ClendVaultsClient {
526
564
  // 3. The final amount to repay is the optimistic amount from the quote,
527
565
  // as we will use the `repay_up_to_amount` flag on-chain.
528
566
  const finalDebtToRepay = swapQuote.outAmount;
529
- const swapIxns = await this.swapper.getSwapIxns(swapQuote);
567
+ const swapIxns = await this.swapper.getSwapIxns(swapQuote, null);
530
568
  // Withdraw the calculated collateral needed for the swap
531
569
  const withdrawIxns = await this.program.clendAccountWithdraw(vault, vaultManager, clendAccountState.group, clendAccount, collateralBankData.mint, collateralTokenProgram, collateralToWithdrawBN, remainingAccounts);
532
570
  // Repay the debt with the swap output
@@ -549,18 +587,19 @@ class ClendVaultsClient {
549
587
  // create instructions for redeeming shares
550
588
  // however this will pull from a clend account balance
551
589
  // the equity value of the shares desired for redemption
552
- async prepareRedeemFromClendAccountIxns(user, vault, clendAccount, outputAssetMint, collateralMint, debtMint, totalSharesToRedeem, sharesToRedeemFromClendAccount, slippageBps) {
590
+ async prepareRedeemFromClendAccountIxns(user, vault, clendAccount, outputAssetMint, collateralMint, debtMint, totalSharesToRedeem, sharesToRedeemFromClendAccount, slippageBps, targetLeverage, additionalIxnCount = 0) {
591
+ if (targetLeverage <= 1.0) {
592
+ throw new Error("Target leverage must be greater than 1.0");
593
+ }
553
594
  const vaultState = await this.getVault(vault);
554
595
  const outputAssetState = vaultState.assets.find((a) => a.mint.address.equals(outputAssetMint));
555
596
  if (!outputAssetState) {
556
597
  throw new Error(`output asset mint ${outputAssetMint.toString()} not found in vault ${vault.toString()}`);
557
598
  }
558
599
  const vaultEquity = await this.getVaultEquity(vaultState);
559
- const sharesSupplyUi = (0, math_1.amountToUi)(vaultState.sharesSupply, vaultState.sharesDecimals);
560
600
  const sharesToRedeemFromClendAccountUi = (0, math_1.amountToUi)(sharesToRedeemFromClendAccount, vaultState.sharesDecimals);
561
- const vaultNav = (0, math_1.calculateVaultNav)(vaultEquity.vaultEquity, sharesSupplyUi);
562
- // TODO: will need to add fees to this
563
- const desiredOutputAmount = (0, math_1.convertSharesToAsset)(vaultNav, sharesToRedeemFromClendAccountUi, outputAssetState.mint.decimals, outputAssetState.oracle.priceUi);
601
+ const vaultNavPostFee = vaultEquity.postFeeNav;
602
+ const desiredOutputAmount = (0, math_1.convertSharesToAsset)(vaultNavPostFee, sharesToRedeemFromClendAccountUi, outputAssetState.mint.decimals, outputAssetState.oracle.priceUi);
564
603
  // check if clend account is listed in vault config
565
604
  const clendAccountIsValid = vaultState.clendAccounts.some((c) => c.address.equals(clendAccount));
566
605
  if (!clendAccountIsValid) {
@@ -571,11 +610,6 @@ class ClendVaultsClient {
571
610
  if (!clendAccountState) {
572
611
  throw new Error(`error fetching clend account state: ${clendAccount.toString()}`);
573
612
  }
574
- // we need to know how many ixns will go before the leverage ixns
575
- // this is for the flash loan index positioning
576
- let prependedIxCount = 0;
577
- let cuIxCount = 0; // TODO: in the clend libs we already account for this
578
- prependedIxCount += cuIxCount;
579
613
  // get the redemption ixns
580
614
  // these are run after the withdraw leverage ixns
581
615
  const redeemIxns = await this.prepareRedeemIxns(user, vault, outputAssetMint, totalSharesToRedeem, new anchor_1.BN(0));
@@ -586,22 +620,26 @@ class ClendVaultsClient {
586
620
  let debtBankData;
587
621
  let collateralBankData;
588
622
  switch (outputAssetMint.toString()) {
589
- case collateralMint.toString():
590
- // get params for withdraw leverage
591
- const { debtToRepay: debtToRepayCollateral, collateralToWithdraw: collateralToWithdrawCollateral, debtBankData: debtBankDataCollateral, collateralBankData: collateralBankDataCollateral, } = await this.clendClient.getNetWithdrawLeverageCollateralParams(clendAccountState.group, clendAccount, collateralMint, debtMint, desiredOutputAmount, false);
623
+ case collateralMint.toString(): {
624
+ // withdraw-to-target-leverage for collateral output
625
+ const { debtToRepay: debtToRepayCollateral, collateralToWithdraw: collateralToWithdrawCollateral, debtBankData: debtBankDataCollateral, collateralBankData: collateralBankDataCollateral, } = await this.clendClient.getWithdrawToTargetLeverageCollateralParams(clendAccountState.group, clendAccount, collateralMint, debtMint, desiredOutputAmount, // user-facing collateral amount
626
+ targetLeverage);
592
627
  debtToRepay = debtToRepayCollateral;
593
628
  collateralToWithdraw = collateralToWithdrawCollateral;
594
629
  debtBankData = debtBankDataCollateral;
595
630
  collateralBankData = collateralBankDataCollateral;
596
631
  break;
597
- case debtMint.toString():
598
- // get params for withdraw leverage
599
- const { debtToRepay: debtToRepayDebt, collateralToWithdraw: collateralToWithdrawDebt, debtBankData: debtBankDataDebt, collateralBankData: collateralBankDataDebt, } = await this.clendClient.getNetWithdrawLeverageDebtParams(clendAccountState.group, clendAccount, collateralMint, debtMint, desiredOutputAmount, false);
632
+ }
633
+ case debtMint.toString(): {
634
+ // withdraw-to-target-leverage for debt output
635
+ const { debtToRepay: debtToRepayDebt, collateralToWithdraw: collateralToWithdrawDebt, debtBankData: debtBankDataDebt, collateralBankData: collateralBankDataDebt, } = await this.clendClient.getWithdrawToTargetLeverageDebtParams(clendAccountState.group, clendAccount, collateralMint, debtMint, desiredOutputAmount, // user-facing debt amount
636
+ targetLeverage);
600
637
  debtToRepay = debtToRepayDebt;
601
638
  collateralToWithdraw = collateralToWithdrawDebt;
602
639
  debtBankData = debtBankDataDebt;
603
640
  collateralBankData = collateralBankDataDebt;
604
641
  break;
642
+ }
605
643
  default:
606
644
  throw new Error(`invalid output token mint: ${outputAssetMint.toString()}`);
607
645
  }
@@ -609,22 +647,118 @@ class ClendVaultsClient {
609
647
  let ixns = [];
610
648
  let luts = [];
611
649
  switch (outputAssetMint.toString()) {
612
- case collateralMint.toString():
613
- const { ixns: collateralIxns, luts: collateralLuts } = await this.getWithdrawLeverageCollateralIxns(vault, vaultState.manager, user, clendAccountState.group, clendAccount, collateralMint, debtMint, collateralBankData.mintDecimals, debtBankData.mintDecimals, false, collateralToWithdraw, desiredOutputAmount, debtToRepay, slippageBps, prependedIxCount);
650
+ case collateralMint.toString(): {
651
+ const { ixns: collateralIxns, luts: collateralLuts } = await this.getWithdrawLeverageCollateralIxns(vault, vaultState.manager, user, clendAccountState.group, clendAccount, collateralMint, debtMint, collateralBankData.mintDecimals, debtBankData.mintDecimals, false, collateralToWithdraw, desiredOutputAmount, debtToRepay, slippageBps, additionalIxnCount);
614
652
  ixns = collateralIxns;
615
653
  luts = collateralLuts;
616
654
  break;
617
- case debtMint.toString():
618
- const { ixns: debtIxns, luts: debtLuts } = await this.getWithdrawLeverageDebtIxns(vault, vaultState.manager, user, clendAccountState.group, clendAccount, collateralMint, debtMint, collateralBankData.mintDecimals, debtBankData.mintDecimals, false, debtToRepay, collateralToWithdraw, slippageBps, prependedIxCount);
655
+ }
656
+ case debtMint.toString(): {
657
+ const { ixns: debtIxns, luts: debtLuts } = await this.getWithdrawLeverageDebtIxns(vault, vaultState.manager, user, clendAccountState.group, clendAccount, collateralMint, debtMint, collateralBankData.mintDecimals, debtBankData.mintDecimals, false, debtToRepay, collateralToWithdraw, slippageBps, additionalIxnCount);
619
658
  ixns = debtIxns;
620
659
  luts = debtLuts;
621
660
  break;
661
+ }
622
662
  default:
623
663
  throw new Error(`invalid output token mint: ${outputAssetMint.toString()}`);
624
664
  }
625
- const allIxns = [...ixns, ...redeemIxns];
665
+ const endSessionIxIndex = ixns.length +
666
+ additionalIxnCount +
667
+ 1 + // userSessionStartIx
668
+ redeemIxns.length;
669
+ const { startIx: userSessionStartIx, endIx: userSessionEndIx } = await this.program.userSession(vault, vaultState.manager, user, "redeem", endSessionIxIndex);
670
+ const allIxns = [
671
+ userSessionStartIx,
672
+ ...ixns,
673
+ ...redeemIxns,
674
+ userSessionEndIx,
675
+ ];
626
676
  return { ixns: allIxns, luts };
627
677
  }
678
+ /**
679
+ * Prepare instructions for issuing into a vault *and* depositing into a CLend account
680
+ * to reach a target leverage.
681
+ *
682
+ * Order:
683
+ * 1) userSessionStart
684
+ * 2) issueStart (pull tokens from user into vault)
685
+ * 3) CLend deposit leverage ixns (move vault funds into CLend account at target leverage)
686
+ * 4) issueEnd (mint vault shares to user)
687
+ * 5) userSessionEnd
688
+ */
689
+ async prepareIssueIntoClendAccountIxns(user, vault, clendAccount, inputAssetMint, // what user is depositing into the vault
690
+ collateralMint, // CLend collateral mint
691
+ debtMint, // CLend debt mint
692
+ inputAmount, slippageBps, targetLeverage, additionalIxnCount = 0) {
693
+ if (targetLeverage <= 1.0) {
694
+ throw new Error("Target leverage must be greater than 1.0");
695
+ }
696
+ const vaultState = await this.getVault(vault);
697
+ // Make sure the input asset is part of the vault config
698
+ const assetData = vaultState.assets.find((a) => a.mint.address.equals(inputAssetMint));
699
+ if (!assetData) {
700
+ throw new Error(`input asset mint ${inputAssetMint.toString()} not found in vault ${vault.toString()}`);
701
+ }
702
+ // check if clend account is listed in vault config
703
+ const clendAccountIsValid = vaultState.clendAccounts.some((c) => c.address.equals(clendAccount));
704
+ if (!clendAccountIsValid) {
705
+ throw new Error(`Clend account ${clendAccount.toString()} not found in vault ${vault.toString()} config`);
706
+ }
707
+ // fetch clend account state
708
+ const clendAccountState = await this.clendClient.getClendAccount(clendAccount);
709
+ if (!clendAccountState) {
710
+ throw new Error(`error fetching clend account state: ${clendAccount.toString()}`);
711
+ }
712
+ // ----- 1) Build issue start/end (but don't wrap in userSession yet) -----
713
+ const remainingAccounts = await buildEquityRemainingAccounts(vaultState, this.clendClient);
714
+ const { issueStartIxns, issueEndIxns } = await this.program.issue(vault, vaultState.sharesMint, user, inputAssetMint, assetData.oracle.address, inputAmount, new anchor_1.BN(0), // minAmountOut: can be wired to slippage if you want
715
+ assetData.mint.tokenProgram, spl_token_1.TOKEN_2022_PROGRAM_ID, remainingAccounts);
716
+ // ----- 2) Build CLend deposit-leverage ixns into `clendAccount` -----
717
+ // We'll collect CLend ixns + lookup tables here
718
+ let clendIxns = [];
719
+ let clendLuts = [];
720
+ const clendGroup = clendAccountState.group;
721
+ switch (inputAssetMint.toString()) {
722
+ case collateralMint.toString(): {
723
+ // User deposits collateral token into the vault; we then lever it up as collateral.
724
+ const { borrowAmount, totalDepositAmount: _totalDepositAmount, debtBankData, collateralBankData, } = await this.clendClient.getDepositLeverageFromCollateralParams(user, clendGroup, collateralMint, debtMint, inputAmount, targetLeverage, slippageBps);
725
+ const { ixns, luts } = await this.getDepositLeverageCollateralIxns(vault, vaultState.manager, user, clendGroup, clendAccount, collateralMint, debtMint, collateralBankData.mintDecimals, debtBankData.mintDecimals, borrowAmount, inputAmount, slippageBps, issueStartIxns.length + additionalIxnCount + 1);
726
+ clendIxns = ixns;
727
+ clendLuts = luts;
728
+ break;
729
+ }
730
+ case debtMint.toString(): {
731
+ // User deposits debt token (e.g. USDC) into the vault; we swap+lever into collateral.
732
+ const { borrowAmount, totalDepositAmount, debtBankData, collateralBankData, } = await this.clendClient.getDepositLeverageFromDebtParams(user, clendGroup, collateralMint, debtMint, inputAmount, // initialUserDebtContributionForSwap
733
+ targetLeverage, slippageBps);
734
+ const { ixns, luts } = await this.getDepositLeverageDebtIxns(vault, vaultState.manager, user, clendGroup, clendAccount, collateralMint, debtMint, collateralBankData.mintDecimals, debtBankData.mintDecimals, inputAmount, borrowAmount, slippageBps, issueStartIxns.length + additionalIxnCount + 1);
735
+ clendIxns = ixns;
736
+ clendLuts = luts;
737
+ break;
738
+ }
739
+ default:
740
+ throw new Error(`input asset mint ${inputAssetMint.toString()} is not equal to collateralMint or debtMint`);
741
+ }
742
+ // ----- 3) Build userSessionStart / userSessionEnd around everything -----
743
+ const endSessionIxIndex = additionalIxnCount +
744
+ 1 + // userSessionStartIx
745
+ issueStartIxns.length +
746
+ clendIxns.length +
747
+ issueEndIxns.length;
748
+ const { startIx: userSessionStartIx, endIx: userSessionEndIx } = await this.program.userSession(vault, vaultState.manager, user, "issue", endSessionIxIndex);
749
+ // ----- 4) Stitch everything together in the required order -----
750
+ const ixns = [
751
+ userSessionStartIx,
752
+ ...issueStartIxns,
753
+ ...clendIxns,
754
+ ...issueEndIxns,
755
+ userSessionEndIx,
756
+ ];
757
+ return {
758
+ ixns,
759
+ luts: clendLuts,
760
+ };
761
+ }
628
762
  async getWithdrawLeverageCollateralIxns(vault, vaultManager, user, clendGroup, clendAccount, collateralMint, debtMint, collateralDecimals, debtDecimals, withdrawAll, collateralToWithdraw, desiredNetCollateralToReceive, debtToRepay, slippageBps, additionalIxnCount) {
629
763
  const clendAccountData = await this.clendClient.getClendAccount(clendAccount);
630
764
  if (!clendAccountData) {
@@ -665,11 +799,7 @@ class ClendVaultsClient {
665
799
  const debtToRepayUi = new decimal_js_1.Decimal((0, math_1.amountToUi)(debtToRepay, debtDecimals).toString());
666
800
  const collateralToSwapUi_ideal = debtToRepayUi.div(outAmountUi);
667
801
  const collateralToSwap_ideal = (0, math_1.uiToAmount)(collateralToSwapUi_ideal.toNumber(), collateralDecimals);
668
- collateralToSwap = collateralToSwap_ideal;
669
- //collateralToSwap = adjustAmountForSlippage(
670
- // collateralToSwap_ideal,
671
- // slippageBps,
672
- //);
802
+ collateralToSwap = (0, math_1.adjustAmountForSlippage)(collateralToSwap_ideal, slippageBps);
673
803
  // Step 4 (Final Quote): Get the real quote for the buffered amount.
674
804
  swapQuote = await this.swapper.getQuote(vault, {
675
805
  payer: vaultManager,
@@ -689,7 +819,7 @@ class ClendVaultsClient {
689
819
  }
690
820
  // 3. The final amount to repay is the minimum we are guaranteed to get from the swap.
691
821
  const finalDebtToRepay = swapQuote.outAmount;
692
- const swapIxns = await this.swapper.getSwapIxns(swapQuote);
822
+ const swapIxns = await this.swapper.getSwapIxns(swapQuote, user);
693
823
  const { tokenProgram: collateralTokenProgram } = await (0, state_1.getMintState)(collateralMint, this.connection);
694
824
  const { tokenProgram: debtTokenProgram } = await (0, state_1.getMintState)(debtMint, this.connection);
695
825
  const withdrawIx = await this.program.clendAccountWithdraw(vault, vaultManager, clendGroup, clendAccount, collateralMint, collateralTokenProgram, collateralToWithdraw, remainingAccounts);
@@ -757,8 +887,7 @@ class ClendVaultsClient {
757
887
  ...swapIxns.ixns,
758
888
  ...repayIx,
759
889
  ];
760
- const cuIxns = 2;
761
- const endIndex = (0, utils_1.parseBN)(cuIxns + ixnsWithoutFlashLoan.length + 1 + additionalIxnCount);
890
+ const endIndex = (0, utils_1.parseBN)(ixnsWithoutFlashLoan.length + 1 + additionalIxnCount + 1);
762
891
  const remainingAccountsForFlashLoan = (0, clend_rpc_1.getClendAccountRemainingAccounts)(activeBankData);
763
892
  const { beginFlashLoanIx, endFlashLoanIx } = await this.clendClient.instructions.createFlashLoanInstructions(clendAccount, user, endIndex, remainingAccountsForFlashLoan);
764
893
  const instructions = [
@@ -792,7 +921,7 @@ class ClendVaultsClient {
792
921
  // Determine the final amount to repay. It's the lesser of what we need and what we're guaranteed to get.
793
922
  const finalDebtToRepay = anchor_1.BN.min(debtToRepay, minDebtReceivedFromSwap);
794
923
  // Get Swap Instructions from the swapper
795
- const swapIxns = await this.swapper.getSwapIxns(swapQuote);
924
+ const swapIxns = await this.swapper.getSwapIxns(swapQuote, user);
796
925
  // --- ATAs and Clend Instructions ---
797
926
  const { tokenProgram: collateralTokenProgram } = await (0, state_1.getMintState)(collateralMint, this.connection);
798
927
  const { tokenProgram: debtTokenProgram } = await (0, state_1.getMintState)(debtMint, this.connection);
@@ -865,8 +994,7 @@ class ClendVaultsClient {
865
994
  ...repayIx,
866
995
  ];
867
996
  // Flash Loan Wrapping
868
- const cuIxns = 2;
869
- const endIndex = (0, utils_1.parseBN)(cuIxns + ixnsWithoutFlashLoan.length + 1 + additionalIxnCount);
997
+ const endIndex = (0, utils_1.parseBN)(ixnsWithoutFlashLoan.length + 1 + additionalIxnCount + 1);
870
998
  const remainingAccountsForFlashLoan = (0, clend_rpc_1.getClendAccountRemainingAccounts)(activeBankData);
871
999
  const { beginFlashLoanIx, endFlashLoanIx } = await this.clendClient.instructions.createFlashLoanInstructions(clendAccount, user, endIndex, remainingAccountsForFlashLoan);
872
1000
  const instructions = [
@@ -876,11 +1004,163 @@ class ClendVaultsClient {
876
1004
  ];
877
1005
  return { ixns: instructions, luts: swapIxns.luts };
878
1006
  }
1007
+ async getDepositLeverageCollateralIxns(vault, vaultManager, user, clendGroup, clendAccount, collateralMint, debtMint, collateralDecimals, debtDecimals, borrowAmount, // protocol debt we want to borrow (from params fn)
1008
+ depositAmount, // user's collateral already in the vault (issueStart)
1009
+ slippageBps, additionalIxnCount) {
1010
+ const vaultState = await this.getVault(vault);
1011
+ const debtAssetState = vaultState.assets.find((a) => a.mint.address.equals(debtMint));
1012
+ if (!debtAssetState) {
1013
+ throw new Error(`Debt asset not found: ${debtMint.toString()}`);
1014
+ }
1015
+ const collateralAssetState = vaultState.assets.find((a) => a.mint.address.equals(collateralMint));
1016
+ if (!collateralAssetState) {
1017
+ throw new Error(`Collateral asset not found: ${collateralMint.toString()}`);
1018
+ }
1019
+ // 1. Load clend account + banks
1020
+ const clendAccountData = await this.clendClient.getClendAccount(clendAccount);
1021
+ if (!clendAccountData) {
1022
+ throw new Error(`Clend account not found: ${clendAccount.toString()}`);
1023
+ }
1024
+ let activeBanks = (0, clend_rpc_1.getClendAccountActiveBanks)(clendAccountData);
1025
+ // if collateral or debt bank not active, add them
1026
+ const debtBankPk = (0, clend_rpc_1.getBankPda)(clendGroup, debtMint);
1027
+ const collateralBankPk = (0, clend_rpc_1.getBankPda)(clendGroup, collateralMint);
1028
+ const debtBankExists = activeBanks.some((b) => b.equals(debtBankPk));
1029
+ if (!debtBankExists) {
1030
+ activeBanks.push(debtBankPk);
1031
+ }
1032
+ const collateralBankExists = activeBanks.some((b) => b.equals(collateralBankPk));
1033
+ if (!collateralBankExists) {
1034
+ activeBanks.push(collateralBankPk);
1035
+ }
1036
+ const activeBankData = await this.clendClient.getBanks(activeBanks);
1037
+ const remainingAccounts = (0, clend_rpc_1.getClendAccountRemainingAccounts)(activeBankData);
1038
+ // 2. Quote swap: debt -> collateral for the borrowed amount
1039
+ const swapQuote = await this.swapper.getQuote(vault, {
1040
+ payer: vaultManager,
1041
+ inputMint: debtMint,
1042
+ inputMintDecimals: debtDecimals,
1043
+ outputMint: collateralMint,
1044
+ outputMintDecimals: collateralDecimals,
1045
+ inputAmount: borrowAmount,
1046
+ slippageBps,
1047
+ swapMode: "ExactIn",
1048
+ });
1049
+ const swapIxns = await this.swapper.getSwapIxns(swapQuote, user);
1050
+ const additionalCollateralFromSwap = swapQuote.outAmount;
1051
+ const totalCollateralToDeposit = depositAmount.add(additionalCollateralFromSwap);
1052
+ // 3. Token programs
1053
+ const { tokenProgram: collateralTokenProgram } = await (0, state_1.getMintState)(collateralMint, this.connection);
1054
+ const { tokenProgram: debtTokenProgram } = await (0, state_1.getMintState)(debtMint, this.connection);
1055
+ const userSession = (0, addresses_1.getUserSessionPda)(vault, user);
1056
+ // 4. Borrow & Deposit instructions (vault-aware)
1057
+ const borrowIxns = await this.program.clendAccountBorrow(vault, vaultManager, clendGroup, clendAccount, debtMint, debtAssetState.oracle.address, debtTokenProgram, userSession, borrowAmount, remainingAccounts);
1058
+ const collateralBankData = activeBankData.find((b) => b.mint.equals(collateralMint));
1059
+ if (!collateralBankData) {
1060
+ throw new Error(`Collateral bank not found: ${collateralMint.toString()}`);
1061
+ }
1062
+ const depositIxns = await this.program.clendAccountDeposit(vault, vaultManager, clendGroup, clendAccount, collateralMint, collateralTokenProgram, totalCollateralToDeposit, collateralAssetState.reserve.address, remainingAccounts);
1063
+ // 5. Assemble inner ixns (no flash loan yet)
1064
+ const ixnsWithoutFlashLoan = [
1065
+ ...borrowIxns,
1066
+ ...swapIxns.ixns,
1067
+ ...depositIxns,
1068
+ ];
1069
+ // 6. Flash loan wrapping (same pattern as withdraw helpers)
1070
+ const endIndex = (0, utils_1.parseBN)(1 + // begin flash loan
1071
+ ixnsWithoutFlashLoan.length +
1072
+ additionalIxnCount);
1073
+ const remainingAccountsForFlashLoan = (0, clend_rpc_1.getClendAccountRemainingAccounts)(activeBankData);
1074
+ const { beginFlashLoanIx, endFlashLoanIx } = await this.clendClient.instructions.createFlashLoanInstructions(clendAccount, user, endIndex, remainingAccountsForFlashLoan);
1075
+ const instructions = [
1076
+ beginFlashLoanIx,
1077
+ ...ixnsWithoutFlashLoan,
1078
+ endFlashLoanIx,
1079
+ ];
1080
+ return { ixns: instructions, luts: swapIxns.luts };
1081
+ }
1082
+ async getDepositLeverageDebtIxns(vault, vaultManager, user, clendGroup, clendAccount, collateralMint, debtMint, collateralDecimals, debtDecimals, initialUserDebtContributionForSwap, // user deposit already in vault
1083
+ finalLoopBorrowAmount, // protocol borrow (from params fn)
1084
+ slippageBps, additionalIxnCount) {
1085
+ const vaultState = await this.getVault(vault);
1086
+ const debtAssetState = vaultState.assets.find((a) => a.mint.address.equals(debtMint));
1087
+ if (!debtAssetState) {
1088
+ throw new Error(`Debt asset not found: ${debtMint.toString()}`);
1089
+ }
1090
+ const collateralAssetState = vaultState.assets.find((a) => a.mint.address.equals(collateralMint));
1091
+ if (!collateralAssetState) {
1092
+ throw new Error(`Collateral asset not found: ${collateralMint.toString()}`);
1093
+ }
1094
+ // 1. Load clend account + banks
1095
+ const clendAccountData = await this.clendClient.getClendAccount(clendAccount);
1096
+ if (!clendAccountData) {
1097
+ throw new Error(`Clend account not found: ${clendAccount.toString()}`);
1098
+ }
1099
+ let activeBanks = (0, clend_rpc_1.getClendAccountActiveBanks)(clendAccountData);
1100
+ // if collateral or debt bank not active, add them
1101
+ const collateralBankPk = (0, clend_rpc_1.getBankPda)(clendGroup, collateralMint);
1102
+ const debtBankPk = (0, clend_rpc_1.getBankPda)(clendGroup, debtMint);
1103
+ const collateralBankExists = activeBanks.some((b) => b.equals(collateralBankPk));
1104
+ if (!collateralBankExists) {
1105
+ activeBanks.push(collateralBankPk);
1106
+ }
1107
+ const debtBankExists = activeBanks.some((b) => b.equals(debtBankPk));
1108
+ if (!debtBankExists) {
1109
+ activeBanks.push(debtBankPk);
1110
+ }
1111
+ const activeBankData = await this.clendClient.getBanks(activeBanks);
1112
+ const remainingAccounts = (0, clend_rpc_1.getClendAccountRemainingAccounts)(activeBankData);
1113
+ // 2. Total debt to swap = user contribution + protocol borrow
1114
+ const totalDebtTokenForSwap = initialUserDebtContributionForSwap.add(finalLoopBorrowAmount);
1115
+ // Jupiter quote: debt -> collateral for TOTAL debt
1116
+ const swapQuote = await this.swapper.getQuote(vault, {
1117
+ payer: vaultManager,
1118
+ inputMint: debtMint,
1119
+ inputMintDecimals: debtDecimals,
1120
+ outputMint: collateralMint,
1121
+ outputMintDecimals: collateralDecimals,
1122
+ inputAmount: totalDebtTokenForSwap,
1123
+ slippageBps,
1124
+ swapMode: "ExactIn",
1125
+ });
1126
+ const swapIxns = await this.swapper.getSwapIxns(swapQuote, user);
1127
+ const optimisticCollateralFromQuote = new anchor_1.BN(swapQuote.outAmount);
1128
+ // 3. Token programs
1129
+ const { tokenProgram: collateralTokenProgram } = await (0, state_1.getMintState)(collateralMint, this.connection);
1130
+ const { tokenProgram: debtTokenProgram } = await (0, state_1.getMintState)(debtMint, this.connection);
1131
+ const userSession = (0, addresses_1.getUserSessionPda)(vault, user);
1132
+ // 4. Borrow & Deposit (vault-aware)
1133
+ const borrowIxns = await this.program.clendAccountBorrow(vault, vaultManager, clendGroup, clendAccount, debtMint, debtAssetState.oracle.address, debtTokenProgram, userSession, finalLoopBorrowAmount, remainingAccounts);
1134
+ const collateralBankData = activeBankData.find((b) => b.mint.equals(collateralMint));
1135
+ if (!collateralBankData) {
1136
+ throw new Error(`Collateral bank not found: ${collateralMint.toString()}`);
1137
+ }
1138
+ const depositIxns = await this.program.clendAccountDeposit(vault, vaultManager, clendGroup, clendAccount, collateralMint, collateralTokenProgram, optimisticCollateralFromQuote, collateralAssetState.reserve.address, remainingAccounts);
1139
+ // 5. Inner ixns (same order as clend-protocol: borrow -> swap -> deposit)
1140
+ const ixnsWithoutFlashLoan = [
1141
+ ...borrowIxns,
1142
+ ...swapIxns.ixns,
1143
+ ...depositIxns,
1144
+ ];
1145
+ // 6. Flash loan wrapping (same pattern as withdraw)
1146
+ const endIndex = (0, utils_1.parseBN)(1 + // begin flash loan
1147
+ ixnsWithoutFlashLoan.length +
1148
+ additionalIxnCount);
1149
+ const remainingAccountsForFlashLoan = (0, clend_rpc_1.getClendAccountRemainingAccounts)(activeBankData);
1150
+ const { beginFlashLoanIx, endFlashLoanIx } = await this.clendClient.instructions.createFlashLoanInstructions(clendAccount, user, endIndex, remainingAccountsForFlashLoan);
1151
+ const instructions = [
1152
+ beginFlashLoanIx,
1153
+ ...ixnsWithoutFlashLoan,
1154
+ endFlashLoanIx,
1155
+ ];
1156
+ return {
1157
+ ixns: instructions,
1158
+ luts: swapIxns.luts,
1159
+ };
1160
+ }
879
1161
  async swap(vault, inputMint, outputMint, inAmount) {
880
- const { state: inputMintState, tokenProgram: inputMintTokenProgram } = await (0, state_1.getMintState)(inputMint, this.connection);
881
- const { state: outputMintState, tokenProgram: outputMintTokenProgram } = await (0, state_1.getMintState)(outputMint, this.connection);
882
- const vaultAssetInReserve = (0, spl_token_1.getAssociatedTokenAddressSync)(inputMint, vault, true, inputMintTokenProgram);
883
- const vaultAssetOutReserve = (0, spl_token_1.getAssociatedTokenAddressSync)(outputMint, vault, true, outputMintTokenProgram);
1162
+ const { state: inputMintState, tokenProgram: _inputMintTokenProgram } = await (0, state_1.getMintState)(inputMint, this.connection);
1163
+ const { state: outputMintState, tokenProgram: _outputMintTokenProgram } = await (0, state_1.getMintState)(outputMint, this.connection);
884
1164
  const quote = await this.swapper.getQuote(vault, {
885
1165
  payer: this.address(),
886
1166
  inputMint: inputMint,
@@ -891,36 +1171,7 @@ class ClendVaultsClient {
891
1171
  swapMode: "ExactIn",
892
1172
  slippageBps: 30,
893
1173
  });
894
- const swapIxns = await this.swapper.getSwapIxns(quote);
895
- //const swapIx = swapIxns.ixns[0];
896
- ////const swapIx = swapIxns.ixns.find((ix) =>
897
- //// ix.programId.equals(JUPITER_SWAP_PROGRAM_ID),
898
- ////);
899
- ////if (!swapIx) {
900
- //// throw new Error(
901
- //// `Swap ix not found in swapIxns, programIds: ${JSON.stringify(swapIxns.ixns.map((ix) => ix.programId.toString()))}`,
902
- //// );
903
- ////}
904
- //const swapRemainingAccounts: web3.AccountMeta[] = swapIx.keys.map(
905
- // (key) => ({
906
- // pubkey: key.pubkey,
907
- // // If the account is the vault PDA, mark it as NOT a signer for the client transaction.
908
- // isSigner: key.pubkey.equals(vault) ? false : key.isSigner,
909
- // isWritable: key.isWritable,
910
- // }),
911
- //);
912
- //const swapData = swapIx.data;
913
- //const ixns = await this.program.swap(
914
- // vault,
915
- // this.address(),
916
- // inputMint,
917
- // outputMint,
918
- // vaultAssetInReserve,
919
- // vaultAssetOutReserve,
920
- // swapData,
921
- // swapRemainingAccounts,
922
- // JUPITER_SWAP_PROGRAM_ID,
923
- //);
1174
+ const swapIxns = await this.swapper.getSwapIxns(quote, null);
924
1175
  const txSig = await this.send(swapIxns.ixns, [], swapIxns.luts);
925
1176
  return txSig;
926
1177
  }
@@ -960,7 +1211,7 @@ class ClendVaultsClient {
960
1211
  // detect if CU budget ixns are needed
961
1212
  const firstIx = ixns[0];
962
1213
  if (!firstIx.programId.equals(web3_js_1.ComputeBudgetProgram.programId)) {
963
- ixns.unshift(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 600000 }));
1214
+ ixns.unshift(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 900000 }));
964
1215
  ixns.unshift(web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1000 }));
965
1216
  }
966
1217
  const { blockhash } = await this.connection.getLatestBlockhash("confirmed");