@carrot-protocol/clend-rpc 0.1.27-group-refactor1-dev-0a2c078 → 0.1.27-swapper1-dev-0f3d67b

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
@@ -33,25 +33,10 @@ class ClendClient {
33
33
  address() {
34
34
  return this.provider.wallet.publicKey;
35
35
  }
36
- // determine type of oracle then fetch price from mint
37
- // TODO: do it better than this hack
38
- async getOraclePrice(mint) {
39
- try {
40
- return await this.getPythOraclePrice(mint);
41
- }
42
- catch (e) {
43
- try {
44
- return await this.getSBOraclePrice(mint);
45
- }
46
- catch (sbError) {
47
- throw sbError;
48
- }
49
- }
50
- }
51
36
  /**
52
37
  * Get pyth oracle price for a token
53
38
  */
54
- async getPythOraclePrice(mint) {
39
+ async getOraclePrice(mint) {
55
40
  const oracle = this.getOracle(mint);
56
41
  // get oracle account info
57
42
  const oracleAccountInfo = await this.connection.getAccountInfo(oracle, {
@@ -70,34 +55,10 @@ class ClendClient {
70
55
  // get ema price
71
56
  const emaPrice = new anchor_1.BN(oracleData.priceMessage.emaPrice);
72
57
  // return price
73
- return (0, utils_1.calculatePriceUi)(emaPrice.toNumber(), oracleData.priceMessage.exponent);
58
+ return (emaPrice.toNumber() *
59
+ Math.pow(10, Number(oracleData.priceMessage.exponent)));
74
60
  }
75
- async getSBOraclePrice(mint) {
76
- const oracle = this.getOracle(mint);
77
- // Fetch the raw account info.
78
- const feedAccountInfo = await this.connection.getAccountInfo(oracle, "processed");
79
- if (feedAccountInfo === null) {
80
- throw new Error(`Feed account ${oracle.toString()} not found.`);
81
- }
82
- // Use our manual parser instead of the Anchor coder.
83
- const pullFeedAccountData = (0, utils_1.parsePullFeedAccountData)(feedAccountInfo.data);
84
- if (!pullFeedAccountData ||
85
- !pullFeedAccountData.submissions ||
86
- pullFeedAccountData.submissions.length === 0) {
87
- throw new Error(`No valid submissions found for feed ${oracle.toString()}.`);
88
- }
89
- // The latest submission will be the first element after sorting.
90
- const latestSubmission = pullFeedAccountData.submissions[0];
91
- // The 'value' is a BN (BigNumber), so we convert it to a standard JavaScript number.
92
- // Be aware of potential precision loss if the number is larger than Number.MAX_SAFE_INTEGER.
93
- const price = Number(latestSubmission.value);
94
- // The exponent for Switchboard v2/v3 feeds is typically -18, but you should
95
- // confirm this for the specific feed you are using.
96
- const exponent = -18;
97
- const priceDecimal = (0, utils_1.calculatePriceUi)(price, exponent);
98
- return priceDecimal;
99
- }
100
- // find oracle for a mint from in memory cache
61
+ // find oracle for a mint
101
62
  getOracle(mint) {
102
63
  const oracleStr = this.mintOracleMapping.get(mint.toString());
103
64
  if (!oracleStr) {
@@ -197,73 +158,14 @@ class ClendClient {
197
158
  return this.send([initIx]);
198
159
  }
199
160
  // Bank Operations
200
- async addBank(clendGroup, bankMint, oracleSetupArgs, bankConfig, interestRateConfig) {
161
+ async addBank(clendGroup, bankMint, pythOracle, pythOracleFeedId, bankConfig, interestRateConfig) {
201
162
  const tokenProgram = (0, utils_1.getTokenProgramForMint)(bankMint);
202
163
  const feeStateAccountData = await this.getFeeState();
203
164
  const globalFeeWallet = feeStateAccountData.globalFeeWallet;
204
- const { bank, ixns } = await this.instructions.addBank(clendGroup, this.address(), globalFeeWallet, bankMint, tokenProgram, oracleSetupArgs, bankConfig, interestRateConfig);
165
+ const { bank, ixns } = await this.instructions.addBank(clendGroup, this.address(), globalFeeWallet, bankMint, tokenProgram, pythOracle, pythOracleFeedId, bankConfig, interestRateConfig);
205
166
  const txSig = await this.send(ixns);
206
167
  return { bank, txSig };
207
168
  }
208
- async setupBankEmissions(bank, emissionsMint, emissionsMode, emissionsRate, totalEmissions) {
209
- // some sanity checks
210
- if (totalEmissions.lte(new anchor_1.BN(0))) {
211
- throw new Error("totalEmissions must be greater than 0");
212
- }
213
- if (emissionsRate.lte(new anchor_1.BN(0))) {
214
- throw new Error("emissionsRate must be greater than 0");
215
- }
216
- if (emissionsRate.gt(totalEmissions)) {
217
- throw new Error("emissionsRate must be less than totalEmissions");
218
- }
219
- const bankData = await this.getBank(bank);
220
- if (!bankData.emissionsMint.equals(anchor_1.web3.PublicKey.default)) {
221
- throw new Error("bank already has emissions mint set");
222
- }
223
- // fetch token program from chain
224
- const emissionsMintTokenProgram = await (0, utils_1.getTokenProgramForMintFromRpc)(this.connection, emissionsMint);
225
- const ixns = await this.instructions.setupBankEmissions(bankData.group, this.address(), bank, emissionsMint, emissionsMintTokenProgram, emissionsMode, emissionsRate, totalEmissions);
226
- const txSig = await this.send(ixns);
227
- return txSig;
228
- }
229
- async updateBankEmissions(bank, emissionsMode, emissionsRate, additionalEmissions) {
230
- // some sanity checks
231
- if (additionalEmissions !== null && additionalEmissions.lte(new anchor_1.BN(0))) {
232
- throw new Error("additionalEmissions must be greater than 0");
233
- }
234
- if (emissionsRate !== null && emissionsRate.lte(new anchor_1.BN(0))) {
235
- throw new Error("emissionsRate must be greater than 0");
236
- }
237
- if (emissionsRate !== null &&
238
- additionalEmissions !== null &&
239
- emissionsRate.gt(additionalEmissions)) {
240
- throw new Error("emissionsRate must be less than additionalEmissions");
241
- }
242
- const bankData = await this.getBank(bank);
243
- // with updateBankEmissions you cannot change the emissions mint
244
- const emissionsMint = bankData.emissionsMint;
245
- if (emissionsMint.equals(anchor_1.web3.PublicKey.default)) {
246
- throw new Error("bank does not have emissions mint set, must call setupBankEmissions first");
247
- }
248
- const emissionsMintTokenProgram = await (0, utils_1.getTokenProgramForMintFromRpc)(this.connection, emissionsMint);
249
- const ix = await this.instructions.updateBankEmissions(bankData.group, this.address(), bank, emissionsMint, emissionsMintTokenProgram, emissionsMode, emissionsRate, additionalEmissions);
250
- const txSig = await this.send([ix]);
251
- return txSig;
252
- }
253
- async withdrawEmissions(clendAccount, bank) {
254
- const bankData = await this.getBank(bank);
255
- const emissionsMint = bankData.emissionsMint;
256
- const mintData = await this.connection.getAccountInfo(emissionsMint, {
257
- commitment: "processed",
258
- });
259
- if (mintData === null) {
260
- throw new Error(`emissions mint not found: ${emissionsMint.toString()}`);
261
- }
262
- const emissionsMintTokenProgram = mintData.owner;
263
- const ixns = await this.instructions.withdrawEmissions(bankData.group, clendAccount, this.address(), bank, emissionsMint, emissionsMintTokenProgram);
264
- const txSig = await this.send(ixns);
265
- return txSig;
266
- }
267
169
  // Account fetching Operations
268
170
  async getClendGroup(clendGroup) {
269
171
  const accountInfo = await this.connection.getAccountInfo(clendGroup, {
@@ -338,9 +240,9 @@ class ClendClient {
338
240
  collectedGroupFeesOutstanding: Number((0, utils_1.wrappedI80F48toBigNumber)(data.collectedGroupFeesOutstanding)),
339
241
  lastUpdate: new anchor_1.BN(data.lastUpdate),
340
242
  config: bankConfig,
341
- flags: (0, state_1.parseBankFlags)(new anchor_1.BN(data.flags)),
243
+ flags: new anchor_1.BN(data.flags),
342
244
  emissionsRate: new anchor_1.BN(data.emissionsRate),
343
- emissionsRemaining: new anchor_1.BN((0, utils_1.wrappedI80F48toBigNumber)(data.emissionsRemaining)),
245
+ emissionsRemaining: (0, utils_1.wrappedI80F48toBigNumber)(data.emissionsRemaining),
344
246
  emissionsMint: new anchor_1.web3.PublicKey(data.emissionsMint),
345
247
  collectedProgramFeesOutstanding: Number((0, utils_1.wrappedI80F48toBigNumber)(data.collectedProgramFeesOutstanding)),
346
248
  assetAmount,
@@ -393,29 +295,21 @@ class ClendClient {
393
295
  const bankData = await this.getBank(bank);
394
296
  const bankMint = bankData.mint;
395
297
  const price = await this.getOraclePrice(bankMint);
396
- const assetShares = Number((0, utils_1.wrappedI80F48toBigNumber)(balance.assetShares));
397
- const liabilityShares = Number((0, utils_1.wrappedI80F48toBigNumber)(balance.liabilityShares));
398
- const lastUpdate = new anchor_1.BN(balance.lastUpdate);
399
- const emissionsOutstanding = new anchor_1.BN((0, utils_1.wrappedI80F48toBigNumber)(balance.emissionsOutstanding));
400
- // calculate oustanding emissions
401
- const emissionsOutstandingAndUnclaimed = (0, utils_1.calculateUnclaimedEmissions)(new anchor_1.BN(assetShares), new anchor_1.BN(liabilityShares), lastUpdate, emissionsOutstanding, bankData.flags, bankData.assetShareValue, bankData.liabilityShareValue, bankData.emissionsRate, bankData.emissionsRemaining, bankData.mintDecimals);
402
298
  const lendingAccountBalance = (0, state_1.newClendAccountBalance)({
403
299
  active,
404
300
  bankPk: bank,
405
301
  bankMint,
406
302
  bankAssetTag: Number(balance.bankAssetTag),
407
- assetShares,
303
+ assetShares: Number((0, utils_1.wrappedI80F48toBigNumber)(balance.assetShares)),
408
304
  assetShareValue: Number(bankData.assetShareValue),
409
305
  assetMintDecimals: Number(bankData.mintDecimals),
410
- liabilityShares,
306
+ liabilityShares: Number((0, utils_1.wrappedI80F48toBigNumber)(balance.liabilityShares)),
411
307
  liabilityShareValue: Number(bankData.liabilityShareValue),
412
308
  liabilityMintDecimals: Number(bankData.mintDecimals),
413
309
  lastUpdate: new anchor_1.BN(balance.lastUpdate),
414
310
  assetWeightMaint: Number(bankData.config.assetWeightMaint),
415
311
  liabilityWeightMaint: Number(bankData.config.liabilityWeightMaint),
416
312
  price,
417
- emissionsOutstanding,
418
- emissionsOutstandingAndUnclaimed,
419
313
  });
420
314
  balances.push(lendingAccountBalance);
421
315
  healthFactorWeightedAssetArgs.push(lendingAccountBalance.assetWeightedMaintValue);
@@ -501,6 +395,13 @@ class ClendClient {
501
395
  // get required remaining accounts based on account balance
502
396
  const clendAccountData = await this.getClendAccount(clendAccount);
503
397
  let clendAccountActiveBanks = (0, utils_1.getClendAccountActiveBanks)(clendAccountData);
398
+ //// If withdrawAll is true, remove the target bank from activeBanks
399
+ //// as we wont have a remaining balance after the withdraw operation
400
+ //if (withdrawAll === true) {
401
+ // clendAccountActiveBanks = clendAccountActiveBanks.filter(
402
+ // (b) => !b.equals(bank),
403
+ // );
404
+ //}
504
405
  // fetch all active banks
505
406
  const activeBankData = [];
506
407
  for (const bank of clendAccountActiveBanks) {
@@ -535,7 +436,7 @@ class ClendClient {
535
436
  const ix = await this.instructions.borrow(clendGroup, clendAccount, this.address(), bank, destinationTokenAccount, tokenProgram, amount, remainingAccounts);
536
437
  return this.send([ix]);
537
438
  }
538
- async repay(clendGroup, clendAccount, mint, amount, repayAll, repayUpToAmount) {
439
+ async repay(clendGroup, clendAccount, mint, amount, repayAll = null) {
539
440
  const bank = (0, addresses_1.getBankPda)(clendGroup, mint);
540
441
  // Get bank data to access the liquidityVault and mint
541
442
  const bankData = await this.getBank(bank);
@@ -551,7 +452,7 @@ class ClendClient {
551
452
  activeBankData.push(bankData);
552
453
  }
553
454
  const remainingAccounts = (0, utils_1.getClendAccountRemainingAccounts)(activeBankData);
554
- const ix = await this.instructions.repay(clendGroup, clendAccount, this.address(), bank, userTokenAccount, tokenProgram, amount, repayAll, repayUpToAmount, remainingAccounts);
455
+ const ix = await this.instructions.repay(clendGroup, clendAccount, this.address(), bank, userTokenAccount, tokenProgram, amount, repayAll, remainingAccounts);
555
456
  return this.send([ix]);
556
457
  }
557
458
  async editGlobalFeeState(clendGroup, newGlobalFeeWallet, newBankInitFlatSolFee, newProgramFeeFixed, newProgramFeeRate) {
@@ -592,10 +493,9 @@ class ClendClient {
592
493
  const ix = await this.instructions.resetBankFees(bankData.group, this.address(), bank);
593
494
  return this.send([ix]);
594
495
  }
595
- async liquidateClendAccountWithFlashLoan(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps, marginfiLiabFlashLoanBank, liquidatorMarginfiAccount, additionalIxnCount, // used to calculate the end flash loan index
596
- swapperOverride) {
496
+ async liquidateClendAccountWithFlashLoan(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps, marginfiLiabFlashLoanBank, liquidatorMarginfiAccount, additionalIxnCount) {
597
497
  // create instructions
598
- const result = await this.getLiquidateClendAccountIxns(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps, swapperOverride);
498
+ const result = await this.getLiquidateClendAccountIxns(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps);
599
499
  if (!result) {
600
500
  return;
601
501
  }
@@ -628,8 +528,8 @@ class ClendClient {
628
528
  ];
629
529
  return this.send(ixns, [], luts);
630
530
  }
631
- async liquidateClendAccount(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps, swapperOverride) {
632
- const result = await this.getLiquidateClendAccountIxns(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps, swapperOverride);
531
+ async liquidateClendAccount(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps) {
532
+ const result = await this.getLiquidateClendAccountIxns(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps);
633
533
  if (!result) {
634
534
  return;
635
535
  }
@@ -654,12 +554,7 @@ class ClendClient {
654
554
  * Must be <= 0.
655
555
  * returns: instructions, luts, deposit amount (libaility token)
656
556
  */
657
- async getLiquidateClendAccountIxns(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps, swapperOverride) {
658
- let activeSwapper = this.swapper;
659
- // override swapper if provided
660
- if (swapperOverride) {
661
- activeSwapper = swapperOverride;
662
- }
557
+ async getLiquidateClendAccountIxns(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps) {
663
558
  utils_1.logger.info("Starting liquidation calculation", {
664
559
  liquidatee: liquidateeClendAccount.authority.toString(),
665
560
  liquidator: liquidatorClendAccount.authority.toString(),
@@ -779,8 +674,7 @@ class ClendClient {
779
674
  // balance for the asset
780
675
  const withdrawAssetIx = await this.instructions.withdraw(assetBankData.group, liquidatorClendAccount.key, liquidatorClendAccount.authority, assetBankData.key, liquidatorAssetAta, assetTokenProgram, finalAssetAmount, false, remainingAccounts);
781
676
  // get a swap quote
782
- const swapQuote = await activeSwapper.getQuote({
783
- payer: liquidatorClendAccount.authority,
677
+ const swapQuote = await this.swapper.getQuote({
784
678
  inputMint: assetBankData.mint,
785
679
  inputMintDecimals: assetBankData.mintDecimals,
786
680
  outputMint: liabBankData.mint,
@@ -796,7 +690,7 @@ class ClendClient {
796
690
  outAmountMin: outAmountMin.toString(10),
797
691
  });
798
692
  // 5. Get Swap Instructions from Jupiter
799
- const swapIxns = await activeSwapper.getSwapIxns(swapQuote);
693
+ const swapIxns = await this.swapper.getSwapIxns(this.address(), swapQuote);
800
694
  const ixns = [
801
695
  depositIx, // deposit liability mint into liquidator clend account
802
696
  liquidationIx, // liquidate liquidatee clend account
@@ -835,35 +729,30 @@ class ClendClient {
835
729
  const ix = await this.instructions.configureBankOracleMaxAge(clendGroup, this.address(), bank, maxAge);
836
730
  return this.send([ix]);
837
731
  }
838
- async configureBankPythOracle(bank, pythOracle, pythOracleFeed) {
732
+ async configureBankOracle(bank, pythOracle, pythOracleFeed) {
839
733
  const bankData = await this.getBank(bank);
840
- const ix = await this.instructions.configureBankPythOracle(bankData.group, this.address(), bank, pythOracle, pythOracleFeed);
841
- return this.send([ix]);
842
- }
843
- async configureBankSBOracle(bank, pullFeedAccount) {
844
- const bankData = await this.getBank(bank);
845
- const ix = await this.instructions.configureBankSwitchboardOracle(bankData.group, this.address(), bank, pullFeedAccount);
734
+ const ix = await this.instructions.configureBankOracle(bankData.group, this.address(), bank, pythOracle, pythOracleFeed);
846
735
  return this.send([ix]);
847
736
  }
848
737
  async setBankOperationalState(clendGroup, bank, operationalState) {
849
738
  const ix = await this.instructions.setBankOperationalState(clendGroup, this.address(), bank, operationalState);
850
739
  return this.send([ix]);
851
740
  }
852
- async depositLeverage(clendGroup, clendAccount, selectedMint, collateralMint, debtMint, depositAmount, targetLeverage, slippageBps, swapperOverride) {
741
+ async depositLeverage(clendGroup, clendAccount, selectedMint, collateralMint, debtMint, depositAmount, targetLeverage, slippageBps) {
853
742
  let ixns = [];
854
743
  let luts = [];
855
744
  let additionalSigners = [];
856
745
  switch (selectedMint.toString()) {
857
746
  case collateralMint.toString():
858
747
  const collateralParams = await this.getDepositLeverageFromCollateralParams(clendGroup, collateralMint, debtMint, depositAmount, targetLeverage, slippageBps);
859
- const { ixns: collateralIxns, luts: collateralLuts, additionalSigners: collateralAdditionalSigners, } = await this.getDepositLeverageFromCollateralIxns(clendGroup, this.address(), clendAccount, collateralMint, debtMint, collateralParams.collateralBankData.mintDecimals, collateralParams.debtBankData.mintDecimals, collateralParams.borrowAmount, depositAmount, slippageBps, 0, swapperOverride);
748
+ const { ixns: collateralIxns, luts: collateralLuts, additionalSigners: collateralAdditionalSigners, } = await this.getDepositLeverageFromCollateralIxns(clendGroup, this.address(), clendAccount, collateralMint, debtMint, collateralParams.collateralBankData.mintDecimals, collateralParams.debtBankData.mintDecimals, collateralParams.borrowAmount, depositAmount, slippageBps, 0);
860
749
  ixns = collateralIxns;
861
750
  luts = collateralLuts;
862
751
  additionalSigners = collateralAdditionalSigners;
863
752
  break;
864
753
  case debtMint.toString():
865
- const debtParams = await this.getDepositLeverageFromDebtParams(clendGroup, collateralMint, debtMint, depositAmount, targetLeverage, slippageBps, swapperOverride);
866
- const { ixns: debtIxns, luts: debtLuts, additionalSigners: debtAdditionalSigners, } = await this.getDepositLeverageFromDebtIxns(clendGroup, this.address(), clendAccount, collateralMint, debtMint, debtParams.debtBankData.mintDecimals, debtParams.collateralBankData.mintDecimals, depositAmount, debtParams.borrowAmount, slippageBps, 0, swapperOverride);
754
+ const debtParams = await this.getDepositLeverageFromDebtParams(clendGroup, collateralMint, debtMint, depositAmount, targetLeverage, slippageBps);
755
+ const { ixns: debtIxns, luts: debtLuts, additionalSigners: debtAdditionalSigners, } = await this.getDepositLeverageFromDebtIxns(clendGroup, this.address(), clendAccount, collateralMint, debtMint, debtParams.debtBankData.mintDecimals, debtParams.collateralBankData.mintDecimals, depositAmount, debtParams.borrowAmount, slippageBps, 0);
867
756
  ixns = debtIxns;
868
757
  luts = debtLuts;
869
758
  additionalSigners = debtAdditionalSigners;
@@ -875,13 +764,7 @@ class ClendClient {
875
764
  }
876
765
  async getDepositLeverageFromDebtIxns(clendGroup, user, clendAccount, collateralMint, // e.g., JLP
877
766
  debtMint, // e.g., USDC
878
- debtMintDecimals, collateralMintDecimals, initialUserDebtContributionForSwap, finalLoopBorrowAmount, slippageBps, additionalIxnCount, // For JIT liquidity, etc.
879
- swapperOverride) {
880
- // override swapper if provided
881
- let activeSwapper = this.swapper;
882
- if (swapperOverride) {
883
- activeSwapper = swapperOverride;
884
- }
767
+ debtMintDecimals, collateralMintDecimals, initialUserDebtContributionForSwap, finalLoopBorrowAmount, slippageBps, additionalIxnCount) {
885
768
  const collateralTokenProgram = (0, utils_1.getTokenProgramForMint)(collateralMint);
886
769
  const debtTokenProgram = (0, utils_1.getTokenProgramForMint)(debtMint);
887
770
  const debtBank = (0, addresses_1.getBankPda)(clendGroup, debtMint);
@@ -921,8 +804,7 @@ class ClendClient {
921
804
  const totalDebtTokenForSwap = initialUserDebtContributionForSwap.add(finalLoopBorrowAmount);
922
805
  // 3. Get Jupiter Quote for the TOTAL debt token amount
923
806
  const swapMode = "ExactIn";
924
- const swapQuote = await activeSwapper.getQuote({
925
- payer: user,
807
+ const swapQuote = await this.swapper.getQuote({
926
808
  inputMint: debtMint,
927
809
  inputMintDecimals: debtMintDecimals,
928
810
  outputMint: collateralMint,
@@ -944,7 +826,7 @@ class ClendClient {
944
826
  const userDebtAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtMint, user, true, debtTokenProgram);
945
827
  const userCollateralAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collateralMint, user, true, collateralTokenProgram);
946
828
  // 5. Get Swap Instructions from Jupiter
947
- const swapIxns = await activeSwapper.getSwapIxns(swapQuote);
829
+ const swapIxns = await this.swapper.getSwapIxns(user, swapQuote);
948
830
  // 6. Create Clend Protocol Instructions
949
831
  const borrowIx = await this.instructions.borrow(clendGroup, clendAccount, // clendAccount is guaranteed to be non-null here
950
832
  user, debtBank, userDebtAta, // Borrow into user's ATA
@@ -990,13 +872,7 @@ class ClendClient {
990
872
  additionalSigners,
991
873
  };
992
874
  }
993
- async getDepositLeverageFromCollateralIxns(clendGroup, user, clendAccount, collateralMint, debtMint, debtMintDecimals, collateralMintDecimals, borrowAmount, depositAmount, slippageBps, additionalIxnCount, // used to correctly offset the flash loan index
994
- swapperOverride) {
995
- // override swapper if provided
996
- let activeSwapper = this.swapper;
997
- if (swapperOverride) {
998
- activeSwapper = swapperOverride;
999
- }
875
+ async getDepositLeverageFromCollateralIxns(clendGroup, user, clendAccount, collateralMint, debtMint, debtMintDecimals, collateralMintDecimals, borrowAmount, depositAmount, slippageBps, additionalIxnCount) {
1000
876
  // Get token programs
1001
877
  const collateralTokenProgram = (0, utils_1.getTokenProgramForMint)(collateralMint);
1002
878
  const debtTokenProgram = (0, utils_1.getTokenProgramForMint)(debtMint);
@@ -1004,8 +880,7 @@ class ClendClient {
1004
880
  const collateralBank = (0, addresses_1.getBankPda)(clendGroup, collateralMint);
1005
881
  // Get Jupiter quote for swapping debt token to collateral token
1006
882
  const swapMode = "ExactIn";
1007
- const swapQuote = await activeSwapper.getQuote({
1008
- payer: user,
883
+ const swapQuote = await this.swapper.getQuote({
1009
884
  inputMint: debtMint,
1010
885
  inputMintDecimals: debtMintDecimals,
1011
886
  outputMint: collateralMint,
@@ -1056,7 +931,7 @@ class ClendClient {
1056
931
  }
1057
932
  const remainingAccounts = (0, utils_1.getClendAccountRemainingAccounts)(activeBankData);
1058
933
  // Get swap instructions
1059
- const swapIxns = await activeSwapper.getSwapIxns(swapQuote);
934
+ const swapIxns = await this.swapper.getSwapIxns(user, swapQuote);
1060
935
  // Calculate additional collateral from swap
1061
936
  const additionalCollateralAmount = swapQuote.outAmount;
1062
937
  const totalCollateralAmount = depositAmount.add(additionalCollateralAmount);
@@ -1172,12 +1047,7 @@ class ClendClient {
1172
1047
  async getDepositLeverageFromDebtParams(clendGroup, collateralMint, // The asset being leveraged (e.g., JLP)
1173
1048
  debtMint, // The asset provided initially by the user (e.g., USDC)
1174
1049
  initialUserDebtContributionForSwap, // User's initial DEBT token for the swap
1175
- targetLeverage, slippageBps, swapperOverride) {
1176
- // override swapper if provided
1177
- let activeSwapper = this.swapper;
1178
- if (swapperOverride) {
1179
- activeSwapper = swapperOverride;
1180
- }
1050
+ targetLeverage, slippageBps) {
1181
1051
  // 1. Get decimals and bank data
1182
1052
  const collateralDecimals = (0, utils_1.getTokenDecimalsForMint)(collateralMint);
1183
1053
  const debtDecimals = (0, utils_1.getTokenDecimalsForMint)(debtMint);
@@ -1208,8 +1078,7 @@ class ClendClient {
1208
1078
  // This is the user's initial contribution + what's borrowed from the protocol
1209
1079
  const totalDebtTokenForSwap = initialUserDebtContributionForSwap.add(finalLoopBorrowAmount);
1210
1080
  // 7. Calculate Expected COLLATERAL Output from swapping the total DEBT token amount
1211
- const swapQuote = await activeSwapper.getQuote({
1212
- payer: anchor_1.web3.PublicKey.default, // doesnt matter right now
1081
+ const swapQuote = await this.swapper.getQuote({
1213
1082
  inputMint: debtMint,
1214
1083
  inputMintDecimals: debtDecimals,
1215
1084
  outputMint: collateralMint,
@@ -1248,19 +1117,19 @@ class ClendClient {
1248
1117
  collateralBankData,
1249
1118
  };
1250
1119
  }
1251
- async withdrawLeverage(clendGroup, clendAccount, selectedMint, collateralMint, debtMint, withdrawAmount, withdrawAll, slippageBps, swapperOverride) {
1120
+ async withdrawLeverage(clendGroup, clendAccount, selectedMint, collateralMint, debtMint, withdrawAmount, withdrawAll, slippageBps) {
1252
1121
  let ixns = [];
1253
1122
  let luts = [];
1254
1123
  switch (selectedMint.toString()) {
1255
1124
  case collateralMint.toString():
1256
- const collateralParams = await this.getNetWithdrawLeverageCollateralParams(clendGroup, clendAccount, collateralMint, debtMint, withdrawAmount, withdrawAll);
1257
- const { ixns: collateralIxns, luts: collateralLuts } = await this.getWithdrawLeverageCollateralIxns(clendGroup, clendAccount, collateralMint, debtMint, collateralParams.collateralBankData.mintDecimals, collateralParams.debtBankData.mintDecimals, withdrawAll, collateralParams.collateralToWithdraw, withdrawAmount, slippageBps, 0, swapperOverride);
1125
+ const collateralParams = await this.getNetWithdrawLeverageCollateralParams(clendGroup, clendAccount, collateralMint, debtMint, withdrawAmount, withdrawAll, slippageBps);
1126
+ const { ixns: collateralIxns, luts: collateralLuts } = await this.getWithdrawLeverageCollateralIxns(clendGroup, clendAccount, collateralMint, debtMint, collateralParams.collateralBankData.mintDecimals, collateralParams.debtBankData.mintDecimals, withdrawAll, collateralParams.debtToRepay, collateralParams.collateralToWithdraw, slippageBps, 0);
1258
1127
  ixns = collateralIxns;
1259
1128
  luts = collateralLuts;
1260
1129
  break;
1261
1130
  case debtMint.toString():
1262
- const debtParams = await this.getNetWithdrawLeverageDebtParams(clendGroup, clendAccount, collateralMint, debtMint, withdrawAmount, withdrawAll, swapperOverride);
1263
- const { ixns: debtIxns, luts: debtLuts } = await this.getWithdrawLeverageDebtIxns(clendGroup, clendAccount, collateralMint, debtMint, debtParams.collateralBankData.mintDecimals, debtParams.debtBankData.mintDecimals, withdrawAll, debtParams.debtToRepay, debtParams.collateralToWithdraw, slippageBps, 0, swapperOverride);
1131
+ const debtParams = await this.getNetWithdrawLeverageDebtParams(clendGroup, clendAccount, collateralMint, debtMint, withdrawAmount, withdrawAll, slippageBps);
1132
+ const { ixns: debtIxns, luts: debtLuts } = await this.getWithdrawLeverageDebtIxns(clendGroup, clendAccount, collateralMint, debtMint, debtParams.collateralBankData.mintDecimals, debtParams.debtBankData.mintDecimals, withdrawAll, debtParams.debtToRepay, debtParams.collateralToWithdraw, withdrawAmount, slippageBps, 0);
1264
1133
  ixns = debtIxns;
1265
1134
  luts = debtLuts;
1266
1135
  break;
@@ -1270,98 +1139,71 @@ class ClendClient {
1270
1139
  // Send transaction
1271
1140
  return this.send(ixns, [], luts);
1272
1141
  }
1273
- async getWithdrawLeverageDebtIxns(clendGroup, clendAccount, collateralMint, debtMint, collateralDecimals, debtDecimals, withdrawAll, debtToRepay, collateralToWithdraw, slippageBps, additionalIxnCount, swapperOverride) {
1274
- // override swapper if provided
1275
- let activeSwapper = this.swapper;
1276
- if (swapperOverride) {
1277
- activeSwapper = swapperOverride;
1278
- }
1142
+ async getWithdrawLeverageCollateralIxns(clendGroup, clendAccount, collateralMint, debtMint, collateralDecimals, debtDecimals, withdrawAll, debtToRepay, collateralToWithdraw, slippageBps, additionalIxnCount) {
1279
1143
  const debtBank = (0, addresses_1.getBankPda)(clendGroup, debtMint);
1280
1144
  const collateralBank = (0, addresses_1.getBankPda)(clendGroup, collateralMint);
1145
+ // Get remaining accounts for flash loan
1281
1146
  const clendAccountData = await this.getClendAccount(clendAccount);
1282
1147
  if (!clendAccountData) {
1283
1148
  throw new Error(`Clend account not found: ${clendAccount.toString()}`);
1284
1149
  }
1285
1150
  let activeBanks = (0, utils_1.getClendAccountActiveBanks)(clendAccountData);
1286
- const activeBankData = await Promise.all(activeBanks.map((bankPk) => this.getBank(bankPk)));
1151
+ const activeBankData = [];
1152
+ for (const bank of activeBanks) {
1153
+ const bankData = await this.getBank(bank);
1154
+ activeBankData.push(bankData);
1155
+ }
1287
1156
  const remainingAccounts = (0, utils_1.getClendAccountRemainingAccounts)(activeBankData);
1288
- // Get a fresh 'ExactIn' quote for swapping the total collateral to withdraw
1289
- const swapQuote = await activeSwapper.getQuote({
1290
- payer: clendAccountData.authority,
1157
+ // Get Jupiter quote for swapping collateral to debt token (ExactOut mode)
1158
+ const swapMode = "ExactOut";
1159
+ const swapQuote = await this.swapper.getQuote({
1291
1160
  inputMint: collateralMint,
1292
1161
  inputMintDecimals: collateralDecimals,
1293
1162
  outputMint: debtMint,
1294
1163
  outputMintDecimals: debtDecimals,
1295
- inputAmount: collateralToWithdraw, // Use the total collateral to withdraw as the exact input
1164
+ inputAmount: debtToRepay,
1296
1165
  slippageBps,
1297
- swapMode: "ExactIn",
1166
+ swapMode,
1298
1167
  });
1299
- // The minimum amount of debt token we are guaranteed to receive from the swap
1300
- const minDebtReceivedFromSwap = new anchor_1.BN(swapQuote.otherAmountThreshold);
1301
- // Determine the final amount to repay. It's the lesser of what we need and what we're guaranteed to get.
1302
- const finalDebtToRepay = anchor_1.BN.min(debtToRepay, minDebtReceivedFromSwap);
1303
- utils_1.logger.debug("getWithdrawLeverageDebtIxns: swap and repay details", {
1304
- collateralToWithdraw: collateralToWithdraw.toString(),
1305
- minDebtReceivedFromSwap: minDebtReceivedFromSwap.toString(),
1306
- targetDebtToRepay: debtToRepay.toString(),
1307
- finalDebtToRepay: finalDebtToRepay.toString(),
1168
+ // set collateralToSwap to the inAmount from the swap quote
1169
+ const collateralToSwap = swapQuote.inAmount;
1170
+ utils_1.logger.debug("withdrawLeverage collateralToSwap", {
1171
+ collateralToSwap,
1308
1172
  });
1309
- // Get Swap Instructions from the swapper
1310
- const swapIxns = await activeSwapper.getSwapIxns(swapQuote);
1311
- utils_1.logger.info(`swapIxns fetched for ReceiveDebtToken`);
1312
- // --- ATAs and Clend Instructions ---
1173
+ // Get swap instructions
1174
+ const swapInputs = {
1175
+ inputAmountLamports: new decimal_js_1.default(collateralToSwap.toString()),
1176
+ inputMint: collateralMint,
1177
+ outputMint: debtMint,
1178
+ };
1179
+ utils_1.logger.debug("withdrawLeverage swapInputs", {
1180
+ swapInputs,
1181
+ });
1182
+ const swapIxns = await this.swapper.getSwapIxns(clendAccountData.authority, swapQuote);
1183
+ utils_1.logger.info(`swapIxns fetched`);
1313
1184
  const collateralTokenProgram = (0, utils_1.getTokenProgramForMint)(collateralMint);
1314
1185
  const debtTokenProgram = (0, utils_1.getTokenProgramForMint)(debtMint);
1315
1186
  const userCollateralAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collateralMint, clendAccountData.authority, true, collateralTokenProgram);
1316
1187
  const userDebtAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtMint, clendAccountData.authority, true, debtTokenProgram);
1317
- // Create withdraw instruction: Withdraw the total collateral calculated by the params function
1188
+ utils_1.logger.info(`begin crafting ixns`);
1189
+ // Create withdraw instruction
1318
1190
  const withdrawIx = await this.instructions.withdraw(clendGroup, clendAccount, clendAccountData.authority, collateralBank, userCollateralAta, collateralTokenProgram, collateralToWithdraw, withdrawAll, remainingAccounts);
1319
- // if withdrawAll == true we need to make sure we withdraw emissions from both banks before withdrawing all the bank tokens
1320
- const emissionsIxns = [];
1321
- if (withdrawAll) {
1322
- // get bank data
1323
- const collateralBankData = activeBankData.find((b) => b.mint.equals(collateralMint));
1324
- if (!collateralBankData) {
1325
- throw new Error(`Collateral bank not found: ${collateralMint.toString()}`);
1326
- }
1327
- const debtBankData = activeBankData.find((b) => b.mint.equals(debtMint));
1328
- if (!debtBankData) {
1329
- throw new Error(`Debt bank not found: ${debtMint.toString()}`);
1330
- }
1331
- // collateral bank emissions
1332
- if (!collateralBankData.emissionsMint.equals(anchor_1.web3.PublicKey.default) &&
1333
- (collateralBankData.flags === state_1.BankFlags.LendingEmissionsActive ||
1334
- collateralBankData.flags ===
1335
- state_1.BankFlags.LendingAndBorrowingEmissionsActive)) {
1336
- const emissionsMint = collateralBankData.emissionsMint;
1337
- const emissionsMintTokenProgram = await (0, utils_1.getTokenProgramForMintFromRpc)(this.connection, emissionsMint);
1338
- const withdrawEmissionsIx = await this.instructions.withdrawEmissions(clendGroup, clendAccount, clendAccountData.authority, collateralBank, emissionsMint, emissionsMintTokenProgram);
1339
- emissionsIxns.push(...withdrawEmissionsIx);
1340
- }
1341
- // debt bank emissions
1342
- if (!debtBankData.emissionsMint.equals(anchor_1.web3.PublicKey.default) &&
1343
- (debtBankData.flags === state_1.BankFlags.BorrowEmissionsActive ||
1344
- debtBankData.flags === state_1.BankFlags.LendingAndBorrowingEmissionsActive)) {
1345
- const emissionsMint = debtBankData.emissionsMint;
1346
- const emissionsMintTokenProgram = await (0, utils_1.getTokenProgramForMintFromRpc)(this.connection, emissionsMint);
1347
- const withdrawEmissionsIx = await this.instructions.withdrawEmissions(clendGroup, clendAccount, clendAccountData.authority, debtBank, emissionsMint, emissionsMintTokenProgram);
1348
- emissionsIxns.push(...withdrawEmissionsIx);
1349
- }
1350
- }
1351
- // Create repay instruction: Repay the debt portion using the guaranteed swap output
1352
- const repayIx = await this.instructions.repay(clendGroup, clendAccount, clendAccountData.authority, debtBank, userDebtAta, debtTokenProgram, finalDebtToRepay, // Use the slippage-protected amount from the final quote
1353
- withdrawAll, true, remainingAccounts);
1354
- // Assemble instructions
1191
+ utils_1.logger.info(`withdrawIx set`);
1192
+ // Create repay instruction
1193
+ const repayIx = await this.instructions.repay(clendGroup, clendAccount, clendAccountData.authority, debtBank, userDebtAta, debtTokenProgram, debtToRepay, withdrawAll, remainingAccounts);
1194
+ utils_1.logger.info(`repayIx set`);
1195
+ // Assemble all instructions without flash loan
1355
1196
  const ixnsWithoutFlashLoan = [
1356
- ...emissionsIxns,
1357
1197
  withdrawIx,
1358
1198
  ...swapIxns.ixns,
1359
1199
  repayIx,
1360
1200
  ];
1361
- // Flash Loan Wrapping
1201
+ utils_1.logger.info(`ixnsWithoutFlashLoan set`);
1202
+ // Create flash loan instructions
1362
1203
  const cuIxns = 2;
1363
1204
  const endIndex = new anchor_1.BN(cuIxns + ixnsWithoutFlashLoan.length + 1 + additionalIxnCount);
1364
1205
  const { beginFlashLoanIx, endFlashLoanIx } = await this.instructions.createFlashLoanInstructions(clendAccount, clendAccountData.authority, endIndex, remainingAccounts);
1206
+ // Assemble all instructions in the correct order
1365
1207
  const instructions = [
1366
1208
  beginFlashLoanIx,
1367
1209
  ...ixnsWithoutFlashLoan,
@@ -1369,13 +1211,84 @@ class ClendClient {
1369
1211
  ];
1370
1212
  return { ixns: instructions, luts: swapIxns.luts };
1371
1213
  }
1372
- async getNetWithdrawLeverageCollateralParams(clendGroup, clendAccount, collateralMint, debtMint, desiredNetWithdraw, withdrawAll) {
1373
- utils_1.logger.info("getNetWithdrawLeverageCollateralParams", {
1214
+ async getWithdrawLeverageDebtIxns(clendGroup, clendAccount, collateralMint, debtMint, collateralDecimals, debtDecimals, withdrawAll, debtToRepay, collateralToWithdraw, desiredNetDebtToReceive, slippageBps, additionalIxnCount) {
1215
+ const debtBank = (0, addresses_1.getBankPda)(clendGroup, debtMint);
1216
+ const collateralBank = (0, addresses_1.getBankPda)(clendGroup, collateralMint);
1217
+ const clendAccountData = await this.getClendAccount(clendAccount);
1218
+ if (!clendAccountData) {
1219
+ throw new Error(`Clend account not found: ${clendAccount.toString()}`);
1220
+ }
1221
+ let activeBanks = (0, utils_1.getClendAccountActiveBanks)(clendAccountData);
1222
+ //if (withdrawAll) {
1223
+ // activeBanks = activeBanks.filter(
1224
+ // (b) => !b.equals(collateralBank) && !b.equals(debtBank),
1225
+ // );
1226
+ //}
1227
+ const activeBankData = await Promise.all(activeBanks.map((bankPk) => this.getBank(bankPk)));
1228
+ const remainingAccounts = (0, utils_1.getClendAccountRemainingAccounts)(activeBankData);
1229
+ // 1. Determine Total USDC Needed from the Swap
1230
+ // This is the amount needed to repay Clend's debt AND give the user their desired net withdrawal.
1231
+ const totalDebtToReceive = debtToRepay.add(desiredNetDebtToReceive);
1232
+ const swapMode = "ExactIn";
1233
+ const swapQuote = await this.swapper.getQuote({
1234
+ inputMint: collateralMint,
1235
+ inputMintDecimals: collateralDecimals,
1236
+ outputMint: debtMint,
1237
+ outputMintDecimals: debtDecimals,
1238
+ inputAmount: collateralToWithdraw,
1239
+ slippageBps,
1240
+ swapMode,
1241
+ });
1242
+ const minUsdcReceivedFromSwap_BN = new anchor_1.BN(swapQuote.otherAmountThreshold);
1243
+ utils_1.logger.debug("getWithdrawLeverageDebtIxns: collateral and debt details", {
1244
+ debtToRepay: debtToRepay.toString(),
1245
+ desiredNetDebtToReceive: desiredNetDebtToReceive.toString(),
1246
+ totalDebtToReceive: totalDebtToReceive.toString(),
1247
+ minUsdcReceivedFromSwap_BN: minUsdcReceivedFromSwap_BN.toString(),
1248
+ });
1249
+ // 3. Get Swap Instructions from swapper
1250
+ const swapIxns = await this.swapper.getSwapIxns(clendAccountData.authority, swapQuote);
1251
+ utils_1.logger.info(`swapIxns fetched for ReceiveDebtToken`);
1252
+ // --- ATAs and Clend Instructions (largely similar structure) ---
1253
+ const collateralTokenProgram = (0, utils_1.getTokenProgramForMint)(collateralMint);
1254
+ const debtTokenProgram = (0, utils_1.getTokenProgramForMint)(debtMint);
1255
+ const userCollateralAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collateralMint, clendAccountData.authority, true, collateralTokenProgram);
1256
+ const userDebtAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtMint, clendAccountData.authority, true, debtTokenProgram);
1257
+ // Create withdraw instruction: Withdraw ALL the JLP calculated by params function
1258
+ const withdrawIx = await this.instructions.withdraw(clendGroup, clendAccount, clendAccountData.authority, collateralBank, userCollateralAta, collateralTokenProgram, collateralToWithdraw, // Withdraw all the JLP needed for the full swap
1259
+ withdrawAll, // This flag is for Clend's internal logic
1260
+ remainingAccounts);
1261
+ // Create repay instruction: Repay the calculated debt portion to Clend
1262
+ const repayIx = await this.instructions.repay(clendGroup, clendAccount, clendAccountData.authority, debtBank, userDebtAta, debtTokenProgram, debtToRepay, // Repay the specified debt amount
1263
+ withdrawAll, // This flag is for Clend's internal logic
1264
+ remainingAccounts);
1265
+ // Assemble instructions
1266
+ const ixnsWithoutFlashLoan = [
1267
+ withdrawIx,
1268
+ ...swapIxns.ixns, // Swap all withdrawn collateral to debt
1269
+ repayIx, // Repay the debt portion
1270
+ ]; // User is left with the remaining debt in their ATA
1271
+ // Flash Loan Wrapping (same as before)
1272
+ const cuIxns = 2;
1273
+ const endIndex = new anchor_1.BN(cuIxns + ixnsWithoutFlashLoan.length + 1 + additionalIxnCount);
1274
+ const { beginFlashLoanIx, endFlashLoanIx } = await this.instructions.createFlashLoanInstructions(clendAccount, clendAccountData.authority, endIndex, remainingAccounts);
1275
+ const instructions = [
1276
+ beginFlashLoanIx,
1277
+ ...ixnsWithoutFlashLoan,
1278
+ endFlashLoanIx,
1279
+ ];
1280
+ return { ixns: instructions, luts: swapIxns.luts };
1281
+ }
1282
+ // caller will receive desiredNetWithdraw, the position will be adjusted accordingly
1283
+ // receive the collateral mint
1284
+ async getNetWithdrawLeverageCollateralParams(clendGroup, clendAccount, collateralMint, debtMint, desiredNetWithdraw, withdrawAll, slippageBps) {
1285
+ utils_1.logger.info("getNetWithdrawParams", {
1374
1286
  desiredNetWithdraw: desiredNetWithdraw.toString(),
1375
1287
  withdrawAll,
1376
1288
  });
1289
+ // if desiredNetWithdraw is zero, throw an error
1377
1290
  if (desiredNetWithdraw.isZero() && !withdrawAll) {
1378
- throw new Error("Desired net withdrawal is zero for a partial withdraw.");
1291
+ throw new Error("Desired net withdrawal is zero");
1379
1292
  }
1380
1293
  const collateralBank = (0, addresses_1.getBankPda)(clendGroup, collateralMint);
1381
1294
  const debtBank = (0, addresses_1.getBankPda)(clendGroup, debtMint);
@@ -1387,6 +1300,7 @@ class ClendClient {
1387
1300
  if (!clendAccountData) {
1388
1301
  throw new Error(`Clend account not found: ${clendAccount.toString()}`);
1389
1302
  }
1303
+ // fetch balances and pricing
1390
1304
  let debtBalance;
1391
1305
  let debtPrice;
1392
1306
  let collateralBalance;
@@ -1405,32 +1319,75 @@ class ClendClient {
1405
1319
  throw new Error("Could not find debt or collateral position in account");
1406
1320
  }
1407
1321
  const collateralAmount = collateralBalance.assetBalance;
1408
- const debtAmount = debtBalance.liabilityBalance;
1322
+ const collateralAmountUI = collateralBalance.assetBalanceUi;
1323
+ const collateralValue = collateralBalance.assetValue;
1409
1324
  const collateralDecimals = collateralBankData.mintDecimals;
1325
+ const debtAmount = debtBalance.liabilityBalance;
1326
+ const debtAmountUI = debtBalance.liabilityBalanceUi;
1327
+ const debtValue = debtBalance.liabilityValue;
1410
1328
  const debtDecimals = debtBankData.mintDecimals;
1329
+ const desiredNetWithdrawUi = (0, clend_common_1.amountToUi)(desiredNetWithdraw, debtDecimals);
1330
+ utils_1.logger.debug("Current amounts", {
1331
+ desiredNetWithdrawUi,
1332
+ collateralAmount: collateralAmount.toString(),
1333
+ collateralAmountUI,
1334
+ collateralValue,
1335
+ debtAmount: debtAmount.toString(),
1336
+ debtAmountUI,
1337
+ debtValue,
1338
+ });
1339
+ // --- Determine collateral to withdraw and debt to repay ---
1411
1340
  let collateralToWithdraw;
1412
1341
  let debtToRepay;
1413
1342
  if (withdrawAll) {
1414
1343
  utils_1.logger.info("WithdrawAll selected");
1415
1344
  collateralToWithdraw = collateralAmount;
1416
- debtToRepay = (0, clend_common_1.calculateLiabilityInterest)(debtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), Math.floor(Date.now() / 1000));
1345
+ debtToRepay = (0, clend_common_1.calculateLiabilityInterest)(debtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), new Date().getTime() / 1000);
1346
+ utils_1.logger.info(`calculated debt to repay`, {
1347
+ debtToRepayWithInterest: debtToRepay.toString(),
1348
+ debtToRepayRaw: debtAmount.toString(),
1349
+ });
1417
1350
  }
1418
1351
  else {
1419
- utils_1.logger.info("Calculating partial deleverage amounts");
1352
+ utils_1.logger.info("Calculating partial deleverage for net withdrawal");
1353
+ // 1. Calculate estimated current debt accurately
1420
1354
  const estimatedCurrentDebt = (0, clend_common_1.calculateLiabilityInterest)(debtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), Math.floor(Date.now() / 1000));
1421
1355
  const estimatedCurrentDebtUi = (0, clend_common_1.amountToUi)(estimatedCurrentDebt, debtDecimals);
1422
1356
  const collateralAmountUI = (0, clend_common_1.amountToUi)(collateralAmount, collateralDecimals);
1423
- const desiredNetWithdrawUi = (0, clend_common_1.amountToUi)(desiredNetWithdraw, collateralDecimals);
1424
- const { debtToRepayUi, totalCollateralToWithdrawUi } = (0, clend_common_1.computeWithdrawLeverageAmounts)(collateralAmountUI, estimatedCurrentDebtUi, desiredNetWithdrawUi, collateralPrice, debtPrice);
1425
- collateralToWithdraw = (0, clend_common_1.uiToAmount)(totalCollateralToWithdrawUi, collateralDecimals);
1357
+ const { debtToRepayUi, totalCollateralToWithdrawUi, newLeverage, currentLeverage, } = (0, clend_common_1.computeWithdrawLeverageAmounts)(collateralAmountUI, estimatedCurrentDebtUi, desiredNetWithdrawUi, collateralPrice, debtPrice);
1358
+ utils_1.logger.info("computeWithdrawLeverageAmounts", {
1359
+ debtToRepayUi,
1360
+ totalCollateralToWithdrawUi,
1361
+ newLeverage,
1362
+ currentLeverage,
1363
+ });
1426
1364
  const baseDebtToRepay = (0, clend_common_1.uiToAmount)(debtToRepayUi, debtDecimals);
1365
+ // --- Step 5: Add Buffers to the Debt Repayment Target ---
1366
+ // This adds a small buffer for interest that might accrue during the transaction's execution time.
1427
1367
  const timeBufferSec = 15;
1428
1368
  debtToRepay = (0, clend_common_1.addInterestAccrualBuffer)(baseDebtToRepay, debtBankData.borrowApy, timeBufferSec);
1369
+ // --- Step 6: Get a Jupiter Quote for the Swap ---
1370
+ // Query Jupiter for an EXACT_OUT swap to find out exactly how much collateral
1371
+ // is needed to get the final amount of debt we need to repay.
1372
+ const swapQuote = await this.swapper.getQuote({
1373
+ inputMint: collateralMint,
1374
+ inputMintDecimals: collateralDecimals,
1375
+ outputMint: debtMint,
1376
+ outputMintDecimals: debtDecimals,
1377
+ inputAmount: debtToRepay,
1378
+ slippageBps,
1379
+ swapMode: "ExactOut",
1380
+ });
1381
+ const collateralNeededForSwap = new anchor_1.BN(swapQuote.inAmount);
1382
+ // --- Step 7: Calculate the Final Total Collateral to Withdraw ---
1383
+ // This is the sum of what the user gets (desiredNetWithdraw) and what the swap needs.
1384
+ collateralToWithdraw = desiredNetWithdraw.add(collateralNeededForSwap);
1385
+ // Final sanity check to ensure we don't try to withdraw more than exists.
1429
1386
  if (collateralToWithdraw.gt(collateralAmount)) {
1430
- throw new Error("Total required collateral for withdrawal exceeds the available balance.");
1387
+ throw new Error("Total required collateral for withdrawal and swap exceeds the available balance. The position may be too leveraged for this withdrawal amount.");
1431
1388
  }
1432
1389
  }
1433
- utils_1.logger.debug("Final calculated amounts from params function", {
1390
+ utils_1.logger.debug("Final calculated amounts", {
1434
1391
  collateralToWithdraw: collateralToWithdraw.toString(),
1435
1392
  debtToRepay: debtToRepay.toString(),
1436
1393
  });
@@ -1441,85 +1398,9 @@ class ClendClient {
1441
1398
  debtBankData,
1442
1399
  };
1443
1400
  }
1444
- async getWithdrawLeverageCollateralIxns(clendGroup, clendAccount, collateralMint, debtMint, collateralDecimals, debtDecimals, withdrawAll, collateralToWithdraw, desiredNetCollateralToReceive, slippageBps, additionalIxnCount, swapperOverride) {
1445
- // override swapper if provided
1446
- let activeSwapper = this.swapper;
1447
- if (swapperOverride) {
1448
- activeSwapper = swapperOverride;
1449
- }
1450
- const debtBank = (0, addresses_1.getBankPda)(clendGroup, debtMint);
1451
- const collateralBank = (0, addresses_1.getBankPda)(clendGroup, collateralMint);
1452
- const clendAccountData = await this.getClendAccount(clendAccount);
1453
- if (!clendAccountData) {
1454
- throw new Error(`Clend account not found: ${clendAccount.toString()}`);
1455
- }
1456
- let activeBanks = (0, utils_1.getClendAccountActiveBanks)(clendAccountData);
1457
- const activeBankData = [];
1458
- for (const bank of activeBanks) {
1459
- const bankData = await this.getBank(bank);
1460
- activeBankData.push(bankData);
1461
- }
1462
- const remainingAccounts = (0, utils_1.getClendAccountRemainingAccounts)(activeBankData);
1463
- // 1. Determine the amount of collateral that needs to be swapped.
1464
- const collateralToSwap = collateralToWithdraw.sub(desiredNetCollateralToReceive);
1465
- // Safety check
1466
- if (collateralToSwap.isNeg() || collateralToSwap.isZero()) {
1467
- throw new Error(`Invalid calculation: collateral to swap is not positive. Total withdraw: ${collateralToWithdraw}, user receives: ${desiredNetCollateralToReceive}. Collateral to swap: ${collateralToSwap}`);
1468
- }
1469
- // 2. Get a fresh 'ExactIn' quote for swapping that collateral amount.
1470
- const swapQuote = await activeSwapper.getQuote({
1471
- payer: clendAccountData.authority,
1472
- inputMint: collateralMint,
1473
- inputMintDecimals: collateralDecimals,
1474
- outputMint: debtMint,
1475
- outputMintDecimals: debtDecimals,
1476
- inputAmount: collateralToSwap, // This is our exact input
1477
- slippageBps,
1478
- swapMode: "ExactIn",
1479
- });
1480
- // 3. The final amount to repay is the minimum we are guaranteed to get from the swap.
1481
- const finalDebtToRepay = new anchor_1.BN(swapQuote.otherAmountThreshold);
1482
- const swapIxns = await activeSwapper.getSwapIxns(swapQuote);
1483
- utils_1.logger.info(`swapIxns fetched`);
1484
- const collateralTokenProgram = (0, utils_1.getTokenProgramForMint)(collateralMint);
1485
- const debtTokenProgram = (0, utils_1.getTokenProgramForMint)(debtMint);
1486
- const userCollateralAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collateralMint, clendAccountData.authority, true, collateralTokenProgram);
1487
- const userDebtAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtMint, clendAccountData.authority, true, debtTokenProgram);
1488
- utils_1.logger.info(`begin crafting ixns`);
1489
- const withdrawIx = await this.instructions.withdraw(clendGroup, clendAccount, clendAccountData.authority, collateralBank, userCollateralAta, collateralTokenProgram, collateralToWithdraw, withdrawAll, remainingAccounts);
1490
- utils_1.logger.info(`withdrawIx set`);
1491
- const emissionsIxns = [];
1492
- if (withdrawAll) {
1493
- // ... (emission logic remains the same)
1494
- }
1495
- // Create repay instruction with the amount from the 'ExactIn' swap
1496
- const repayIx = await this.instructions.repay(clendGroup, clendAccount, clendAccountData.authority, debtBank, userDebtAta, debtTokenProgram, finalDebtToRepay, // Use the slippage-protected amount from the final quote
1497
- withdrawAll, true, // Use the new repay_up_to_amount flag
1498
- remainingAccounts);
1499
- utils_1.logger.info(`repayIx set`);
1500
- const ixnsWithoutFlashLoan = [
1501
- ...emissionsIxns,
1502
- withdrawIx,
1503
- ...swapIxns.ixns,
1504
- repayIx,
1505
- ];
1506
- utils_1.logger.info(`ixnsWithoutFlashLoan set`);
1507
- const cuIxns = 2;
1508
- const endIndex = new anchor_1.BN(cuIxns + ixnsWithoutFlashLoan.length + 1 + additionalIxnCount);
1509
- const { beginFlashLoanIx, endFlashLoanIx } = await this.instructions.createFlashLoanInstructions(clendAccount, clendAccountData.authority, endIndex, remainingAccounts);
1510
- const instructions = [
1511
- beginFlashLoanIx,
1512
- ...ixnsWithoutFlashLoan,
1513
- endFlashLoanIx,
1514
- ];
1515
- return { ixns: instructions, luts: swapIxns.luts };
1516
- }
1517
- async getNetWithdrawLeverageDebtParams(clendGroup, clendAccount, collateralMint, debtMint, desiredNetWithdraw, withdrawAll, swapperOverride) {
1518
- // override swapper if provided
1519
- let activeSwapper = this.swapper;
1520
- if (swapperOverride) {
1521
- activeSwapper = swapperOverride;
1522
- }
1401
+ // caller will receive desiredNetWithdraw, the position will be adjusted accordingly
1402
+ // receive the debt mint
1403
+ async getNetWithdrawLeverageDebtParams(clendGroup, clendAccount, collateralMint, debtMint, desiredNetWithdraw, withdrawAll, slippageBps) {
1523
1404
  utils_1.logger.info("getNetWithdrawDebtParams", {
1524
1405
  desiredNetWithdraw: desiredNetWithdraw.toString(),
1525
1406
  withdrawAll,
@@ -1565,31 +1446,52 @@ class ClendClient {
1565
1446
  let debtToRepay;
1566
1447
  if (withdrawAll) {
1567
1448
  utils_1.logger.info("WithdrawAll selected for receiving debt token");
1568
- collateralToWithdraw = collateralAmount; // All collateral will be swapped
1449
+ collateralToWithdraw = collateralAmount; // All JLP will be swapped
1569
1450
  debtToRepay = (0, clend_common_1.calculateLiabilityInterest)(currentDebtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), Math.floor(Date.now() / 1000));
1570
- utils_1.logger.info(`calculated debt to repay for withdrawAll`, {
1451
+ utils_1.logger.info(`calculated debt to repay`, {
1571
1452
  debtToRepayWithInterest: debtToRepay.toString(),
1453
+ debtToRepayRaw: debtAmount.toString(),
1572
1454
  });
1573
1455
  }
1574
1456
  else {
1575
- utils_1.logger.info("Calculating partial deleverage for receiving debt token with ExactIn logic");
1457
+ utils_1.logger.info("Calculating partial deleverage for receiving debt token");
1576
1458
  // 1. Calculate estimated current debt accurately
1577
1459
  const estimatedCurrentDebt = (0, clend_common_1.calculateLiabilityInterest)(currentDebtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), Math.floor(Date.now() / 1000));
1578
1460
  const estimatedCurrentDebtUi = (0, clend_common_1.amountToUi)(estimatedCurrentDebt, debtDecimals);
1579
1461
  const collateralAmountUI = (0, clend_common_1.amountToUi)(collateralAmount, collateralDecimals);
1580
1462
  const desiredNetWithdrawalOfDebtUi = (0, clend_common_1.amountToUi)(desiredNetWithdraw, debtDecimals);
1581
- // 2. Convert the desired net debt withdrawal into its collateral-equivalent value
1463
+ // Convert the value of the desired debt withdrawal into an equivalent amount of collateral
1464
+ // This is the "collateral-equivalent" value of the equity being removed.
1582
1465
  const netWithdrawAmountInCollateralUi = (0, clend_common_1.calculateWeightedValue)(desiredNetWithdrawalOfDebtUi, debtPrice, 1) /
1583
1466
  collateralPrice;
1584
- // 3. Compute the ideal deleverage amounts
1585
- const { debtToRepayUi, totalCollateralToWithdrawUi } = (0, clend_common_1.computeWithdrawLeverageAmounts)(collateralAmountUI, estimatedCurrentDebtUi, netWithdrawAmountInCollateralUi, collateralPrice, debtPrice);
1586
- // 4. Set the total collateral to withdraw based on the calculation
1587
- collateralToWithdraw = (0, clend_common_1.uiToAmount)(totalCollateralToWithdrawUi, collateralDecimals);
1588
- // 5. Calculate the base debt to repay and add a buffer for interest accrual
1467
+ const { debtToRepayUi, totalCollateralToWithdrawUi, newLeverage, currentLeverage, } = (0, clend_common_1.computeWithdrawLeverageAmounts)(collateralAmountUI, estimatedCurrentDebtUi, netWithdrawAmountInCollateralUi, collateralPrice, debtPrice);
1468
+ utils_1.logger.info("computeWithdrawLeverageAmounts", {
1469
+ debtToRepayUi,
1470
+ totalCollateralToWithdrawUi,
1471
+ newLeverage,
1472
+ currentLeverage,
1473
+ });
1589
1474
  const baseDebtToRepay = (0, clend_common_1.uiToAmount)(debtToRepayUi, debtDecimals);
1475
+ // --- Step 5: Add Buffers to the Debt Repayment Target ---
1476
+ // This adds a small buffer for interest that might accrue during the transaction's execution time.
1590
1477
  const timeBufferSec = 15;
1591
1478
  debtToRepay = (0, clend_common_1.addInterestAccrualBuffer)(baseDebtToRepay, debtBankData.borrowApy, timeBufferSec);
1592
- // 6. Final sanity check
1479
+ // --- Step 2: Determine total USDC needed from the swap ---
1480
+ // The total value we need to get from selling JLP is the debt we must repay
1481
+ const totalDebtNeededFromSwap = debtToRepay.add(desiredNetWithdraw);
1482
+ // Now, find how much collateral is needed to produce this swap via swap
1483
+ const swapQuote = await this.swapper.getQuote({
1484
+ inputMint: collateralMint,
1485
+ inputMintDecimals: collateralDecimals,
1486
+ outputMint: debtMint,
1487
+ outputMintDecimals: debtDecimals,
1488
+ inputAmount: totalDebtNeededFromSwap,
1489
+ slippageBps,
1490
+ swapMode: "ExactOut",
1491
+ });
1492
+ // The answer from swapper is the total collateral we must withdraw.
1493
+ collateralToWithdraw = swapQuote.inAmount;
1494
+ // --- Step 4: Final Sanity Check ---
1593
1495
  if (collateralToWithdraw.gt(collateralAmount)) {
1594
1496
  throw new Error("Total required collateral for withdrawal and swap exceeds the available balance.");
1595
1497
  }
@@ -1605,9 +1507,9 @@ class ClendClient {
1605
1507
  debtBankData,
1606
1508
  };
1607
1509
  }
1608
- async adjustLeverage(clendGroup, clendAccount, collateralMint, debtMint, targetLeverage, slippageBps, swapperOverride) {
1510
+ async adjustLeverage(clendGroup, clendAccount, collateralMint, debtMint, targetLeverage, slippageBps) {
1609
1511
  const params = await this.getAdjustLeverageParams(clendGroup, clendAccount, collateralMint, debtMint, targetLeverage, slippageBps);
1610
- const { ixns, luts } = await this.getAdjustLeverageIxns(clendGroup, clendAccount, this.address(), params.debtBankData, params.collateralBankData, params.isIncrease, params.finalDebtDeltaUi, params.finalCollateralDeltaUi, slippageBps, 0, swapperOverride);
1512
+ const { ixns, luts } = await this.getAdjustLeverageIxns(clendGroup, clendAccount, this.address(), params.debtBankData, params.collateralBankData, params.isIncrease, params.finalDebtDeltaUi, params.finalCollateralDeltaUi, slippageBps, 0);
1611
1513
  // Send transaction
1612
1514
  return this.send(ixns, [], luts);
1613
1515
  }
@@ -1751,7 +1653,7 @@ class ClendClient {
1751
1653
  finalDebtToRepayTargetBN: finalDebtToRepayTargetBN.toString(),
1752
1654
  finalAdjustedDebtDeltaUi: finalDebtDeltaUi,
1753
1655
  });
1754
- // 3. Recalculate Collateral Delta needed for the swap
1656
+ // 3. Recalculate Collateral Delta needed for the swap (ExactOut)
1755
1657
  const slippageFactor = new decimal_js_1.default(slippageBps).div(10000);
1756
1658
  if (slippageFactor.gte(1)) {
1757
1659
  throw new Error("Slippage cannot be 100% or more.");
@@ -1815,12 +1717,7 @@ class ClendClient {
1815
1717
  */
1816
1718
  async getAdjustLeverageIxns(clendGroup, clendAccount, user, debtBankData, collateralBankData, isIncrease, finalDebtDeltaUi, // Use the final adjusted value from getAdjustLeverageParams
1817
1719
  finalCollateralDeltaUi, // Use the final adjusted value from getAdjustLeverageParams
1818
- slippageBps, additionalIxnCount, swapperOverride) {
1819
- // override swapper if provided
1820
- let activeSwapper = this.swapper;
1821
- if (swapperOverride) {
1822
- activeSwapper = swapperOverride;
1823
- }
1720
+ slippageBps, additionalIxnCount) {
1824
1721
  const clendAccountData = await this.getClendAccount(clendAccount);
1825
1722
  if (!clendAccountData) {
1826
1723
  throw new Error(`Clend account not found: ${clendAccount.toString()}`);
@@ -1849,60 +1746,90 @@ class ClendClient {
1849
1746
  let swapLookupTables = [];
1850
1747
  if (isIncrease) {
1851
1748
  utils_1.logger.info("Building instructions for increasing leverage");
1852
- // This logic is already ExactIn and correct. No changes needed.
1749
+ // Use the finalDebtDeltaUi passed in (already adjusted)
1853
1750
  const additionalDebtAmount = (0, clend_common_1.uiToAmount)(finalDebtDeltaUi, debtBankData.mintDecimals);
1854
- const swapQuote = await activeSwapper.getQuote({
1855
- payer: user,
1751
+ utils_1.logger.debug("additionalDebtAmount to borrow (final adjusted)", {
1752
+ additionalDebtAmount: additionalDebtAmount.toString(),
1753
+ });
1754
+ // Get Jupiter quote for swapping debt token to collateral token (ExactIn)
1755
+ const swapMode = "ExactIn";
1756
+ const swapQuote = await this.swapper.getQuote({
1856
1757
  inputMint: debtBankData.mint,
1857
1758
  inputMintDecimals: debtBankData.mintDecimals,
1858
1759
  outputMint: collateralBankData.mint,
1859
1760
  outputMintDecimals: collateralBankData.mintDecimals,
1860
1761
  inputAmount: additionalDebtAmount,
1861
1762
  slippageBps,
1862
- swapMode: "ExactIn",
1763
+ swapMode,
1764
+ });
1765
+ utils_1.logger.debug("swapQuote calculated (Increase)", {
1766
+ inAmount: swapQuote.inAmount.toString(),
1767
+ outAmount: swapQuote.outAmount.toString(),
1768
+ otherAmountThreshold: swapQuote.otherAmountThreshold.toString(),
1863
1769
  });
1864
- const swapIxns = await activeSwapper.getSwapIxns(swapQuote);
1865
- const borrowIx = await this.instructions.borrow(clendGroup, clendAccount, user, debtBankData.key, userDebtAta, debtTokenProgram, additionalDebtAmount, remainingAccounts);
1770
+ // Get swap instructions
1771
+ const swapIxns = await this.swapper.getSwapIxns(user, swapQuote);
1772
+ // Borrow the final adjusted debt amount
1773
+ const borrowIx = await this.instructions.borrow(clendGroup, clendAccount, user, debtBankData.key, userDebtAta, debtTokenProgram, additionalDebtAmount, // Use final adjusted amount
1774
+ remainingAccounts);
1775
+ // Calculate additional collateral expected from swap (based on quote)
1866
1776
  const additionalCollateralAmount = swapQuote.outAmount;
1867
- const depositIx = await this.instructions.deposit(clendGroup, clendAccount, user, collateralBankData.key, userCollateralAta, collateralTokenProgram, additionalCollateralAmount, true, remainingAccounts);
1777
+ utils_1.logger.debug("additionalCollateralAmount expected from swap", {
1778
+ additionalCollateralAmount: additionalCollateralAmount.toString(),
1779
+ });
1780
+ // Deposit additional collateral received from swap
1781
+ const depositIx = await this.instructions.deposit(clendGroup, clendAccount, user, collateralBankData.key, userCollateralAta, collateralTokenProgram, additionalCollateralAmount, // Deposit what the swap provides
1782
+ true, remainingAccounts);
1868
1783
  instructions.push(borrowIx, ...swapIxns.ixns, depositIx);
1869
1784
  swapLookupTables = swapIxns.luts;
1870
1785
  }
1871
1786
  else {
1872
- // --- DECREASING LEVERAGE - NEW ExactIn LOGIC ---
1787
+ // Decreasing Leverage
1873
1788
  utils_1.logger.info("Building instructions for decreasing leverage");
1874
- // 1. The collateral to withdraw/sell is our exact input for the swap.
1875
- const collateralToWithdrawBN = (0, clend_common_1.uiToAmount)(finalCollateralDeltaUi, collateralBankData.mintDecimals);
1876
- // 2. Get a fresh 'ExactIn' quote for swapping that collateral amount.
1877
- const swapQuote = await activeSwapper.getQuote({
1878
- payer: clendAccountData.authority,
1789
+ // Use the finalDebtDeltaUi passed in (already includes future buffer)
1790
+ const finalDebtToRepayBN = (0, clend_common_1.uiToAmount)(finalDebtDeltaUi, debtBankData.mintDecimals);
1791
+ // Use the finalCollateralDeltaUi passed in (already includes slippage adjustment for swap)
1792
+ const finalCollateralToWithdrawBN = (0, clend_common_1.uiToAmount)(finalCollateralDeltaUi, collateralBankData.mintDecimals);
1793
+ utils_1.logger.debug("Final amounts for decrease operation (Native)", {
1794
+ finalDebtToRepayBN: finalDebtToRepayBN.toString(),
1795
+ finalCollateralToWithdrawBN: finalCollateralToWithdrawBN.toString(),
1796
+ });
1797
+ // Get Jupiter quote for swapping collateral token to debt token (ExactOut)
1798
+ // Target the final buffered debt repayment amount
1799
+ const swapMode = "ExactOut";
1800
+ const swapQuote = await this.swapper.getQuote({
1879
1801
  inputMint: collateralBankData.mint,
1880
1802
  inputMintDecimals: collateralBankData.mintDecimals,
1881
1803
  outputMint: debtBankData.mint,
1882
1804
  outputMintDecimals: debtBankData.mintDecimals,
1883
- inputAmount: collateralToWithdrawBN, // Use the collateral delta as the exact input
1805
+ inputAmount: finalDebtToRepayBN,
1884
1806
  slippageBps,
1885
- swapMode: "ExactIn",
1807
+ swapMode,
1886
1808
  });
1887
- // 3. The final amount to repay is the optimistic amount from the quote,
1888
- // as we will use the `repay_up_to_amount` flag on-chain.
1889
- const finalDebtToRepay = swapQuote.outAmount;
1890
- const swapIxns = await activeSwapper.getSwapIxns(swapQuote);
1891
- // Withdraw the calculated collateral needed for the swap
1892
- const withdrawIx = await this.instructions.withdraw(clendGroup, clendAccount, user, collateralBankData.key, userCollateralAta, collateralTokenProgram, collateralToWithdrawBN, false, // Not withdrawing all
1809
+ const estimatedCollateralInput = swapQuote.inAmount;
1810
+ utils_1.logger.debug("swapQuote calculated (Decrease)", {
1811
+ targetOutput: finalDebtToRepayBN.toString(),
1812
+ estimatedInput: estimatedCollateralInput,
1813
+ maxInputCalculated: finalCollateralToWithdrawBN.toString(), // Compare Jupiter estimate vs our max calc
1814
+ });
1815
+ const swapIxns = await this.swapper.getSwapIxns(user, swapQuote);
1816
+ // Withdraw the CALCULATED MAXIMUM collateral needed (includes swap input + buffer)
1817
+ const withdrawIx = await this.instructions.withdraw(clendGroup, clendAccount, user, collateralBankData.key, userCollateralAta, collateralTokenProgram, finalCollateralToWithdrawBN, // Use the final adjusted amount from params
1818
+ false, // Not withdrawing all
1893
1819
  remainingAccounts);
1894
- // Repay the debt with the swap output
1895
- const repayIx = await this.instructions.repay(clendGroup, clendAccount, user, debtBankData.key, userDebtAta, debtTokenProgram, finalDebtToRepay, // Use optimistic amount
1820
+ // Repay the CALCULATED BUFFERED debt amount
1821
+ const repayIx = await this.instructions.repay(clendGroup, clendAccount, user, debtBankData.key, userDebtAta, debtTokenProgram, finalDebtToRepayBN, // Use the final adjusted amount from params
1896
1822
  false, // Not repaying all
1897
- true, // Use the new repay_up_to_amount flag
1898
1823
  remainingAccounts);
1899
1824
  instructions.push(withdrawIx, ...swapIxns.ixns, repayIx);
1900
1825
  swapLookupTables = swapIxns.luts;
1901
1826
  }
1902
1827
  // --- Flash Loan Wrapping ---
1903
- const cuIxns = 2;
1828
+ const cuIxns = 2; // Assuming 2 compute budget instructions are added later
1829
+ // Calculate end index based on actual instructions generated + potential JIT ixns + flash loan ixns
1904
1830
  const endIndex = new anchor_1.BN(cuIxns + instructions.length + 1 + additionalIxnCount);
1905
1831
  const { beginFlashLoanIx, endFlashLoanIx } = await this.instructions.createFlashLoanInstructions(clendAccount, user, endIndex, remainingAccounts);
1832
+ // Create a new array with the flash loan instructions wrapping the existing instructions
1906
1833
  const finalInstructions = [
1907
1834
  beginFlashLoanIx,
1908
1835
  ...instructions,