@carrot-protocol/clend-rpc 0.1.27-group-refactor1-dev-a8f2f9c → 0.1.27-group-refactor1-dev-f36a637

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
@@ -501,13 +501,6 @@ class ClendClient {
501
501
  // get required remaining accounts based on account balance
502
502
  const clendAccountData = await this.getClendAccount(clendAccount);
503
503
  let clendAccountActiveBanks = (0, utils_1.getClendAccountActiveBanks)(clendAccountData);
504
- //// If withdrawAll is true, remove the target bank from activeBanks
505
- //// as we wont have a remaining balance after the withdraw operation
506
- //if (withdrawAll === true) {
507
- // clendAccountActiveBanks = clendAccountActiveBanks.filter(
508
- // (b) => !b.equals(bank),
509
- // );
510
- //}
511
504
  // fetch all active banks
512
505
  const activeBankData = [];
513
506
  for (const bank of clendAccountActiveBanks) {
@@ -542,7 +535,7 @@ class ClendClient {
542
535
  const ix = await this.instructions.borrow(clendGroup, clendAccount, this.address(), bank, destinationTokenAccount, tokenProgram, amount, remainingAccounts);
543
536
  return this.send([ix]);
544
537
  }
545
- async repay(clendGroup, clendAccount, mint, amount, repayAll = null) {
538
+ async repay(clendGroup, clendAccount, mint, amount, repayAll, repayUpToAmount) {
546
539
  const bank = (0, addresses_1.getBankPda)(clendGroup, mint);
547
540
  // Get bank data to access the liquidityVault and mint
548
541
  const bankData = await this.getBank(bank);
@@ -558,7 +551,7 @@ class ClendClient {
558
551
  activeBankData.push(bankData);
559
552
  }
560
553
  const remainingAccounts = (0, utils_1.getClendAccountRemainingAccounts)(activeBankData);
561
- 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);
562
555
  return this.send([ix]);
563
556
  }
564
557
  async editGlobalFeeState(clendGroup, newGlobalFeeWallet, newBankInitFlatSolFee, newProgramFeeFixed, newProgramFeeRate) {
@@ -1260,14 +1253,14 @@ class ClendClient {
1260
1253
  let luts = [];
1261
1254
  switch (selectedMint.toString()) {
1262
1255
  case collateralMint.toString():
1263
- const collateralParams = await this.getNetWithdrawLeverageCollateralParams(clendGroup, clendAccount, collateralMint, debtMint, withdrawAmount, withdrawAll, slippageBps, swapperOverride);
1264
- 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);
1265
1258
  ixns = collateralIxns;
1266
1259
  luts = collateralLuts;
1267
1260
  break;
1268
1261
  case debtMint.toString():
1269
- const debtParams = await this.getNetWithdrawLeverageDebtParams(clendGroup, clendAccount, collateralMint, debtMint, withdrawAmount, withdrawAll, slippageBps, swapperOverride);
1270
- 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);
1271
1264
  ixns = debtIxns;
1272
1265
  luts = debtLuts;
1273
1266
  break;
@@ -1277,7 +1270,7 @@ class ClendClient {
1277
1270
  // Send transaction
1278
1271
  return this.send(ixns, [], luts);
1279
1272
  }
1280
- 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) {
1281
1274
  // override swapper if provided
1282
1275
  let activeSwapper = this.swapper;
1283
1276
  if (swapperOverride) {
@@ -1285,54 +1278,44 @@ class ClendClient {
1285
1278
  }
1286
1279
  const debtBank = (0, addresses_1.getBankPda)(clendGroup, debtMint);
1287
1280
  const collateralBank = (0, addresses_1.getBankPda)(clendGroup, collateralMint);
1288
- // Get remaining accounts for flash loan
1289
1281
  const clendAccountData = await this.getClendAccount(clendAccount);
1290
1282
  if (!clendAccountData) {
1291
1283
  throw new Error(`Clend account not found: ${clendAccount.toString()}`);
1292
1284
  }
1293
1285
  let activeBanks = (0, utils_1.getClendAccountActiveBanks)(clendAccountData);
1294
- const activeBankData = [];
1295
- for (const bank of activeBanks) {
1296
- const bankData = await this.getBank(bank);
1297
- activeBankData.push(bankData);
1298
- }
1286
+ const activeBankData = await Promise.all(activeBanks.map((bankPk) => this.getBank(bankPk)));
1299
1287
  const remainingAccounts = (0, utils_1.getClendAccountRemainingAccounts)(activeBankData);
1300
- // Get Jupiter quote for swapping collateral to debt token (ExactOut mode)
1301
- const swapMode = "ExactOut";
1288
+ // Get a fresh 'ExactIn' quote for swapping the total collateral to withdraw
1302
1289
  const swapQuote = await activeSwapper.getQuote({
1303
1290
  payer: clendAccountData.authority,
1304
1291
  inputMint: collateralMint,
1305
1292
  inputMintDecimals: collateralDecimals,
1306
1293
  outputMint: debtMint,
1307
1294
  outputMintDecimals: debtDecimals,
1308
- inputAmount: debtToRepay,
1295
+ inputAmount: collateralToWithdraw, // Use the total collateral to withdraw as the exact input
1309
1296
  slippageBps,
1310
- swapMode,
1311
- });
1312
- // set collateralToSwap to the inAmount from the swap quote
1313
- const collateralToSwap = swapQuote.inAmount;
1314
- utils_1.logger.debug("withdrawLeverage collateralToSwap", {
1315
- collateralToSwap,
1297
+ swapMode: "ExactIn",
1316
1298
  });
1317
- // Get swap instructions
1318
- const swapInputs = {
1319
- inputAmountLamports: new decimal_js_1.default(collateralToSwap.toString()),
1320
- inputMint: collateralMint,
1321
- outputMint: debtMint,
1322
- };
1323
- utils_1.logger.debug("withdrawLeverage swapInputs", {
1324
- 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(),
1325
1308
  });
1309
+ // Get Swap Instructions from the swapper
1326
1310
  const swapIxns = await activeSwapper.getSwapIxns(swapQuote);
1327
- utils_1.logger.info(`swapIxns fetched`);
1311
+ utils_1.logger.info(`swapIxns fetched for ReceiveDebtToken`);
1312
+ // --- ATAs and Clend Instructions ---
1328
1313
  const collateralTokenProgram = (0, utils_1.getTokenProgramForMint)(collateralMint);
1329
1314
  const debtTokenProgram = (0, utils_1.getTokenProgramForMint)(debtMint);
1330
1315
  const userCollateralAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collateralMint, clendAccountData.authority, true, collateralTokenProgram);
1331
1316
  const userDebtAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtMint, clendAccountData.authority, true, debtTokenProgram);
1332
- utils_1.logger.info(`begin crafting ixns`);
1333
- // Create withdraw instruction
1317
+ // Create withdraw instruction: Withdraw the total collateral calculated by the params function
1334
1318
  const withdrawIx = await this.instructions.withdraw(clendGroup, clendAccount, clendAccountData.authority, collateralBank, userCollateralAta, collateralTokenProgram, collateralToWithdraw, withdrawAll, remainingAccounts);
1335
- utils_1.logger.info(`withdrawIx set`);
1336
1319
  // if withdrawAll == true we need to make sure we withdraw emissions from both banks before withdrawing all the bank tokens
1337
1320
  const emissionsIxns = [];
1338
1321
  if (withdrawAll) {
@@ -1356,7 +1339,6 @@ class ClendClient {
1356
1339
  emissionsIxns.push(...withdrawEmissionsIx);
1357
1340
  }
1358
1341
  // debt bank emissions
1359
- // only create if emissions mint is set and flags are set to borrow or both
1360
1342
  if (!debtBankData.emissionsMint.equals(anchor_1.web3.PublicKey.default) &&
1361
1343
  (debtBankData.flags === state_1.BankFlags.BorrowEmissionsActive ||
1362
1344
  debtBankData.flags === state_1.BankFlags.LendingAndBorrowingEmissionsActive)) {
@@ -1366,121 +1348,17 @@ class ClendClient {
1366
1348
  emissionsIxns.push(...withdrawEmissionsIx);
1367
1349
  }
1368
1350
  }
1369
- // Create repay instruction
1370
- const repayIx = await this.instructions.repay(clendGroup, clendAccount, clendAccountData.authority, debtBank, userDebtAta, debtTokenProgram, debtToRepay, withdrawAll, remainingAccounts);
1371
- utils_1.logger.info(`repayIx set`);
1372
- // Assemble all instructions without flash loan
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
1373
1355
  const ixnsWithoutFlashLoan = [
1374
1356
  ...emissionsIxns,
1375
1357
  withdrawIx,
1376
1358
  ...swapIxns.ixns,
1377
1359
  repayIx,
1378
1360
  ];
1379
- utils_1.logger.info(`ixnsWithoutFlashLoan set`);
1380
- // Create flash loan instructions
1381
- const cuIxns = 2;
1382
- const endIndex = new anchor_1.BN(cuIxns + ixnsWithoutFlashLoan.length + 1 + additionalIxnCount);
1383
- const { beginFlashLoanIx, endFlashLoanIx } = await this.instructions.createFlashLoanInstructions(clendAccount, clendAccountData.authority, endIndex, remainingAccounts);
1384
- // Assemble all instructions in the correct order
1385
- const instructions = [
1386
- beginFlashLoanIx,
1387
- ...ixnsWithoutFlashLoan,
1388
- endFlashLoanIx,
1389
- ];
1390
- return { ixns: instructions, luts: swapIxns.luts };
1391
- }
1392
- async getWithdrawLeverageDebtIxns(clendGroup, clendAccount, collateralMint, debtMint, collateralDecimals, debtDecimals, withdrawAll, debtToRepay, collateralToWithdraw, desiredNetDebtToReceive, slippageBps, additionalIxnCount, swapperOverride) {
1393
- // override swapper if provided
1394
- let activeSwapper = this.swapper;
1395
- if (swapperOverride) {
1396
- activeSwapper = swapperOverride;
1397
- }
1398
- const debtBank = (0, addresses_1.getBankPda)(clendGroup, debtMint);
1399
- const collateralBank = (0, addresses_1.getBankPda)(clendGroup, collateralMint);
1400
- const clendAccountData = await this.getClendAccount(clendAccount);
1401
- if (!clendAccountData) {
1402
- throw new Error(`Clend account not found: ${clendAccount.toString()}`);
1403
- }
1404
- let activeBanks = (0, utils_1.getClendAccountActiveBanks)(clendAccountData);
1405
- const activeBankData = await Promise.all(activeBanks.map((bankPk) => this.getBank(bankPk)));
1406
- const remainingAccounts = (0, utils_1.getClendAccountRemainingAccounts)(activeBankData);
1407
- // 1. Determine Total USDC Needed from the Swap
1408
- // This is the amount needed to repay Clend's debt AND give the user their desired net withdrawal.
1409
- const totalDebtToReceive = debtToRepay.add(desiredNetDebtToReceive);
1410
- const swapMode = "ExactIn";
1411
- const swapQuote = await activeSwapper.getQuote({
1412
- payer: clendAccountData.authority,
1413
- inputMint: collateralMint,
1414
- inputMintDecimals: collateralDecimals,
1415
- outputMint: debtMint,
1416
- outputMintDecimals: debtDecimals,
1417
- inputAmount: collateralToWithdraw,
1418
- slippageBps,
1419
- swapMode,
1420
- });
1421
- const minUsdcReceivedFromSwap_BN = new anchor_1.BN(swapQuote.otherAmountThreshold);
1422
- utils_1.logger.debug("getWithdrawLeverageDebtIxns: collateral and debt details", {
1423
- debtToRepay: debtToRepay.toString(),
1424
- desiredNetDebtToReceive: desiredNetDebtToReceive.toString(),
1425
- totalDebtToReceive: totalDebtToReceive.toString(),
1426
- minUsdcReceivedFromSwap_BN: minUsdcReceivedFromSwap_BN.toString(),
1427
- });
1428
- // 3. Get Swap Instructions from swapper
1429
- const swapIxns = await activeSwapper.getSwapIxns(swapQuote);
1430
- utils_1.logger.info(`swapIxns fetched for ReceiveDebtToken`);
1431
- // --- ATAs and Clend Instructions (largely similar structure) ---
1432
- const collateralTokenProgram = (0, utils_1.getTokenProgramForMint)(collateralMint);
1433
- const debtTokenProgram = (0, utils_1.getTokenProgramForMint)(debtMint);
1434
- const userCollateralAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collateralMint, clendAccountData.authority, true, collateralTokenProgram);
1435
- const userDebtAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtMint, clendAccountData.authority, true, debtTokenProgram);
1436
- // Create withdraw instruction: Withdraw ALL the JLP calculated by params function
1437
- const withdrawIx = await this.instructions.withdraw(clendGroup, clendAccount, clendAccountData.authority, collateralBank, userCollateralAta, collateralTokenProgram, collateralToWithdraw, // Withdraw all the JLP needed for the full swap
1438
- withdrawAll, // This flag is for Clend's internal logic
1439
- remainingAccounts);
1440
- // if withdrawAll == true we need to make sure we withdraw emissions from both banks before withdrawing all the bank tokens
1441
- const emissionsIxns = [];
1442
- if (withdrawAll) {
1443
- // get bank data
1444
- const collateralBankData = activeBankData.find((b) => b.mint.equals(collateralMint));
1445
- if (!collateralBankData) {
1446
- throw new Error(`Collateral bank not found: ${collateralMint.toString()}`);
1447
- }
1448
- const debtBankData = activeBankData.find((b) => b.mint.equals(debtMint));
1449
- if (!debtBankData) {
1450
- throw new Error(`Debt bank not found: ${debtMint.toString()}`);
1451
- }
1452
- // collateral bank emissions
1453
- if (!collateralBankData.emissionsMint.equals(anchor_1.web3.PublicKey.default) &&
1454
- (collateralBankData.flags === state_1.BankFlags.LendingEmissionsActive ||
1455
- collateralBankData.flags ===
1456
- state_1.BankFlags.LendingAndBorrowingEmissionsActive)) {
1457
- const emissionsMint = collateralBankData.emissionsMint;
1458
- const emissionsMintTokenProgram = await (0, utils_1.getTokenProgramForMintFromRpc)(this.connection, emissionsMint);
1459
- const withdrawEmissionsIx = await this.instructions.withdrawEmissions(clendGroup, clendAccount, clendAccountData.authority, collateralBank, emissionsMint, emissionsMintTokenProgram);
1460
- emissionsIxns.push(...withdrawEmissionsIx);
1461
- }
1462
- // debt bank emissions
1463
- if (!debtBankData.emissionsMint.equals(anchor_1.web3.PublicKey.default) &&
1464
- (debtBankData.flags === state_1.BankFlags.BorrowEmissionsActive ||
1465
- debtBankData.flags === state_1.BankFlags.LendingAndBorrowingEmissionsActive)) {
1466
- const emissionsMint = debtBankData.emissionsMint;
1467
- const emissionsMintTokenProgram = await (0, utils_1.getTokenProgramForMintFromRpc)(this.connection, emissionsMint);
1468
- const withdrawEmissionsIx = await this.instructions.withdrawEmissions(clendGroup, clendAccount, clendAccountData.authority, debtBank, emissionsMint, emissionsMintTokenProgram);
1469
- emissionsIxns.push(...withdrawEmissionsIx);
1470
- }
1471
- }
1472
- // Create repay instruction: Repay the calculated debt portion to Clend
1473
- const repayIx = await this.instructions.repay(clendGroup, clendAccount, clendAccountData.authority, debtBank, userDebtAta, debtTokenProgram, debtToRepay, // Repay the specified debt amount
1474
- withdrawAll, // This flag is for Clend's internal logic
1475
- remainingAccounts);
1476
- // Assemble instructions
1477
- const ixnsWithoutFlashLoan = [
1478
- ...emissionsIxns,
1479
- withdrawIx,
1480
- ...swapIxns.ixns, // Swap all withdrawn collateral to debt
1481
- repayIx, // Repay the debt portion
1482
- ]; // User is left with the remaining debt in their ATA
1483
- // Flash Loan Wrapping (same as before)
1361
+ // Flash Loan Wrapping
1484
1362
  const cuIxns = 2;
1485
1363
  const endIndex = new anchor_1.BN(cuIxns + ixnsWithoutFlashLoan.length + 1 + additionalIxnCount);
1486
1364
  const { beginFlashLoanIx, endFlashLoanIx } = await this.instructions.createFlashLoanInstructions(clendAccount, clendAccountData.authority, endIndex, remainingAccounts);
@@ -1491,21 +1369,13 @@ class ClendClient {
1491
1369
  ];
1492
1370
  return { ixns: instructions, luts: swapIxns.luts };
1493
1371
  }
1494
- // caller will receive desiredNetWithdraw, the position will be adjusted accordingly
1495
- // receive the collateral mint
1496
- async getNetWithdrawLeverageCollateralParams(clendGroup, clendAccount, collateralMint, debtMint, desiredNetWithdraw, withdrawAll, slippageBps, swapperOverride) {
1497
- // override swapper if provided
1498
- let activeSwapper = this.swapper;
1499
- if (swapperOverride) {
1500
- activeSwapper = swapperOverride;
1501
- }
1502
- utils_1.logger.info("getNetWithdrawParams", {
1372
+ async getNetWithdrawLeverageCollateralParams(clendGroup, clendAccount, collateralMint, debtMint, desiredNetWithdraw, withdrawAll) {
1373
+ utils_1.logger.info("getNetWithdrawLeverageCollateralParams", {
1503
1374
  desiredNetWithdraw: desiredNetWithdraw.toString(),
1504
1375
  withdrawAll,
1505
1376
  });
1506
- // if desiredNetWithdraw is zero, throw an error
1507
1377
  if (desiredNetWithdraw.isZero() && !withdrawAll) {
1508
- throw new Error("Desired net withdrawal is zero");
1378
+ throw new Error("Desired net withdrawal is zero for a partial withdraw.");
1509
1379
  }
1510
1380
  const collateralBank = (0, addresses_1.getBankPda)(clendGroup, collateralMint);
1511
1381
  const debtBank = (0, addresses_1.getBankPda)(clendGroup, debtMint);
@@ -1517,7 +1387,6 @@ class ClendClient {
1517
1387
  if (!clendAccountData) {
1518
1388
  throw new Error(`Clend account not found: ${clendAccount.toString()}`);
1519
1389
  }
1520
- // fetch balances and pricing
1521
1390
  let debtBalance;
1522
1391
  let debtPrice;
1523
1392
  let collateralBalance;
@@ -1536,76 +1405,32 @@ class ClendClient {
1536
1405
  throw new Error("Could not find debt or collateral position in account");
1537
1406
  }
1538
1407
  const collateralAmount = collateralBalance.assetBalance;
1539
- const collateralAmountUI = collateralBalance.assetBalanceUi;
1540
- const collateralValue = collateralBalance.assetValue;
1541
- const collateralDecimals = collateralBankData.mintDecimals;
1542
1408
  const debtAmount = debtBalance.liabilityBalance;
1543
- const debtAmountUI = debtBalance.liabilityBalanceUi;
1544
- const debtValue = debtBalance.liabilityValue;
1409
+ const collateralDecimals = collateralBankData.mintDecimals;
1545
1410
  const debtDecimals = debtBankData.mintDecimals;
1546
- const desiredNetWithdrawUi = (0, clend_common_1.amountToUi)(desiredNetWithdraw, debtDecimals);
1547
- utils_1.logger.debug("Current amounts", {
1548
- desiredNetWithdrawUi,
1549
- collateralAmount: collateralAmount.toString(),
1550
- collateralAmountUI,
1551
- collateralValue,
1552
- debtAmount: debtAmount.toString(),
1553
- debtAmountUI,
1554
- debtValue,
1555
- });
1556
- // --- Determine collateral to withdraw and debt to repay ---
1557
1411
  let collateralToWithdraw;
1558
1412
  let debtToRepay;
1559
1413
  if (withdrawAll) {
1560
1414
  utils_1.logger.info("WithdrawAll selected");
1561
1415
  collateralToWithdraw = collateralAmount;
1562
- debtToRepay = (0, clend_common_1.calculateLiabilityInterest)(debtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), new Date().getTime() / 1000);
1563
- utils_1.logger.info(`calculated debt to repay`, {
1564
- debtToRepayWithInterest: debtToRepay.toString(),
1565
- debtToRepayRaw: debtAmount.toString(),
1566
- });
1416
+ debtToRepay = (0, clend_common_1.calculateLiabilityInterest)(debtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), Math.floor(Date.now() / 1000));
1567
1417
  }
1568
1418
  else {
1569
- utils_1.logger.info("Calculating partial deleverage for net withdrawal");
1570
- // 1. Calculate estimated current debt accurately
1419
+ utils_1.logger.info("Calculating partial deleverage amounts");
1571
1420
  const estimatedCurrentDebt = (0, clend_common_1.calculateLiabilityInterest)(debtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), Math.floor(Date.now() / 1000));
1572
1421
  const estimatedCurrentDebtUi = (0, clend_common_1.amountToUi)(estimatedCurrentDebt, debtDecimals);
1573
1422
  const collateralAmountUI = (0, clend_common_1.amountToUi)(collateralAmount, collateralDecimals);
1574
- const { debtToRepayUi, totalCollateralToWithdrawUi, newLeverage, currentLeverage, } = (0, clend_common_1.computeWithdrawLeverageAmounts)(collateralAmountUI, estimatedCurrentDebtUi, desiredNetWithdrawUi, collateralPrice, debtPrice);
1575
- utils_1.logger.info("computeWithdrawLeverageAmounts", {
1576
- debtToRepayUi,
1577
- totalCollateralToWithdrawUi,
1578
- newLeverage,
1579
- currentLeverage,
1580
- });
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);
1581
1426
  const baseDebtToRepay = (0, clend_common_1.uiToAmount)(debtToRepayUi, debtDecimals);
1582
- // --- Step 5: Add Buffers to the Debt Repayment Target ---
1583
- // This adds a small buffer for interest that might accrue during the transaction's execution time.
1584
1427
  const timeBufferSec = 15;
1585
1428
  debtToRepay = (0, clend_common_1.addInterestAccrualBuffer)(baseDebtToRepay, debtBankData.borrowApy, timeBufferSec);
1586
- // --- Step 6: Get a Jupiter Quote for the Swap ---
1587
- // Query Jupiter for an EXACT_OUT swap to find out exactly how much collateral
1588
- // is needed to get the final amount of debt we need to repay.
1589
- const swapQuote = await activeSwapper.getQuote({
1590
- payer: clendAccountData.authority,
1591
- inputMint: collateralMint,
1592
- inputMintDecimals: collateralDecimals,
1593
- outputMint: debtMint,
1594
- outputMintDecimals: debtDecimals,
1595
- inputAmount: debtToRepay,
1596
- slippageBps,
1597
- swapMode: "ExactOut",
1598
- });
1599
- const collateralNeededForSwap = new anchor_1.BN(swapQuote.inAmount);
1600
- // --- Step 7: Calculate the Final Total Collateral to Withdraw ---
1601
- // This is the sum of what the user gets (desiredNetWithdraw) and what the swap needs.
1602
- collateralToWithdraw = desiredNetWithdraw.add(collateralNeededForSwap);
1603
- // Final sanity check to ensure we don't try to withdraw more than exists.
1604
1429
  if (collateralToWithdraw.gt(collateralAmount)) {
1605
- 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.");
1606
1431
  }
1607
1432
  }
1608
- utils_1.logger.debug("Final calculated amounts", {
1433
+ utils_1.logger.debug("Final calculated amounts from params function", {
1609
1434
  collateralToWithdraw: collateralToWithdraw.toString(),
1610
1435
  debtToRepay: debtToRepay.toString(),
1611
1436
  });
@@ -1616,9 +1441,80 @@ class ClendClient {
1616
1441
  debtBankData,
1617
1442
  };
1618
1443
  }
1619
- // caller will receive desiredNetWithdraw, the position will be adjusted accordingly
1620
- // receive the debt mint
1621
- 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}`);
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) {
1622
1518
  // override swapper if provided
1623
1519
  let activeSwapper = this.swapper;
1624
1520
  if (swapperOverride) {
@@ -1669,53 +1565,31 @@ class ClendClient {
1669
1565
  let debtToRepay;
1670
1566
  if (withdrawAll) {
1671
1567
  utils_1.logger.info("WithdrawAll selected for receiving debt token");
1672
- collateralToWithdraw = collateralAmount; // All JLP will be swapped
1568
+ collateralToWithdraw = collateralAmount; // All collateral will be swapped
1673
1569
  debtToRepay = (0, clend_common_1.calculateLiabilityInterest)(currentDebtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), Math.floor(Date.now() / 1000));
1674
- utils_1.logger.info(`calculated debt to repay`, {
1570
+ utils_1.logger.info(`calculated debt to repay for withdrawAll`, {
1675
1571
  debtToRepayWithInterest: debtToRepay.toString(),
1676
- debtToRepayRaw: debtAmount.toString(),
1677
1572
  });
1678
1573
  }
1679
1574
  else {
1680
- 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");
1681
1576
  // 1. Calculate estimated current debt accurately
1682
1577
  const estimatedCurrentDebt = (0, clend_common_1.calculateLiabilityInterest)(currentDebtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), Math.floor(Date.now() / 1000));
1683
1578
  const estimatedCurrentDebtUi = (0, clend_common_1.amountToUi)(estimatedCurrentDebt, debtDecimals);
1684
1579
  const collateralAmountUI = (0, clend_common_1.amountToUi)(collateralAmount, collateralDecimals);
1685
1580
  const desiredNetWithdrawalOfDebtUi = (0, clend_common_1.amountToUi)(desiredNetWithdraw, debtDecimals);
1686
- // Convert the value of the desired debt withdrawal into an equivalent amount of collateral
1687
- // This is the "collateral-equivalent" value of the equity being removed.
1581
+ // 2. Convert the desired net debt withdrawal into its collateral-equivalent value
1688
1582
  const netWithdrawAmountInCollateralUi = (0, clend_common_1.calculateWeightedValue)(desiredNetWithdrawalOfDebtUi, debtPrice, 1) /
1689
1583
  collateralPrice;
1690
- const { debtToRepayUi, totalCollateralToWithdrawUi, newLeverage, currentLeverage, } = (0, clend_common_1.computeWithdrawLeverageAmounts)(collateralAmountUI, estimatedCurrentDebtUi, netWithdrawAmountInCollateralUi, collateralPrice, debtPrice);
1691
- utils_1.logger.info("computeWithdrawLeverageAmounts", {
1692
- debtToRepayUi,
1693
- totalCollateralToWithdrawUi,
1694
- newLeverage,
1695
- currentLeverage,
1696
- });
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
1697
1589
  const baseDebtToRepay = (0, clend_common_1.uiToAmount)(debtToRepayUi, debtDecimals);
1698
- // --- Step 5: Add Buffers to the Debt Repayment Target ---
1699
- // This adds a small buffer for interest that might accrue during the transaction's execution time.
1700
1590
  const timeBufferSec = 15;
1701
1591
  debtToRepay = (0, clend_common_1.addInterestAccrualBuffer)(baseDebtToRepay, debtBankData.borrowApy, timeBufferSec);
1702
- // --- Step 2: Determine total USDC needed from the swap ---
1703
- // The total value we need to get from selling JLP is the debt we must repay
1704
- const totalDebtNeededFromSwap = debtToRepay.add(desiredNetWithdraw);
1705
- // Now, find how much collateral is needed to produce this swap via swap
1706
- const swapQuote = await activeSwapper.getQuote({
1707
- payer: clendAccountData.authority,
1708
- inputMint: collateralMint,
1709
- inputMintDecimals: collateralDecimals,
1710
- outputMint: debtMint,
1711
- outputMintDecimals: debtDecimals,
1712
- inputAmount: totalDebtNeededFromSwap,
1713
- slippageBps,
1714
- swapMode: "ExactOut",
1715
- });
1716
- // The answer from swapper is the total collateral we must withdraw.
1717
- collateralToWithdraw = swapQuote.inAmount;
1718
- // --- Step 4: Final Sanity Check ---
1592
+ // 6. Final sanity check
1719
1593
  if (collateralToWithdraw.gt(collateralAmount)) {
1720
1594
  throw new Error("Total required collateral for withdrawal and swap exceeds the available balance.");
1721
1595
  }
@@ -1877,7 +1751,7 @@ class ClendClient {
1877
1751
  finalDebtToRepayTargetBN: finalDebtToRepayTargetBN.toString(),
1878
1752
  finalAdjustedDebtDeltaUi: finalDebtDeltaUi,
1879
1753
  });
1880
- // 3. Recalculate Collateral Delta needed for the swap (ExactOut)
1754
+ // 3. Recalculate Collateral Delta needed for the swap
1881
1755
  const slippageFactor = new decimal_js_1.default(slippageBps).div(10000);
1882
1756
  if (slippageFactor.gte(1)) {
1883
1757
  throw new Error("Slippage cannot be 100% or more.");
@@ -1975,13 +1849,8 @@ class ClendClient {
1975
1849
  let swapLookupTables = [];
1976
1850
  if (isIncrease) {
1977
1851
  utils_1.logger.info("Building instructions for increasing leverage");
1978
- // Use the finalDebtDeltaUi passed in (already adjusted)
1852
+ // This logic is already ExactIn and correct. No changes needed.
1979
1853
  const additionalDebtAmount = (0, clend_common_1.uiToAmount)(finalDebtDeltaUi, debtBankData.mintDecimals);
1980
- utils_1.logger.debug("additionalDebtAmount to borrow (final adjusted)", {
1981
- additionalDebtAmount: additionalDebtAmount.toString(),
1982
- });
1983
- // Get Jupiter quote for swapping debt token to collateral token (ExactIn)
1984
- const swapMode = "ExactIn";
1985
1854
  const swapQuote = await activeSwapper.getQuote({
1986
1855
  payer: user,
1987
1856
  inputMint: debtBankData.mint,
@@ -1990,77 +1859,50 @@ class ClendClient {
1990
1859
  outputMintDecimals: collateralBankData.mintDecimals,
1991
1860
  inputAmount: additionalDebtAmount,
1992
1861
  slippageBps,
1993
- swapMode,
1994
- });
1995
- utils_1.logger.debug("swapQuote calculated (Increase)", {
1996
- inAmount: swapQuote.inAmount.toString(),
1997
- outAmount: swapQuote.outAmount.toString(),
1998
- otherAmountThreshold: swapQuote.otherAmountThreshold.toString(),
1862
+ swapMode: "ExactIn",
1999
1863
  });
2000
- // Get swap instructions
2001
1864
  const swapIxns = await activeSwapper.getSwapIxns(swapQuote);
2002
- // Borrow the final adjusted debt amount
2003
- const borrowIx = await this.instructions.borrow(clendGroup, clendAccount, user, debtBankData.key, userDebtAta, debtTokenProgram, additionalDebtAmount, // Use final adjusted amount
2004
- remainingAccounts);
2005
- // 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);
2006
1866
  const additionalCollateralAmount = swapQuote.outAmount;
2007
- utils_1.logger.debug("additionalCollateralAmount expected from swap", {
2008
- additionalCollateralAmount: additionalCollateralAmount.toString(),
2009
- });
2010
- // Deposit additional collateral received from swap
2011
- const depositIx = await this.instructions.deposit(clendGroup, clendAccount, user, collateralBankData.key, userCollateralAta, collateralTokenProgram, additionalCollateralAmount, // Deposit what the swap provides
2012
- true, remainingAccounts);
1867
+ const depositIx = await this.instructions.deposit(clendGroup, clendAccount, user, collateralBankData.key, userCollateralAta, collateralTokenProgram, additionalCollateralAmount, true, remainingAccounts);
2013
1868
  instructions.push(borrowIx, ...swapIxns.ixns, depositIx);
2014
1869
  swapLookupTables = swapIxns.luts;
2015
1870
  }
2016
1871
  else {
2017
- // Decreasing Leverage
1872
+ // --- DECREASING LEVERAGE - NEW ExactIn LOGIC ---
2018
1873
  utils_1.logger.info("Building instructions for decreasing leverage");
2019
- // Use the finalDebtDeltaUi passed in (already includes future buffer)
2020
- const finalDebtToRepayBN = (0, clend_common_1.uiToAmount)(finalDebtDeltaUi, debtBankData.mintDecimals);
2021
- // Use the finalCollateralDeltaUi passed in (already includes slippage adjustment for swap)
2022
- const finalCollateralToWithdrawBN = (0, clend_common_1.uiToAmount)(finalCollateralDeltaUi, collateralBankData.mintDecimals);
2023
- utils_1.logger.debug("Final amounts for decrease operation (Native)", {
2024
- finalDebtToRepayBN: finalDebtToRepayBN.toString(),
2025
- finalCollateralToWithdrawBN: finalCollateralToWithdrawBN.toString(),
2026
- });
2027
- // Get Jupiter quote for swapping collateral token to debt token (ExactOut)
2028
- // Target the final buffered debt repayment amount
2029
- 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.
2030
1877
  const swapQuote = await activeSwapper.getQuote({
2031
1878
  payer: clendAccountData.authority,
2032
1879
  inputMint: collateralBankData.mint,
2033
1880
  inputMintDecimals: collateralBankData.mintDecimals,
2034
1881
  outputMint: debtBankData.mint,
2035
1882
  outputMintDecimals: debtBankData.mintDecimals,
2036
- inputAmount: finalDebtToRepayBN,
1883
+ inputAmount: collateralToWithdrawBN, // Use the collateral delta as the exact input
2037
1884
  slippageBps,
2038
- swapMode,
2039
- });
2040
- const estimatedCollateralInput = swapQuote.inAmount;
2041
- utils_1.logger.debug("swapQuote calculated (Decrease)", {
2042
- targetOutput: finalDebtToRepayBN.toString(),
2043
- estimatedInput: estimatedCollateralInput,
2044
- maxInputCalculated: finalCollateralToWithdrawBN.toString(), // Compare Jupiter estimate vs our max calc
1885
+ swapMode: "ExactIn",
2045
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;
2046
1890
  const swapIxns = await activeSwapper.getSwapIxns(swapQuote);
2047
- // Withdraw the CALCULATED MAXIMUM collateral needed (includes swap input + buffer)
2048
- const withdrawIx = await this.instructions.withdraw(clendGroup, clendAccount, user, collateralBankData.key, userCollateralAta, collateralTokenProgram, finalCollateralToWithdrawBN, // Use the final adjusted amount from params
2049
- 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
2050
1893
  remainingAccounts);
2051
- // Repay the CALCULATED BUFFERED debt amount
2052
- 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
2053
1896
  false, // Not repaying all
1897
+ true, // Use the new repay_up_to_amount flag
2054
1898
  remainingAccounts);
2055
1899
  instructions.push(withdrawIx, ...swapIxns.ixns, repayIx);
2056
1900
  swapLookupTables = swapIxns.luts;
2057
1901
  }
2058
1902
  // --- Flash Loan Wrapping ---
2059
- const cuIxns = 2; // Assuming 2 compute budget instructions are added later
2060
- // Calculate end index based on actual instructions generated + potential JIT ixns + flash loan ixns
1903
+ const cuIxns = 2;
2061
1904
  const endIndex = new anchor_1.BN(cuIxns + instructions.length + 1 + additionalIxnCount);
2062
1905
  const { beginFlashLoanIx, endFlashLoanIx } = await this.instructions.createFlashLoanInstructions(clendAccount, user, endIndex, remainingAccounts);
2063
- // Create a new array with the flash loan instructions wrapping the existing instructions
2064
1906
  const finalInstructions = [
2065
1907
  beginFlashLoanIx,
2066
1908
  ...instructions,