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