@carrot-protocol/clend-rpc 0.1.27-swapper1-dev-f4263ff → 0.1.27

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
@@ -90,12 +90,11 @@ class ClendClient {
90
90
  const latestSubmission = pullFeedAccountData.submissions[0];
91
91
  // The 'value' is a BN (BigNumber), so we convert it to a standard JavaScript number.
92
92
  // Be aware of potential precision loss if the number is larger than Number.MAX_SAFE_INTEGER.
93
- const price = new decimal_js_1.default(latestSubmission.value.toString());
93
+ const price = Number(latestSubmission.value);
94
94
  // The exponent for Switchboard v2/v3 feeds is typically -18, but you should
95
95
  // confirm this for the specific feed you are using.
96
96
  const exponent = -18;
97
- const priceDecimal = (0, utils_1.calculatePriceUiDecimal)(price, exponent);
98
- console.log("priceDecimal", priceDecimal);
97
+ const priceDecimal = (0, utils_1.calculatePriceUi)(price, exponent);
99
98
  return priceDecimal;
100
99
  }
101
100
  // find oracle for a mint from in memory cache
@@ -206,6 +205,65 @@ class ClendClient {
206
205
  const txSig = await this.send(ixns);
207
206
  return { bank, txSig };
208
207
  }
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
+ }
209
267
  // Account fetching Operations
210
268
  async getClendGroup(clendGroup) {
211
269
  const accountInfo = await this.connection.getAccountInfo(clendGroup, {
@@ -280,9 +338,9 @@ class ClendClient {
280
338
  collectedGroupFeesOutstanding: Number((0, utils_1.wrappedI80F48toBigNumber)(data.collectedGroupFeesOutstanding)),
281
339
  lastUpdate: new anchor_1.BN(data.lastUpdate),
282
340
  config: bankConfig,
283
- flags: new anchor_1.BN(data.flags),
341
+ flags: (0, state_1.parseBankFlags)(new anchor_1.BN(data.flags)),
284
342
  emissionsRate: new anchor_1.BN(data.emissionsRate),
285
- emissionsRemaining: (0, utils_1.wrappedI80F48toBigNumber)(data.emissionsRemaining),
343
+ emissionsRemaining: new anchor_1.BN((0, utils_1.wrappedI80F48toBigNumber)(data.emissionsRemaining)),
286
344
  emissionsMint: new anchor_1.web3.PublicKey(data.emissionsMint),
287
345
  collectedProgramFeesOutstanding: Number((0, utils_1.wrappedI80F48toBigNumber)(data.collectedProgramFeesOutstanding)),
288
346
  assetAmount,
@@ -335,21 +393,29 @@ class ClendClient {
335
393
  const bankData = await this.getBank(bank);
336
394
  const bankMint = bankData.mint;
337
395
  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);
338
402
  const lendingAccountBalance = (0, state_1.newClendAccountBalance)({
339
403
  active,
340
404
  bankPk: bank,
341
405
  bankMint,
342
406
  bankAssetTag: Number(balance.bankAssetTag),
343
- assetShares: Number((0, utils_1.wrappedI80F48toBigNumber)(balance.assetShares)),
407
+ assetShares,
344
408
  assetShareValue: Number(bankData.assetShareValue),
345
409
  assetMintDecimals: Number(bankData.mintDecimals),
346
- liabilityShares: Number((0, utils_1.wrappedI80F48toBigNumber)(balance.liabilityShares)),
410
+ liabilityShares,
347
411
  liabilityShareValue: Number(bankData.liabilityShareValue),
348
412
  liabilityMintDecimals: Number(bankData.mintDecimals),
349
413
  lastUpdate: new anchor_1.BN(balance.lastUpdate),
350
414
  assetWeightMaint: Number(bankData.config.assetWeightMaint),
351
415
  liabilityWeightMaint: Number(bankData.config.liabilityWeightMaint),
352
416
  price,
417
+ emissionsOutstanding,
418
+ emissionsOutstandingAndUnclaimed,
353
419
  });
354
420
  balances.push(lendingAccountBalance);
355
421
  healthFactorWeightedAssetArgs.push(lendingAccountBalance.assetWeightedMaintValue);
@@ -435,13 +501,6 @@ class ClendClient {
435
501
  // get required remaining accounts based on account balance
436
502
  const clendAccountData = await this.getClendAccount(clendAccount);
437
503
  let clendAccountActiveBanks = (0, utils_1.getClendAccountActiveBanks)(clendAccountData);
438
- //// If withdrawAll is true, remove the target bank from activeBanks
439
- //// as we wont have a remaining balance after the withdraw operation
440
- //if (withdrawAll === true) {
441
- // clendAccountActiveBanks = clendAccountActiveBanks.filter(
442
- // (b) => !b.equals(bank),
443
- // );
444
- //}
445
504
  // fetch all active banks
446
505
  const activeBankData = [];
447
506
  for (const bank of clendAccountActiveBanks) {
@@ -476,7 +535,7 @@ class ClendClient {
476
535
  const ix = await this.instructions.borrow(clendGroup, clendAccount, this.address(), bank, destinationTokenAccount, tokenProgram, amount, remainingAccounts);
477
536
  return this.send([ix]);
478
537
  }
479
- async repay(clendGroup, clendAccount, mint, amount, repayAll = null) {
538
+ async repay(clendGroup, clendAccount, mint, amount, repayAll, repayUpToAmount) {
480
539
  const bank = (0, addresses_1.getBankPda)(clendGroup, mint);
481
540
  // Get bank data to access the liquidityVault and mint
482
541
  const bankData = await this.getBank(bank);
@@ -492,7 +551,7 @@ class ClendClient {
492
551
  activeBankData.push(bankData);
493
552
  }
494
553
  const remainingAccounts = (0, utils_1.getClendAccountRemainingAccounts)(activeBankData);
495
- const ix = await this.instructions.repay(clendGroup, clendAccount, this.address(), bank, userTokenAccount, tokenProgram, amount, repayAll, remainingAccounts);
554
+ const ix = await this.instructions.repay(clendGroup, clendAccount, this.address(), bank, userTokenAccount, tokenProgram, amount, repayAll, repayUpToAmount, remainingAccounts);
496
555
  return this.send([ix]);
497
556
  }
498
557
  async editGlobalFeeState(clendGroup, newGlobalFeeWallet, newBankInitFlatSolFee, newProgramFeeFixed, newProgramFeeRate) {
@@ -776,11 +835,16 @@ class ClendClient {
776
835
  const ix = await this.instructions.configureBankOracleMaxAge(clendGroup, this.address(), bank, maxAge);
777
836
  return this.send([ix]);
778
837
  }
779
- async configureBankOracle(bank, pythOracle, pythOracleFeed) {
838
+ async configureBankPythOracle(bank, pythOracle, pythOracleFeed) {
780
839
  const bankData = await this.getBank(bank);
781
840
  const ix = await this.instructions.configureBankPythOracle(bankData.group, this.address(), bank, pythOracle, pythOracleFeed);
782
841
  return this.send([ix]);
783
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);
846
+ return this.send([ix]);
847
+ }
784
848
  async setBankOperationalState(clendGroup, bank, operationalState) {
785
849
  const ix = await this.instructions.setBankOperationalState(clendGroup, this.address(), bank, operationalState);
786
850
  return this.send([ix]);
@@ -1189,14 +1253,14 @@ class ClendClient {
1189
1253
  let luts = [];
1190
1254
  switch (selectedMint.toString()) {
1191
1255
  case collateralMint.toString():
1192
- const collateralParams = await this.getNetWithdrawLeverageCollateralParams(clendGroup, clendAccount, collateralMint, debtMint, withdrawAmount, withdrawAll, slippageBps, swapperOverride);
1193
- 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, swapperOverride);
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);
1194
1258
  ixns = collateralIxns;
1195
1259
  luts = collateralLuts;
1196
1260
  break;
1197
1261
  case debtMint.toString():
1198
- const debtParams = await this.getNetWithdrawLeverageDebtParams(clendGroup, clendAccount, collateralMint, debtMint, withdrawAmount, withdrawAll, slippageBps, swapperOverride);
1199
- 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, swapperOverride);
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);
1200
1264
  ixns = debtIxns;
1201
1265
  luts = debtLuts;
1202
1266
  break;
@@ -1206,7 +1270,7 @@ class ClendClient {
1206
1270
  // Send transaction
1207
1271
  return this.send(ixns, [], luts);
1208
1272
  }
1209
- async getWithdrawLeverageCollateralIxns(clendGroup, clendAccount, collateralMint, debtMint, collateralDecimals, debtDecimals, withdrawAll, debtToRepay, collateralToWithdraw, slippageBps, additionalIxnCount, swapperOverride) {
1273
+ async getWithdrawLeverageDebtIxns(clendGroup, clendAccount, collateralMint, debtMint, collateralDecimals, debtDecimals, withdrawAll, debtToRepay, collateralToWithdraw, slippageBps, additionalIxnCount, swapperOverride) {
1210
1274
  // override swapper if provided
1211
1275
  let activeSwapper = this.swapper;
1212
1276
  if (swapperOverride) {
@@ -1214,140 +1278,87 @@ class ClendClient {
1214
1278
  }
1215
1279
  const debtBank = (0, addresses_1.getBankPda)(clendGroup, debtMint);
1216
1280
  const collateralBank = (0, addresses_1.getBankPda)(clendGroup, collateralMint);
1217
- // Get remaining accounts for flash loan
1218
1281
  const clendAccountData = await this.getClendAccount(clendAccount);
1219
1282
  if (!clendAccountData) {
1220
1283
  throw new Error(`Clend account not found: ${clendAccount.toString()}`);
1221
1284
  }
1222
1285
  let activeBanks = (0, utils_1.getClendAccountActiveBanks)(clendAccountData);
1223
- const activeBankData = [];
1224
- for (const bank of activeBanks) {
1225
- const bankData = await this.getBank(bank);
1226
- activeBankData.push(bankData);
1227
- }
1286
+ const activeBankData = await Promise.all(activeBanks.map((bankPk) => this.getBank(bankPk)));
1228
1287
  const remainingAccounts = (0, utils_1.getClendAccountRemainingAccounts)(activeBankData);
1229
- // Get Jupiter quote for swapping collateral to debt token (ExactOut mode)
1230
- const swapMode = "ExactOut";
1288
+ // Get a fresh 'ExactIn' quote for swapping the total collateral to withdraw
1231
1289
  const swapQuote = await activeSwapper.getQuote({
1232
1290
  payer: clendAccountData.authority,
1233
1291
  inputMint: collateralMint,
1234
1292
  inputMintDecimals: collateralDecimals,
1235
1293
  outputMint: debtMint,
1236
1294
  outputMintDecimals: debtDecimals,
1237
- inputAmount: debtToRepay,
1295
+ inputAmount: collateralToWithdraw, // Use the total collateral to withdraw as the exact input
1238
1296
  slippageBps,
1239
- swapMode,
1240
- });
1241
- // set collateralToSwap to the inAmount from the swap quote
1242
- const collateralToSwap = swapQuote.inAmount;
1243
- utils_1.logger.debug("withdrawLeverage collateralToSwap", {
1244
- collateralToSwap,
1297
+ swapMode: "ExactIn",
1245
1298
  });
1246
- // Get swap instructions
1247
- const swapInputs = {
1248
- inputAmountLamports: new decimal_js_1.default(collateralToSwap.toString()),
1249
- inputMint: collateralMint,
1250
- outputMint: debtMint,
1251
- };
1252
- utils_1.logger.debug("withdrawLeverage swapInputs", {
1253
- swapInputs,
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(),
1254
1308
  });
1309
+ // Get Swap Instructions from the swapper
1255
1310
  const swapIxns = await activeSwapper.getSwapIxns(swapQuote);
1256
- utils_1.logger.info(`swapIxns fetched`);
1311
+ utils_1.logger.info(`swapIxns fetched for ReceiveDebtToken`);
1312
+ // --- ATAs and Clend Instructions ---
1257
1313
  const collateralTokenProgram = (0, utils_1.getTokenProgramForMint)(collateralMint);
1258
1314
  const debtTokenProgram = (0, utils_1.getTokenProgramForMint)(debtMint);
1259
1315
  const userCollateralAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collateralMint, clendAccountData.authority, true, collateralTokenProgram);
1260
1316
  const userDebtAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtMint, clendAccountData.authority, true, debtTokenProgram);
1261
- utils_1.logger.info(`begin crafting ixns`);
1262
- // Create withdraw instruction
1317
+ // Create withdraw instruction: Withdraw the total collateral calculated by the params function
1263
1318
  const withdrawIx = await this.instructions.withdraw(clendGroup, clendAccount, clendAccountData.authority, collateralBank, userCollateralAta, collateralTokenProgram, collateralToWithdraw, withdrawAll, remainingAccounts);
1264
- utils_1.logger.info(`withdrawIx set`);
1265
- // Create repay instruction
1266
- const repayIx = await this.instructions.repay(clendGroup, clendAccount, clendAccountData.authority, debtBank, userDebtAta, debtTokenProgram, debtToRepay, withdrawAll, remainingAccounts);
1267
- utils_1.logger.info(`repayIx set`);
1268
- // Assemble all instructions without flash loan
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
1269
1355
  const ixnsWithoutFlashLoan = [
1356
+ ...emissionsIxns,
1270
1357
  withdrawIx,
1271
1358
  ...swapIxns.ixns,
1272
1359
  repayIx,
1273
1360
  ];
1274
- utils_1.logger.info(`ixnsWithoutFlashLoan set`);
1275
- // Create flash loan instructions
1276
- const cuIxns = 2;
1277
- const endIndex = new anchor_1.BN(cuIxns + ixnsWithoutFlashLoan.length + 1 + additionalIxnCount);
1278
- const { beginFlashLoanIx, endFlashLoanIx } = await this.instructions.createFlashLoanInstructions(clendAccount, clendAccountData.authority, endIndex, remainingAccounts);
1279
- // Assemble all instructions in the correct order
1280
- const instructions = [
1281
- beginFlashLoanIx,
1282
- ...ixnsWithoutFlashLoan,
1283
- endFlashLoanIx,
1284
- ];
1285
- return { ixns: instructions, luts: swapIxns.luts };
1286
- }
1287
- async getWithdrawLeverageDebtIxns(clendGroup, clendAccount, collateralMint, debtMint, collateralDecimals, debtDecimals, withdrawAll, debtToRepay, collateralToWithdraw, desiredNetDebtToReceive, slippageBps, additionalIxnCount, swapperOverride) {
1288
- // override swapper if provided
1289
- let activeSwapper = this.swapper;
1290
- if (swapperOverride) {
1291
- activeSwapper = swapperOverride;
1292
- }
1293
- const debtBank = (0, addresses_1.getBankPda)(clendGroup, debtMint);
1294
- const collateralBank = (0, addresses_1.getBankPda)(clendGroup, collateralMint);
1295
- const clendAccountData = await this.getClendAccount(clendAccount);
1296
- if (!clendAccountData) {
1297
- throw new Error(`Clend account not found: ${clendAccount.toString()}`);
1298
- }
1299
- let activeBanks = (0, utils_1.getClendAccountActiveBanks)(clendAccountData);
1300
- //if (withdrawAll) {
1301
- // activeBanks = activeBanks.filter(
1302
- // (b) => !b.equals(collateralBank) && !b.equals(debtBank),
1303
- // );
1304
- //}
1305
- const activeBankData = await Promise.all(activeBanks.map((bankPk) => this.getBank(bankPk)));
1306
- const remainingAccounts = (0, utils_1.getClendAccountRemainingAccounts)(activeBankData);
1307
- // 1. Determine Total USDC Needed from the Swap
1308
- // This is the amount needed to repay Clend's debt AND give the user their desired net withdrawal.
1309
- const totalDebtToReceive = debtToRepay.add(desiredNetDebtToReceive);
1310
- const swapMode = "ExactIn";
1311
- const swapQuote = await activeSwapper.getQuote({
1312
- payer: clendAccountData.authority,
1313
- inputMint: collateralMint,
1314
- inputMintDecimals: collateralDecimals,
1315
- outputMint: debtMint,
1316
- outputMintDecimals: debtDecimals,
1317
- inputAmount: collateralToWithdraw,
1318
- slippageBps,
1319
- swapMode,
1320
- });
1321
- const minUsdcReceivedFromSwap_BN = new anchor_1.BN(swapQuote.otherAmountThreshold);
1322
- utils_1.logger.debug("getWithdrawLeverageDebtIxns: collateral and debt details", {
1323
- debtToRepay: debtToRepay.toString(),
1324
- desiredNetDebtToReceive: desiredNetDebtToReceive.toString(),
1325
- totalDebtToReceive: totalDebtToReceive.toString(),
1326
- minUsdcReceivedFromSwap_BN: minUsdcReceivedFromSwap_BN.toString(),
1327
- });
1328
- // 3. Get Swap Instructions from swapper
1329
- const swapIxns = await activeSwapper.getSwapIxns(swapQuote);
1330
- utils_1.logger.info(`swapIxns fetched for ReceiveDebtToken`);
1331
- // --- ATAs and Clend Instructions (largely similar structure) ---
1332
- const collateralTokenProgram = (0, utils_1.getTokenProgramForMint)(collateralMint);
1333
- const debtTokenProgram = (0, utils_1.getTokenProgramForMint)(debtMint);
1334
- const userCollateralAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collateralMint, clendAccountData.authority, true, collateralTokenProgram);
1335
- const userDebtAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtMint, clendAccountData.authority, true, debtTokenProgram);
1336
- // Create withdraw instruction: Withdraw ALL the JLP calculated by params function
1337
- const withdrawIx = await this.instructions.withdraw(clendGroup, clendAccount, clendAccountData.authority, collateralBank, userCollateralAta, collateralTokenProgram, collateralToWithdraw, // Withdraw all the JLP needed for the full swap
1338
- withdrawAll, // This flag is for Clend's internal logic
1339
- remainingAccounts);
1340
- // Create repay instruction: Repay the calculated debt portion to Clend
1341
- const repayIx = await this.instructions.repay(clendGroup, clendAccount, clendAccountData.authority, debtBank, userDebtAta, debtTokenProgram, debtToRepay, // Repay the specified debt amount
1342
- withdrawAll, // This flag is for Clend's internal logic
1343
- remainingAccounts);
1344
- // Assemble instructions
1345
- const ixnsWithoutFlashLoan = [
1346
- withdrawIx,
1347
- ...swapIxns.ixns, // Swap all withdrawn collateral to debt
1348
- repayIx, // Repay the debt portion
1349
- ]; // User is left with the remaining debt in their ATA
1350
- // Flash Loan Wrapping (same as before)
1361
+ // Flash Loan Wrapping
1351
1362
  const cuIxns = 2;
1352
1363
  const endIndex = new anchor_1.BN(cuIxns + ixnsWithoutFlashLoan.length + 1 + additionalIxnCount);
1353
1364
  const { beginFlashLoanIx, endFlashLoanIx } = await this.instructions.createFlashLoanInstructions(clendAccount, clendAccountData.authority, endIndex, remainingAccounts);
@@ -1358,21 +1369,13 @@ class ClendClient {
1358
1369
  ];
1359
1370
  return { ixns: instructions, luts: swapIxns.luts };
1360
1371
  }
1361
- // caller will receive desiredNetWithdraw, the position will be adjusted accordingly
1362
- // receive the collateral mint
1363
- async getNetWithdrawLeverageCollateralParams(clendGroup, clendAccount, collateralMint, debtMint, desiredNetWithdraw, withdrawAll, slippageBps, swapperOverride) {
1364
- // override swapper if provided
1365
- let activeSwapper = this.swapper;
1366
- if (swapperOverride) {
1367
- activeSwapper = swapperOverride;
1368
- }
1369
- utils_1.logger.info("getNetWithdrawParams", {
1372
+ async getNetWithdrawLeverageCollateralParams(clendGroup, clendAccount, collateralMint, debtMint, desiredNetWithdraw, withdrawAll) {
1373
+ utils_1.logger.info("getNetWithdrawLeverageCollateralParams", {
1370
1374
  desiredNetWithdraw: desiredNetWithdraw.toString(),
1371
1375
  withdrawAll,
1372
1376
  });
1373
- // if desiredNetWithdraw is zero, throw an error
1374
1377
  if (desiredNetWithdraw.isZero() && !withdrawAll) {
1375
- throw new Error("Desired net withdrawal is zero");
1378
+ throw new Error("Desired net withdrawal is zero for a partial withdraw.");
1376
1379
  }
1377
1380
  const collateralBank = (0, addresses_1.getBankPda)(clendGroup, collateralMint);
1378
1381
  const debtBank = (0, addresses_1.getBankPda)(clendGroup, debtMint);
@@ -1384,7 +1387,6 @@ class ClendClient {
1384
1387
  if (!clendAccountData) {
1385
1388
  throw new Error(`Clend account not found: ${clendAccount.toString()}`);
1386
1389
  }
1387
- // fetch balances and pricing
1388
1390
  let debtBalance;
1389
1391
  let debtPrice;
1390
1392
  let collateralBalance;
@@ -1403,76 +1405,32 @@ class ClendClient {
1403
1405
  throw new Error("Could not find debt or collateral position in account");
1404
1406
  }
1405
1407
  const collateralAmount = collateralBalance.assetBalance;
1406
- const collateralAmountUI = collateralBalance.assetBalanceUi;
1407
- const collateralValue = collateralBalance.assetValue;
1408
- const collateralDecimals = collateralBankData.mintDecimals;
1409
1408
  const debtAmount = debtBalance.liabilityBalance;
1410
- const debtAmountUI = debtBalance.liabilityBalanceUi;
1411
- const debtValue = debtBalance.liabilityValue;
1409
+ const collateralDecimals = collateralBankData.mintDecimals;
1412
1410
  const debtDecimals = debtBankData.mintDecimals;
1413
- const desiredNetWithdrawUi = (0, clend_common_1.amountToUi)(desiredNetWithdraw, debtDecimals);
1414
- utils_1.logger.debug("Current amounts", {
1415
- desiredNetWithdrawUi,
1416
- collateralAmount: collateralAmount.toString(),
1417
- collateralAmountUI,
1418
- collateralValue,
1419
- debtAmount: debtAmount.toString(),
1420
- debtAmountUI,
1421
- debtValue,
1422
- });
1423
- // --- Determine collateral to withdraw and debt to repay ---
1424
1411
  let collateralToWithdraw;
1425
1412
  let debtToRepay;
1426
1413
  if (withdrawAll) {
1427
1414
  utils_1.logger.info("WithdrawAll selected");
1428
1415
  collateralToWithdraw = collateralAmount;
1429
- debtToRepay = (0, clend_common_1.calculateLiabilityInterest)(debtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), new Date().getTime() / 1000);
1430
- utils_1.logger.info(`calculated debt to repay`, {
1431
- debtToRepayWithInterest: debtToRepay.toString(),
1432
- debtToRepayRaw: debtAmount.toString(),
1433
- });
1416
+ debtToRepay = (0, clend_common_1.calculateLiabilityInterest)(debtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), Math.floor(Date.now() / 1000));
1434
1417
  }
1435
1418
  else {
1436
- utils_1.logger.info("Calculating partial deleverage for net withdrawal");
1437
- // 1. Calculate estimated current debt accurately
1419
+ utils_1.logger.info("Calculating partial deleverage amounts");
1438
1420
  const estimatedCurrentDebt = (0, clend_common_1.calculateLiabilityInterest)(debtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), Math.floor(Date.now() / 1000));
1439
1421
  const estimatedCurrentDebtUi = (0, clend_common_1.amountToUi)(estimatedCurrentDebt, debtDecimals);
1440
1422
  const collateralAmountUI = (0, clend_common_1.amountToUi)(collateralAmount, collateralDecimals);
1441
- const { debtToRepayUi, totalCollateralToWithdrawUi, newLeverage, currentLeverage, } = (0, clend_common_1.computeWithdrawLeverageAmounts)(collateralAmountUI, estimatedCurrentDebtUi, desiredNetWithdrawUi, collateralPrice, debtPrice);
1442
- utils_1.logger.info("computeWithdrawLeverageAmounts", {
1443
- debtToRepayUi,
1444
- totalCollateralToWithdrawUi,
1445
- newLeverage,
1446
- currentLeverage,
1447
- });
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);
1448
1426
  const baseDebtToRepay = (0, clend_common_1.uiToAmount)(debtToRepayUi, debtDecimals);
1449
- // --- Step 5: Add Buffers to the Debt Repayment Target ---
1450
- // This adds a small buffer for interest that might accrue during the transaction's execution time.
1451
1427
  const timeBufferSec = 15;
1452
1428
  debtToRepay = (0, clend_common_1.addInterestAccrualBuffer)(baseDebtToRepay, debtBankData.borrowApy, timeBufferSec);
1453
- // --- Step 6: Get a Jupiter Quote for the Swap ---
1454
- // Query Jupiter for an EXACT_OUT swap to find out exactly how much collateral
1455
- // is needed to get the final amount of debt we need to repay.
1456
- const swapQuote = await activeSwapper.getQuote({
1457
- payer: clendAccountData.authority,
1458
- inputMint: collateralMint,
1459
- inputMintDecimals: collateralDecimals,
1460
- outputMint: debtMint,
1461
- outputMintDecimals: debtDecimals,
1462
- inputAmount: debtToRepay,
1463
- slippageBps,
1464
- swapMode: "ExactOut",
1465
- });
1466
- const collateralNeededForSwap = new anchor_1.BN(swapQuote.inAmount);
1467
- // --- Step 7: Calculate the Final Total Collateral to Withdraw ---
1468
- // This is the sum of what the user gets (desiredNetWithdraw) and what the swap needs.
1469
- collateralToWithdraw = desiredNetWithdraw.add(collateralNeededForSwap);
1470
- // Final sanity check to ensure we don't try to withdraw more than exists.
1471
1429
  if (collateralToWithdraw.gt(collateralAmount)) {
1472
- throw new Error("Total required collateral for withdrawal and swap exceeds the available balance. The position may be too leveraged for this withdrawal amount.");
1430
+ throw new Error("Total required collateral for withdrawal exceeds the available balance.");
1473
1431
  }
1474
1432
  }
1475
- utils_1.logger.debug("Final calculated amounts", {
1433
+ utils_1.logger.debug("Final calculated amounts from params function", {
1476
1434
  collateralToWithdraw: collateralToWithdraw.toString(),
1477
1435
  debtToRepay: debtToRepay.toString(),
1478
1436
  });
@@ -1483,9 +1441,80 @@ class ClendClient {
1483
1441
  debtBankData,
1484
1442
  };
1485
1443
  }
1486
- // caller will receive desiredNetWithdraw, the position will be adjusted accordingly
1487
- // receive the debt mint
1488
- async getNetWithdrawLeverageDebtParams(clendGroup, clendAccount, collateralMint, debtMint, desiredNetWithdraw, withdrawAll, slippageBps, swapperOverride) {
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) {
1489
1518
  // override swapper if provided
1490
1519
  let activeSwapper = this.swapper;
1491
1520
  if (swapperOverride) {
@@ -1536,53 +1565,31 @@ class ClendClient {
1536
1565
  let debtToRepay;
1537
1566
  if (withdrawAll) {
1538
1567
  utils_1.logger.info("WithdrawAll selected for receiving debt token");
1539
- collateralToWithdraw = collateralAmount; // All JLP will be swapped
1568
+ collateralToWithdraw = collateralAmount; // All collateral will be swapped
1540
1569
  debtToRepay = (0, clend_common_1.calculateLiabilityInterest)(currentDebtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), Math.floor(Date.now() / 1000));
1541
- utils_1.logger.info(`calculated debt to repay`, {
1570
+ utils_1.logger.info(`calculated debt to repay for withdrawAll`, {
1542
1571
  debtToRepayWithInterest: debtToRepay.toString(),
1543
- debtToRepayRaw: debtAmount.toString(),
1544
1572
  });
1545
1573
  }
1546
1574
  else {
1547
- utils_1.logger.info("Calculating partial deleverage for receiving debt token");
1575
+ utils_1.logger.info("Calculating partial deleverage for receiving debt token with ExactIn logic");
1548
1576
  // 1. Calculate estimated current debt accurately
1549
1577
  const estimatedCurrentDebt = (0, clend_common_1.calculateLiabilityInterest)(currentDebtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), Math.floor(Date.now() / 1000));
1550
1578
  const estimatedCurrentDebtUi = (0, clend_common_1.amountToUi)(estimatedCurrentDebt, debtDecimals);
1551
1579
  const collateralAmountUI = (0, clend_common_1.amountToUi)(collateralAmount, collateralDecimals);
1552
1580
  const desiredNetWithdrawalOfDebtUi = (0, clend_common_1.amountToUi)(desiredNetWithdraw, debtDecimals);
1553
- // Convert the value of the desired debt withdrawal into an equivalent amount of collateral
1554
- // This is the "collateral-equivalent" value of the equity being removed.
1581
+ // 2. Convert the desired net debt withdrawal into its collateral-equivalent value
1555
1582
  const netWithdrawAmountInCollateralUi = (0, clend_common_1.calculateWeightedValue)(desiredNetWithdrawalOfDebtUi, debtPrice, 1) /
1556
1583
  collateralPrice;
1557
- const { debtToRepayUi, totalCollateralToWithdrawUi, newLeverage, currentLeverage, } = (0, clend_common_1.computeWithdrawLeverageAmounts)(collateralAmountUI, estimatedCurrentDebtUi, netWithdrawAmountInCollateralUi, collateralPrice, debtPrice);
1558
- utils_1.logger.info("computeWithdrawLeverageAmounts", {
1559
- debtToRepayUi,
1560
- totalCollateralToWithdrawUi,
1561
- newLeverage,
1562
- currentLeverage,
1563
- });
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
1564
1589
  const baseDebtToRepay = (0, clend_common_1.uiToAmount)(debtToRepayUi, debtDecimals);
1565
- // --- Step 5: Add Buffers to the Debt Repayment Target ---
1566
- // This adds a small buffer for interest that might accrue during the transaction's execution time.
1567
1590
  const timeBufferSec = 15;
1568
1591
  debtToRepay = (0, clend_common_1.addInterestAccrualBuffer)(baseDebtToRepay, debtBankData.borrowApy, timeBufferSec);
1569
- // --- Step 2: Determine total USDC needed from the swap ---
1570
- // The total value we need to get from selling JLP is the debt we must repay
1571
- const totalDebtNeededFromSwap = debtToRepay.add(desiredNetWithdraw);
1572
- // Now, find how much collateral is needed to produce this swap via swap
1573
- const swapQuote = await activeSwapper.getQuote({
1574
- payer: clendAccountData.authority,
1575
- inputMint: collateralMint,
1576
- inputMintDecimals: collateralDecimals,
1577
- outputMint: debtMint,
1578
- outputMintDecimals: debtDecimals,
1579
- inputAmount: totalDebtNeededFromSwap,
1580
- slippageBps,
1581
- swapMode: "ExactOut",
1582
- });
1583
- // The answer from swapper is the total collateral we must withdraw.
1584
- collateralToWithdraw = swapQuote.inAmount;
1585
- // --- Step 4: Final Sanity Check ---
1592
+ // 6. Final sanity check
1586
1593
  if (collateralToWithdraw.gt(collateralAmount)) {
1587
1594
  throw new Error("Total required collateral for withdrawal and swap exceeds the available balance.");
1588
1595
  }
@@ -1744,7 +1751,7 @@ class ClendClient {
1744
1751
  finalDebtToRepayTargetBN: finalDebtToRepayTargetBN.toString(),
1745
1752
  finalAdjustedDebtDeltaUi: finalDebtDeltaUi,
1746
1753
  });
1747
- // 3. Recalculate Collateral Delta needed for the swap (ExactOut)
1754
+ // 3. Recalculate Collateral Delta needed for the swap
1748
1755
  const slippageFactor = new decimal_js_1.default(slippageBps).div(10000);
1749
1756
  if (slippageFactor.gte(1)) {
1750
1757
  throw new Error("Slippage cannot be 100% or more.");
@@ -1842,13 +1849,8 @@ class ClendClient {
1842
1849
  let swapLookupTables = [];
1843
1850
  if (isIncrease) {
1844
1851
  utils_1.logger.info("Building instructions for increasing leverage");
1845
- // Use the finalDebtDeltaUi passed in (already adjusted)
1852
+ // This logic is already ExactIn and correct. No changes needed.
1846
1853
  const additionalDebtAmount = (0, clend_common_1.uiToAmount)(finalDebtDeltaUi, debtBankData.mintDecimals);
1847
- utils_1.logger.debug("additionalDebtAmount to borrow (final adjusted)", {
1848
- additionalDebtAmount: additionalDebtAmount.toString(),
1849
- });
1850
- // Get Jupiter quote for swapping debt token to collateral token (ExactIn)
1851
- const swapMode = "ExactIn";
1852
1854
  const swapQuote = await activeSwapper.getQuote({
1853
1855
  payer: user,
1854
1856
  inputMint: debtBankData.mint,
@@ -1857,77 +1859,50 @@ class ClendClient {
1857
1859
  outputMintDecimals: collateralBankData.mintDecimals,
1858
1860
  inputAmount: additionalDebtAmount,
1859
1861
  slippageBps,
1860
- swapMode,
1861
- });
1862
- utils_1.logger.debug("swapQuote calculated (Increase)", {
1863
- inAmount: swapQuote.inAmount.toString(),
1864
- outAmount: swapQuote.outAmount.toString(),
1865
- otherAmountThreshold: swapQuote.otherAmountThreshold.toString(),
1862
+ swapMode: "ExactIn",
1866
1863
  });
1867
- // Get swap instructions
1868
1864
  const swapIxns = await activeSwapper.getSwapIxns(swapQuote);
1869
- // Borrow the final adjusted debt amount
1870
- const borrowIx = await this.instructions.borrow(clendGroup, clendAccount, user, debtBankData.key, userDebtAta, debtTokenProgram, additionalDebtAmount, // Use final adjusted amount
1871
- remainingAccounts);
1872
- // Calculate additional collateral expected from swap (based on quote)
1865
+ const borrowIx = await this.instructions.borrow(clendGroup, clendAccount, user, debtBankData.key, userDebtAta, debtTokenProgram, additionalDebtAmount, remainingAccounts);
1873
1866
  const additionalCollateralAmount = swapQuote.outAmount;
1874
- utils_1.logger.debug("additionalCollateralAmount expected from swap", {
1875
- additionalCollateralAmount: additionalCollateralAmount.toString(),
1876
- });
1877
- // Deposit additional collateral received from swap
1878
- const depositIx = await this.instructions.deposit(clendGroup, clendAccount, user, collateralBankData.key, userCollateralAta, collateralTokenProgram, additionalCollateralAmount, // Deposit what the swap provides
1879
- true, remainingAccounts);
1867
+ const depositIx = await this.instructions.deposit(clendGroup, clendAccount, user, collateralBankData.key, userCollateralAta, collateralTokenProgram, additionalCollateralAmount, true, remainingAccounts);
1880
1868
  instructions.push(borrowIx, ...swapIxns.ixns, depositIx);
1881
1869
  swapLookupTables = swapIxns.luts;
1882
1870
  }
1883
1871
  else {
1884
- // Decreasing Leverage
1872
+ // --- DECREASING LEVERAGE - NEW ExactIn LOGIC ---
1885
1873
  utils_1.logger.info("Building instructions for decreasing leverage");
1886
- // Use the finalDebtDeltaUi passed in (already includes future buffer)
1887
- const finalDebtToRepayBN = (0, clend_common_1.uiToAmount)(finalDebtDeltaUi, debtBankData.mintDecimals);
1888
- // Use the finalCollateralDeltaUi passed in (already includes slippage adjustment for swap)
1889
- const finalCollateralToWithdrawBN = (0, clend_common_1.uiToAmount)(finalCollateralDeltaUi, collateralBankData.mintDecimals);
1890
- utils_1.logger.debug("Final amounts for decrease operation (Native)", {
1891
- finalDebtToRepayBN: finalDebtToRepayBN.toString(),
1892
- finalCollateralToWithdrawBN: finalCollateralToWithdrawBN.toString(),
1893
- });
1894
- // Get Jupiter quote for swapping collateral token to debt token (ExactOut)
1895
- // Target the final buffered debt repayment amount
1896
- const swapMode = "ExactOut";
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.
1897
1877
  const swapQuote = await activeSwapper.getQuote({
1898
1878
  payer: clendAccountData.authority,
1899
1879
  inputMint: collateralBankData.mint,
1900
1880
  inputMintDecimals: collateralBankData.mintDecimals,
1901
1881
  outputMint: debtBankData.mint,
1902
1882
  outputMintDecimals: debtBankData.mintDecimals,
1903
- inputAmount: finalDebtToRepayBN,
1883
+ inputAmount: collateralToWithdrawBN, // Use the collateral delta as the exact input
1904
1884
  slippageBps,
1905
- swapMode,
1906
- });
1907
- const estimatedCollateralInput = swapQuote.inAmount;
1908
- utils_1.logger.debug("swapQuote calculated (Decrease)", {
1909
- targetOutput: finalDebtToRepayBN.toString(),
1910
- estimatedInput: estimatedCollateralInput,
1911
- maxInputCalculated: finalCollateralToWithdrawBN.toString(), // Compare Jupiter estimate vs our max calc
1885
+ swapMode: "ExactIn",
1912
1886
  });
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;
1913
1890
  const swapIxns = await activeSwapper.getSwapIxns(swapQuote);
1914
- // Withdraw the CALCULATED MAXIMUM collateral needed (includes swap input + buffer)
1915
- const withdrawIx = await this.instructions.withdraw(clendGroup, clendAccount, user, collateralBankData.key, userCollateralAta, collateralTokenProgram, finalCollateralToWithdrawBN, // Use the final adjusted amount from params
1916
- false, // Not withdrawing all
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
1917
1893
  remainingAccounts);
1918
- // Repay the CALCULATED BUFFERED debt amount
1919
- const repayIx = await this.instructions.repay(clendGroup, clendAccount, user, debtBankData.key, userDebtAta, debtTokenProgram, finalDebtToRepayBN, // Use the final adjusted amount from params
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
1920
1896
  false, // Not repaying all
1897
+ true, // Use the new repay_up_to_amount flag
1921
1898
  remainingAccounts);
1922
1899
  instructions.push(withdrawIx, ...swapIxns.ixns, repayIx);
1923
1900
  swapLookupTables = swapIxns.luts;
1924
1901
  }
1925
1902
  // --- Flash Loan Wrapping ---
1926
- const cuIxns = 2; // Assuming 2 compute budget instructions are added later
1927
- // Calculate end index based on actual instructions generated + potential JIT ixns + flash loan ixns
1903
+ const cuIxns = 2;
1928
1904
  const endIndex = new anchor_1.BN(cuIxns + instructions.length + 1 + additionalIxnCount);
1929
1905
  const { beginFlashLoanIx, endFlashLoanIx } = await this.instructions.createFlashLoanInstructions(clendAccount, user, endIndex, remainingAccounts);
1930
- // Create a new array with the flash loan instructions wrapping the existing instructions
1931
1906
  const finalInstructions = [
1932
1907
  beginFlashLoanIx,
1933
1908
  ...instructions,