@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/addresses.d.ts +14 -7
- package/dist/addresses.js +30 -11
- package/dist/addresses.js.map +1 -1
- package/dist/events.d.ts +8 -1
- package/dist/events.js +11 -0
- package/dist/events.js.map +1 -1
- package/dist/idl/clend.d.ts +36 -0
- package/dist/idl/clend.js +36 -0
- package/dist/idl/clend.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +5 -2
- package/dist/instructions.js +70 -2
- package/dist/instructions.js.map +1 -1
- package/dist/rpc.d.ts +14 -10
- package/dist/rpc.js +256 -280
- package/dist/rpc.js.map +1 -1
- package/dist/state.d.ts +18 -3
- package/dist/state.js +52 -3
- package/dist/state.js.map +1 -1
- package/dist/utils.d.ts +20 -1
- package/dist/utils.js +82 -5
- package/dist/utils.js.map +1 -1
- package/package.json +2 -2
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 =
|
|
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.
|
|
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
|
|
407
|
+
assetShares,
|
|
343
408
|
assetShareValue: Number(bankData.assetShareValue),
|
|
344
409
|
assetMintDecimals: Number(bankData.mintDecimals),
|
|
345
|
-
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
|
|
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
|
|
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
|
|
1192
|
-
const { ixns: collateralIxns, luts: collateralLuts } = await this.getWithdrawLeverageCollateralIxns(clendGroup, clendAccount, collateralMint, debtMint, collateralParams.collateralBankData.mintDecimals, collateralParams.debtBankData.mintDecimals, withdrawAll, collateralParams.
|
|
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,
|
|
1198
|
-
const { ixns: debtIxns, luts: debtLuts } = await this.getWithdrawLeverageDebtIxns(clendGroup, clendAccount, collateralMint, debtMint, debtParams.collateralBankData.mintDecimals, debtParams.debtBankData.mintDecimals, withdrawAll, debtParams.debtToRepay, debtParams.collateralToWithdraw,
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
//
|
|
1246
|
-
const
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1361
|
-
|
|
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
|
|
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(),
|
|
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
|
|
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
|
|
1441
|
-
|
|
1442
|
-
|
|
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
|
|
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
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
1872
|
+
// --- DECREASING LEVERAGE - NEW ExactIn LOGIC ---
|
|
1884
1873
|
utils_1.logger.info("Building instructions for decreasing leverage");
|
|
1885
|
-
//
|
|
1886
|
-
const
|
|
1887
|
-
//
|
|
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:
|
|
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
|
|
1914
|
-
const withdrawIx = await this.instructions.withdraw(clendGroup, clendAccount, user, collateralBankData.key, userCollateralAta, collateralTokenProgram,
|
|
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
|
|
1918
|
-
const repayIx = await this.instructions.repay(clendGroup, clendAccount, user, debtBankData.key, userDebtAta, debtTokenProgram,
|
|
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;
|
|
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,
|