@carrot-protocol/clend-rpc 0.1.27-swapper1-dev-8d3e6a6 → 0.1.28-emissions-calc-dev-7ecfaac

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