@carrot-protocol/clend-rpc 0.1.27-group-refactor1-dev-0a2c078 → 0.1.27-swapper1-dev-0f3d67b
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 +7 -14
- package/dist/addresses.js +11 -30
- package/dist/addresses.js.map +1 -1
- package/dist/events.d.ts +1 -8
- package/dist/events.js +0 -11
- package/dist/events.js.map +1 -1
- package/dist/idl/clend.d.ts +1 -40
- package/dist/idl/clend.js +1 -40
- package/dist/idl/clend.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +4 -8
- package/dist/instructions.js +6 -101
- package/dist/instructions.js.map +1 -1
- package/dist/rpc.d.ts +22 -31
- package/dist/rpc.js +301 -374
- package/dist/rpc.js.map +1 -1
- package/dist/state.d.ts +4 -34
- package/dist/state.js +3 -61
- package/dist/state.js.map +1 -1
- package/dist/swapper/interface.d.ts +1 -4
- package/dist/swapper/jupiterSwapper.d.ts +2 -1
- package/dist/swapper/jupiterSwapper.js +2 -3
- package/dist/swapper/jupiterSwapper.js.map +1 -1
- package/dist/swapper/mockJupiterSwapper.d.ts +1 -1
- package/dist/swapper/mockJupiterSwapper.js +1 -2
- package/dist/swapper/mockJupiterSwapper.js.map +1 -1
- package/dist/utils.d.ts +1 -49
- package/dist/utils.js +0 -129
- package/dist/utils.js.map +1 -1
- package/package.json +2 -2
package/dist/rpc.js
CHANGED
|
@@ -33,25 +33,10 @@ class ClendClient {
|
|
|
33
33
|
address() {
|
|
34
34
|
return this.provider.wallet.publicKey;
|
|
35
35
|
}
|
|
36
|
-
// determine type of oracle then fetch price from mint
|
|
37
|
-
// TODO: do it better than this hack
|
|
38
|
-
async getOraclePrice(mint) {
|
|
39
|
-
try {
|
|
40
|
-
return await this.getPythOraclePrice(mint);
|
|
41
|
-
}
|
|
42
|
-
catch (e) {
|
|
43
|
-
try {
|
|
44
|
-
return await this.getSBOraclePrice(mint);
|
|
45
|
-
}
|
|
46
|
-
catch (sbError) {
|
|
47
|
-
throw sbError;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
36
|
/**
|
|
52
37
|
* Get pyth oracle price for a token
|
|
53
38
|
*/
|
|
54
|
-
async
|
|
39
|
+
async getOraclePrice(mint) {
|
|
55
40
|
const oracle = this.getOracle(mint);
|
|
56
41
|
// get oracle account info
|
|
57
42
|
const oracleAccountInfo = await this.connection.getAccountInfo(oracle, {
|
|
@@ -70,34 +55,10 @@ class ClendClient {
|
|
|
70
55
|
// get ema price
|
|
71
56
|
const emaPrice = new anchor_1.BN(oracleData.priceMessage.emaPrice);
|
|
72
57
|
// return price
|
|
73
|
-
return (
|
|
58
|
+
return (emaPrice.toNumber() *
|
|
59
|
+
Math.pow(10, Number(oracleData.priceMessage.exponent)));
|
|
74
60
|
}
|
|
75
|
-
|
|
76
|
-
const oracle = this.getOracle(mint);
|
|
77
|
-
// Fetch the raw account info.
|
|
78
|
-
const feedAccountInfo = await this.connection.getAccountInfo(oracle, "processed");
|
|
79
|
-
if (feedAccountInfo === null) {
|
|
80
|
-
throw new Error(`Feed account ${oracle.toString()} not found.`);
|
|
81
|
-
}
|
|
82
|
-
// Use our manual parser instead of the Anchor coder.
|
|
83
|
-
const pullFeedAccountData = (0, utils_1.parsePullFeedAccountData)(feedAccountInfo.data);
|
|
84
|
-
if (!pullFeedAccountData ||
|
|
85
|
-
!pullFeedAccountData.submissions ||
|
|
86
|
-
pullFeedAccountData.submissions.length === 0) {
|
|
87
|
-
throw new Error(`No valid submissions found for feed ${oracle.toString()}.`);
|
|
88
|
-
}
|
|
89
|
-
// The latest submission will be the first element after sorting.
|
|
90
|
-
const latestSubmission = pullFeedAccountData.submissions[0];
|
|
91
|
-
// The 'value' is a BN (BigNumber), so we convert it to a standard JavaScript number.
|
|
92
|
-
// Be aware of potential precision loss if the number is larger than Number.MAX_SAFE_INTEGER.
|
|
93
|
-
const price = Number(latestSubmission.value);
|
|
94
|
-
// The exponent for Switchboard v2/v3 feeds is typically -18, but you should
|
|
95
|
-
// confirm this for the specific feed you are using.
|
|
96
|
-
const exponent = -18;
|
|
97
|
-
const priceDecimal = (0, utils_1.calculatePriceUi)(price, exponent);
|
|
98
|
-
return priceDecimal;
|
|
99
|
-
}
|
|
100
|
-
// find oracle for a mint from in memory cache
|
|
61
|
+
// find oracle for a mint
|
|
101
62
|
getOracle(mint) {
|
|
102
63
|
const oracleStr = this.mintOracleMapping.get(mint.toString());
|
|
103
64
|
if (!oracleStr) {
|
|
@@ -197,73 +158,14 @@ class ClendClient {
|
|
|
197
158
|
return this.send([initIx]);
|
|
198
159
|
}
|
|
199
160
|
// Bank Operations
|
|
200
|
-
async addBank(clendGroup, bankMint,
|
|
161
|
+
async addBank(clendGroup, bankMint, pythOracle, pythOracleFeedId, bankConfig, interestRateConfig) {
|
|
201
162
|
const tokenProgram = (0, utils_1.getTokenProgramForMint)(bankMint);
|
|
202
163
|
const feeStateAccountData = await this.getFeeState();
|
|
203
164
|
const globalFeeWallet = feeStateAccountData.globalFeeWallet;
|
|
204
|
-
const { bank, ixns } = await this.instructions.addBank(clendGroup, this.address(), globalFeeWallet, bankMint, tokenProgram,
|
|
165
|
+
const { bank, ixns } = await this.instructions.addBank(clendGroup, this.address(), globalFeeWallet, bankMint, tokenProgram, pythOracle, pythOracleFeedId, bankConfig, interestRateConfig);
|
|
205
166
|
const txSig = await this.send(ixns);
|
|
206
167
|
return { bank, txSig };
|
|
207
168
|
}
|
|
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
|
-
}
|
|
267
169
|
// Account fetching Operations
|
|
268
170
|
async getClendGroup(clendGroup) {
|
|
269
171
|
const accountInfo = await this.connection.getAccountInfo(clendGroup, {
|
|
@@ -338,9 +240,9 @@ class ClendClient {
|
|
|
338
240
|
collectedGroupFeesOutstanding: Number((0, utils_1.wrappedI80F48toBigNumber)(data.collectedGroupFeesOutstanding)),
|
|
339
241
|
lastUpdate: new anchor_1.BN(data.lastUpdate),
|
|
340
242
|
config: bankConfig,
|
|
341
|
-
flags:
|
|
243
|
+
flags: new anchor_1.BN(data.flags),
|
|
342
244
|
emissionsRate: new anchor_1.BN(data.emissionsRate),
|
|
343
|
-
emissionsRemaining:
|
|
245
|
+
emissionsRemaining: (0, utils_1.wrappedI80F48toBigNumber)(data.emissionsRemaining),
|
|
344
246
|
emissionsMint: new anchor_1.web3.PublicKey(data.emissionsMint),
|
|
345
247
|
collectedProgramFeesOutstanding: Number((0, utils_1.wrappedI80F48toBigNumber)(data.collectedProgramFeesOutstanding)),
|
|
346
248
|
assetAmount,
|
|
@@ -393,29 +295,21 @@ class ClendClient {
|
|
|
393
295
|
const bankData = await this.getBank(bank);
|
|
394
296
|
const bankMint = bankData.mint;
|
|
395
297
|
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);
|
|
402
298
|
const lendingAccountBalance = (0, state_1.newClendAccountBalance)({
|
|
403
299
|
active,
|
|
404
300
|
bankPk: bank,
|
|
405
301
|
bankMint,
|
|
406
302
|
bankAssetTag: Number(balance.bankAssetTag),
|
|
407
|
-
assetShares,
|
|
303
|
+
assetShares: Number((0, utils_1.wrappedI80F48toBigNumber)(balance.assetShares)),
|
|
408
304
|
assetShareValue: Number(bankData.assetShareValue),
|
|
409
305
|
assetMintDecimals: Number(bankData.mintDecimals),
|
|
410
|
-
liabilityShares,
|
|
306
|
+
liabilityShares: Number((0, utils_1.wrappedI80F48toBigNumber)(balance.liabilityShares)),
|
|
411
307
|
liabilityShareValue: Number(bankData.liabilityShareValue),
|
|
412
308
|
liabilityMintDecimals: Number(bankData.mintDecimals),
|
|
413
309
|
lastUpdate: new anchor_1.BN(balance.lastUpdate),
|
|
414
310
|
assetWeightMaint: Number(bankData.config.assetWeightMaint),
|
|
415
311
|
liabilityWeightMaint: Number(bankData.config.liabilityWeightMaint),
|
|
416
312
|
price,
|
|
417
|
-
emissionsOutstanding,
|
|
418
|
-
emissionsOutstandingAndUnclaimed,
|
|
419
313
|
});
|
|
420
314
|
balances.push(lendingAccountBalance);
|
|
421
315
|
healthFactorWeightedAssetArgs.push(lendingAccountBalance.assetWeightedMaintValue);
|
|
@@ -501,6 +395,13 @@ class ClendClient {
|
|
|
501
395
|
// get required remaining accounts based on account balance
|
|
502
396
|
const clendAccountData = await this.getClendAccount(clendAccount);
|
|
503
397
|
let clendAccountActiveBanks = (0, utils_1.getClendAccountActiveBanks)(clendAccountData);
|
|
398
|
+
//// If withdrawAll is true, remove the target bank from activeBanks
|
|
399
|
+
//// as we wont have a remaining balance after the withdraw operation
|
|
400
|
+
//if (withdrawAll === true) {
|
|
401
|
+
// clendAccountActiveBanks = clendAccountActiveBanks.filter(
|
|
402
|
+
// (b) => !b.equals(bank),
|
|
403
|
+
// );
|
|
404
|
+
//}
|
|
504
405
|
// fetch all active banks
|
|
505
406
|
const activeBankData = [];
|
|
506
407
|
for (const bank of clendAccountActiveBanks) {
|
|
@@ -535,7 +436,7 @@ class ClendClient {
|
|
|
535
436
|
const ix = await this.instructions.borrow(clendGroup, clendAccount, this.address(), bank, destinationTokenAccount, tokenProgram, amount, remainingAccounts);
|
|
536
437
|
return this.send([ix]);
|
|
537
438
|
}
|
|
538
|
-
async repay(clendGroup, clendAccount, mint, amount, repayAll
|
|
439
|
+
async repay(clendGroup, clendAccount, mint, amount, repayAll = null) {
|
|
539
440
|
const bank = (0, addresses_1.getBankPda)(clendGroup, mint);
|
|
540
441
|
// Get bank data to access the liquidityVault and mint
|
|
541
442
|
const bankData = await this.getBank(bank);
|
|
@@ -551,7 +452,7 @@ class ClendClient {
|
|
|
551
452
|
activeBankData.push(bankData);
|
|
552
453
|
}
|
|
553
454
|
const remainingAccounts = (0, utils_1.getClendAccountRemainingAccounts)(activeBankData);
|
|
554
|
-
const ix = await this.instructions.repay(clendGroup, clendAccount, this.address(), bank, userTokenAccount, tokenProgram, amount, repayAll,
|
|
455
|
+
const ix = await this.instructions.repay(clendGroup, clendAccount, this.address(), bank, userTokenAccount, tokenProgram, amount, repayAll, remainingAccounts);
|
|
555
456
|
return this.send([ix]);
|
|
556
457
|
}
|
|
557
458
|
async editGlobalFeeState(clendGroup, newGlobalFeeWallet, newBankInitFlatSolFee, newProgramFeeFixed, newProgramFeeRate) {
|
|
@@ -592,10 +493,9 @@ class ClendClient {
|
|
|
592
493
|
const ix = await this.instructions.resetBankFees(bankData.group, this.address(), bank);
|
|
593
494
|
return this.send([ix]);
|
|
594
495
|
}
|
|
595
|
-
async liquidateClendAccountWithFlashLoan(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps, marginfiLiabFlashLoanBank, liquidatorMarginfiAccount, additionalIxnCount
|
|
596
|
-
swapperOverride) {
|
|
496
|
+
async liquidateClendAccountWithFlashLoan(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps, marginfiLiabFlashLoanBank, liquidatorMarginfiAccount, additionalIxnCount) {
|
|
597
497
|
// create instructions
|
|
598
|
-
const result = await this.getLiquidateClendAccountIxns(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps
|
|
498
|
+
const result = await this.getLiquidateClendAccountIxns(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps);
|
|
599
499
|
if (!result) {
|
|
600
500
|
return;
|
|
601
501
|
}
|
|
@@ -628,8 +528,8 @@ class ClendClient {
|
|
|
628
528
|
];
|
|
629
529
|
return this.send(ixns, [], luts);
|
|
630
530
|
}
|
|
631
|
-
async liquidateClendAccount(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps
|
|
632
|
-
const result = await this.getLiquidateClendAccountIxns(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps
|
|
531
|
+
async liquidateClendAccount(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps) {
|
|
532
|
+
const result = await this.getLiquidateClendAccountIxns(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps);
|
|
633
533
|
if (!result) {
|
|
634
534
|
return;
|
|
635
535
|
}
|
|
@@ -654,12 +554,7 @@ class ClendClient {
|
|
|
654
554
|
* Must be <= 0.
|
|
655
555
|
* returns: instructions, luts, deposit amount (libaility token)
|
|
656
556
|
*/
|
|
657
|
-
async getLiquidateClendAccountIxns(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps
|
|
658
|
-
let activeSwapper = this.swapper;
|
|
659
|
-
// override swapper if provided
|
|
660
|
-
if (swapperOverride) {
|
|
661
|
-
activeSwapper = swapperOverride;
|
|
662
|
-
}
|
|
557
|
+
async getLiquidateClendAccountIxns(liquidatorClendAccount, liquidateeClendAccount, assetBankData, liabBankData, targetOnChainMaintenanceHealthValue, slippageBps) {
|
|
663
558
|
utils_1.logger.info("Starting liquidation calculation", {
|
|
664
559
|
liquidatee: liquidateeClendAccount.authority.toString(),
|
|
665
560
|
liquidator: liquidatorClendAccount.authority.toString(),
|
|
@@ -779,8 +674,7 @@ class ClendClient {
|
|
|
779
674
|
// balance for the asset
|
|
780
675
|
const withdrawAssetIx = await this.instructions.withdraw(assetBankData.group, liquidatorClendAccount.key, liquidatorClendAccount.authority, assetBankData.key, liquidatorAssetAta, assetTokenProgram, finalAssetAmount, false, remainingAccounts);
|
|
781
676
|
// get a swap quote
|
|
782
|
-
const swapQuote = await
|
|
783
|
-
payer: liquidatorClendAccount.authority,
|
|
677
|
+
const swapQuote = await this.swapper.getQuote({
|
|
784
678
|
inputMint: assetBankData.mint,
|
|
785
679
|
inputMintDecimals: assetBankData.mintDecimals,
|
|
786
680
|
outputMint: liabBankData.mint,
|
|
@@ -796,7 +690,7 @@ class ClendClient {
|
|
|
796
690
|
outAmountMin: outAmountMin.toString(10),
|
|
797
691
|
});
|
|
798
692
|
// 5. Get Swap Instructions from Jupiter
|
|
799
|
-
const swapIxns = await
|
|
693
|
+
const swapIxns = await this.swapper.getSwapIxns(this.address(), swapQuote);
|
|
800
694
|
const ixns = [
|
|
801
695
|
depositIx, // deposit liability mint into liquidator clend account
|
|
802
696
|
liquidationIx, // liquidate liquidatee clend account
|
|
@@ -835,35 +729,30 @@ class ClendClient {
|
|
|
835
729
|
const ix = await this.instructions.configureBankOracleMaxAge(clendGroup, this.address(), bank, maxAge);
|
|
836
730
|
return this.send([ix]);
|
|
837
731
|
}
|
|
838
|
-
async
|
|
732
|
+
async configureBankOracle(bank, pythOracle, pythOracleFeed) {
|
|
839
733
|
const bankData = await this.getBank(bank);
|
|
840
|
-
const ix = await this.instructions.
|
|
841
|
-
return this.send([ix]);
|
|
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);
|
|
734
|
+
const ix = await this.instructions.configureBankOracle(bankData.group, this.address(), bank, pythOracle, pythOracleFeed);
|
|
846
735
|
return this.send([ix]);
|
|
847
736
|
}
|
|
848
737
|
async setBankOperationalState(clendGroup, bank, operationalState) {
|
|
849
738
|
const ix = await this.instructions.setBankOperationalState(clendGroup, this.address(), bank, operationalState);
|
|
850
739
|
return this.send([ix]);
|
|
851
740
|
}
|
|
852
|
-
async depositLeverage(clendGroup, clendAccount, selectedMint, collateralMint, debtMint, depositAmount, targetLeverage, slippageBps
|
|
741
|
+
async depositLeverage(clendGroup, clendAccount, selectedMint, collateralMint, debtMint, depositAmount, targetLeverage, slippageBps) {
|
|
853
742
|
let ixns = [];
|
|
854
743
|
let luts = [];
|
|
855
744
|
let additionalSigners = [];
|
|
856
745
|
switch (selectedMint.toString()) {
|
|
857
746
|
case collateralMint.toString():
|
|
858
747
|
const collateralParams = await this.getDepositLeverageFromCollateralParams(clendGroup, collateralMint, debtMint, depositAmount, targetLeverage, slippageBps);
|
|
859
|
-
const { ixns: collateralIxns, luts: collateralLuts, additionalSigners: collateralAdditionalSigners, } = await this.getDepositLeverageFromCollateralIxns(clendGroup, this.address(), clendAccount, collateralMint, debtMint, collateralParams.collateralBankData.mintDecimals, collateralParams.debtBankData.mintDecimals, collateralParams.borrowAmount, depositAmount, slippageBps, 0
|
|
748
|
+
const { ixns: collateralIxns, luts: collateralLuts, additionalSigners: collateralAdditionalSigners, } = await this.getDepositLeverageFromCollateralIxns(clendGroup, this.address(), clendAccount, collateralMint, debtMint, collateralParams.collateralBankData.mintDecimals, collateralParams.debtBankData.mintDecimals, collateralParams.borrowAmount, depositAmount, slippageBps, 0);
|
|
860
749
|
ixns = collateralIxns;
|
|
861
750
|
luts = collateralLuts;
|
|
862
751
|
additionalSigners = collateralAdditionalSigners;
|
|
863
752
|
break;
|
|
864
753
|
case debtMint.toString():
|
|
865
|
-
const debtParams = await this.getDepositLeverageFromDebtParams(clendGroup, collateralMint, debtMint, depositAmount, targetLeverage, slippageBps
|
|
866
|
-
const { ixns: debtIxns, luts: debtLuts, additionalSigners: debtAdditionalSigners, } = await this.getDepositLeverageFromDebtIxns(clendGroup, this.address(), clendAccount, collateralMint, debtMint, debtParams.debtBankData.mintDecimals, debtParams.collateralBankData.mintDecimals, depositAmount, debtParams.borrowAmount, slippageBps, 0
|
|
754
|
+
const debtParams = await this.getDepositLeverageFromDebtParams(clendGroup, collateralMint, debtMint, depositAmount, targetLeverage, slippageBps);
|
|
755
|
+
const { ixns: debtIxns, luts: debtLuts, additionalSigners: debtAdditionalSigners, } = await this.getDepositLeverageFromDebtIxns(clendGroup, this.address(), clendAccount, collateralMint, debtMint, debtParams.debtBankData.mintDecimals, debtParams.collateralBankData.mintDecimals, depositAmount, debtParams.borrowAmount, slippageBps, 0);
|
|
867
756
|
ixns = debtIxns;
|
|
868
757
|
luts = debtLuts;
|
|
869
758
|
additionalSigners = debtAdditionalSigners;
|
|
@@ -875,13 +764,7 @@ class ClendClient {
|
|
|
875
764
|
}
|
|
876
765
|
async getDepositLeverageFromDebtIxns(clendGroup, user, clendAccount, collateralMint, // e.g., JLP
|
|
877
766
|
debtMint, // e.g., USDC
|
|
878
|
-
debtMintDecimals, collateralMintDecimals, initialUserDebtContributionForSwap, finalLoopBorrowAmount, slippageBps, additionalIxnCount
|
|
879
|
-
swapperOverride) {
|
|
880
|
-
// override swapper if provided
|
|
881
|
-
let activeSwapper = this.swapper;
|
|
882
|
-
if (swapperOverride) {
|
|
883
|
-
activeSwapper = swapperOverride;
|
|
884
|
-
}
|
|
767
|
+
debtMintDecimals, collateralMintDecimals, initialUserDebtContributionForSwap, finalLoopBorrowAmount, slippageBps, additionalIxnCount) {
|
|
885
768
|
const collateralTokenProgram = (0, utils_1.getTokenProgramForMint)(collateralMint);
|
|
886
769
|
const debtTokenProgram = (0, utils_1.getTokenProgramForMint)(debtMint);
|
|
887
770
|
const debtBank = (0, addresses_1.getBankPda)(clendGroup, debtMint);
|
|
@@ -921,8 +804,7 @@ class ClendClient {
|
|
|
921
804
|
const totalDebtTokenForSwap = initialUserDebtContributionForSwap.add(finalLoopBorrowAmount);
|
|
922
805
|
// 3. Get Jupiter Quote for the TOTAL debt token amount
|
|
923
806
|
const swapMode = "ExactIn";
|
|
924
|
-
const swapQuote = await
|
|
925
|
-
payer: user,
|
|
807
|
+
const swapQuote = await this.swapper.getQuote({
|
|
926
808
|
inputMint: debtMint,
|
|
927
809
|
inputMintDecimals: debtMintDecimals,
|
|
928
810
|
outputMint: collateralMint,
|
|
@@ -944,7 +826,7 @@ class ClendClient {
|
|
|
944
826
|
const userDebtAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtMint, user, true, debtTokenProgram);
|
|
945
827
|
const userCollateralAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collateralMint, user, true, collateralTokenProgram);
|
|
946
828
|
// 5. Get Swap Instructions from Jupiter
|
|
947
|
-
const swapIxns = await
|
|
829
|
+
const swapIxns = await this.swapper.getSwapIxns(user, swapQuote);
|
|
948
830
|
// 6. Create Clend Protocol Instructions
|
|
949
831
|
const borrowIx = await this.instructions.borrow(clendGroup, clendAccount, // clendAccount is guaranteed to be non-null here
|
|
950
832
|
user, debtBank, userDebtAta, // Borrow into user's ATA
|
|
@@ -990,13 +872,7 @@ class ClendClient {
|
|
|
990
872
|
additionalSigners,
|
|
991
873
|
};
|
|
992
874
|
}
|
|
993
|
-
async getDepositLeverageFromCollateralIxns(clendGroup, user, clendAccount, collateralMint, debtMint, debtMintDecimals, collateralMintDecimals, borrowAmount, depositAmount, slippageBps, additionalIxnCount
|
|
994
|
-
swapperOverride) {
|
|
995
|
-
// override swapper if provided
|
|
996
|
-
let activeSwapper = this.swapper;
|
|
997
|
-
if (swapperOverride) {
|
|
998
|
-
activeSwapper = swapperOverride;
|
|
999
|
-
}
|
|
875
|
+
async getDepositLeverageFromCollateralIxns(clendGroup, user, clendAccount, collateralMint, debtMint, debtMintDecimals, collateralMintDecimals, borrowAmount, depositAmount, slippageBps, additionalIxnCount) {
|
|
1000
876
|
// Get token programs
|
|
1001
877
|
const collateralTokenProgram = (0, utils_1.getTokenProgramForMint)(collateralMint);
|
|
1002
878
|
const debtTokenProgram = (0, utils_1.getTokenProgramForMint)(debtMint);
|
|
@@ -1004,8 +880,7 @@ class ClendClient {
|
|
|
1004
880
|
const collateralBank = (0, addresses_1.getBankPda)(clendGroup, collateralMint);
|
|
1005
881
|
// Get Jupiter quote for swapping debt token to collateral token
|
|
1006
882
|
const swapMode = "ExactIn";
|
|
1007
|
-
const swapQuote = await
|
|
1008
|
-
payer: user,
|
|
883
|
+
const swapQuote = await this.swapper.getQuote({
|
|
1009
884
|
inputMint: debtMint,
|
|
1010
885
|
inputMintDecimals: debtMintDecimals,
|
|
1011
886
|
outputMint: collateralMint,
|
|
@@ -1056,7 +931,7 @@ class ClendClient {
|
|
|
1056
931
|
}
|
|
1057
932
|
const remainingAccounts = (0, utils_1.getClendAccountRemainingAccounts)(activeBankData);
|
|
1058
933
|
// Get swap instructions
|
|
1059
|
-
const swapIxns = await
|
|
934
|
+
const swapIxns = await this.swapper.getSwapIxns(user, swapQuote);
|
|
1060
935
|
// Calculate additional collateral from swap
|
|
1061
936
|
const additionalCollateralAmount = swapQuote.outAmount;
|
|
1062
937
|
const totalCollateralAmount = depositAmount.add(additionalCollateralAmount);
|
|
@@ -1172,12 +1047,7 @@ class ClendClient {
|
|
|
1172
1047
|
async getDepositLeverageFromDebtParams(clendGroup, collateralMint, // The asset being leveraged (e.g., JLP)
|
|
1173
1048
|
debtMint, // The asset provided initially by the user (e.g., USDC)
|
|
1174
1049
|
initialUserDebtContributionForSwap, // User's initial DEBT token for the swap
|
|
1175
|
-
targetLeverage, slippageBps
|
|
1176
|
-
// override swapper if provided
|
|
1177
|
-
let activeSwapper = this.swapper;
|
|
1178
|
-
if (swapperOverride) {
|
|
1179
|
-
activeSwapper = swapperOverride;
|
|
1180
|
-
}
|
|
1050
|
+
targetLeverage, slippageBps) {
|
|
1181
1051
|
// 1. Get decimals and bank data
|
|
1182
1052
|
const collateralDecimals = (0, utils_1.getTokenDecimalsForMint)(collateralMint);
|
|
1183
1053
|
const debtDecimals = (0, utils_1.getTokenDecimalsForMint)(debtMint);
|
|
@@ -1208,8 +1078,7 @@ class ClendClient {
|
|
|
1208
1078
|
// This is the user's initial contribution + what's borrowed from the protocol
|
|
1209
1079
|
const totalDebtTokenForSwap = initialUserDebtContributionForSwap.add(finalLoopBorrowAmount);
|
|
1210
1080
|
// 7. Calculate Expected COLLATERAL Output from swapping the total DEBT token amount
|
|
1211
|
-
const swapQuote = await
|
|
1212
|
-
payer: anchor_1.web3.PublicKey.default, // doesnt matter right now
|
|
1081
|
+
const swapQuote = await this.swapper.getQuote({
|
|
1213
1082
|
inputMint: debtMint,
|
|
1214
1083
|
inputMintDecimals: debtDecimals,
|
|
1215
1084
|
outputMint: collateralMint,
|
|
@@ -1248,19 +1117,19 @@ class ClendClient {
|
|
|
1248
1117
|
collateralBankData,
|
|
1249
1118
|
};
|
|
1250
1119
|
}
|
|
1251
|
-
async withdrawLeverage(clendGroup, clendAccount, selectedMint, collateralMint, debtMint, withdrawAmount, withdrawAll, slippageBps
|
|
1120
|
+
async withdrawLeverage(clendGroup, clendAccount, selectedMint, collateralMint, debtMint, withdrawAmount, withdrawAll, slippageBps) {
|
|
1252
1121
|
let ixns = [];
|
|
1253
1122
|
let luts = [];
|
|
1254
1123
|
switch (selectedMint.toString()) {
|
|
1255
1124
|
case collateralMint.toString():
|
|
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.
|
|
1125
|
+
const collateralParams = await this.getNetWithdrawLeverageCollateralParams(clendGroup, clendAccount, collateralMint, debtMint, withdrawAmount, withdrawAll, slippageBps);
|
|
1126
|
+
const { ixns: collateralIxns, luts: collateralLuts } = await this.getWithdrawLeverageCollateralIxns(clendGroup, clendAccount, collateralMint, debtMint, collateralParams.collateralBankData.mintDecimals, collateralParams.debtBankData.mintDecimals, withdrawAll, collateralParams.debtToRepay, collateralParams.collateralToWithdraw, slippageBps, 0);
|
|
1258
1127
|
ixns = collateralIxns;
|
|
1259
1128
|
luts = collateralLuts;
|
|
1260
1129
|
break;
|
|
1261
1130
|
case debtMint.toString():
|
|
1262
|
-
const debtParams = await this.getNetWithdrawLeverageDebtParams(clendGroup, clendAccount, collateralMint, debtMint, withdrawAmount, withdrawAll,
|
|
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
|
|
1131
|
+
const debtParams = await this.getNetWithdrawLeverageDebtParams(clendGroup, clendAccount, collateralMint, debtMint, withdrawAmount, withdrawAll, slippageBps);
|
|
1132
|
+
const { ixns: debtIxns, luts: debtLuts } = await this.getWithdrawLeverageDebtIxns(clendGroup, clendAccount, collateralMint, debtMint, debtParams.collateralBankData.mintDecimals, debtParams.debtBankData.mintDecimals, withdrawAll, debtParams.debtToRepay, debtParams.collateralToWithdraw, withdrawAmount, slippageBps, 0);
|
|
1264
1133
|
ixns = debtIxns;
|
|
1265
1134
|
luts = debtLuts;
|
|
1266
1135
|
break;
|
|
@@ -1270,98 +1139,71 @@ class ClendClient {
|
|
|
1270
1139
|
// Send transaction
|
|
1271
1140
|
return this.send(ixns, [], luts);
|
|
1272
1141
|
}
|
|
1273
|
-
async
|
|
1274
|
-
// override swapper if provided
|
|
1275
|
-
let activeSwapper = this.swapper;
|
|
1276
|
-
if (swapperOverride) {
|
|
1277
|
-
activeSwapper = swapperOverride;
|
|
1278
|
-
}
|
|
1142
|
+
async getWithdrawLeverageCollateralIxns(clendGroup, clendAccount, collateralMint, debtMint, collateralDecimals, debtDecimals, withdrawAll, debtToRepay, collateralToWithdraw, slippageBps, additionalIxnCount) {
|
|
1279
1143
|
const debtBank = (0, addresses_1.getBankPda)(clendGroup, debtMint);
|
|
1280
1144
|
const collateralBank = (0, addresses_1.getBankPda)(clendGroup, collateralMint);
|
|
1145
|
+
// Get remaining accounts for flash loan
|
|
1281
1146
|
const clendAccountData = await this.getClendAccount(clendAccount);
|
|
1282
1147
|
if (!clendAccountData) {
|
|
1283
1148
|
throw new Error(`Clend account not found: ${clendAccount.toString()}`);
|
|
1284
1149
|
}
|
|
1285
1150
|
let activeBanks = (0, utils_1.getClendAccountActiveBanks)(clendAccountData);
|
|
1286
|
-
const activeBankData =
|
|
1151
|
+
const activeBankData = [];
|
|
1152
|
+
for (const bank of activeBanks) {
|
|
1153
|
+
const bankData = await this.getBank(bank);
|
|
1154
|
+
activeBankData.push(bankData);
|
|
1155
|
+
}
|
|
1287
1156
|
const remainingAccounts = (0, utils_1.getClendAccountRemainingAccounts)(activeBankData);
|
|
1288
|
-
// Get
|
|
1289
|
-
const
|
|
1290
|
-
|
|
1157
|
+
// Get Jupiter quote for swapping collateral to debt token (ExactOut mode)
|
|
1158
|
+
const swapMode = "ExactOut";
|
|
1159
|
+
const swapQuote = await this.swapper.getQuote({
|
|
1291
1160
|
inputMint: collateralMint,
|
|
1292
1161
|
inputMintDecimals: collateralDecimals,
|
|
1293
1162
|
outputMint: debtMint,
|
|
1294
1163
|
outputMintDecimals: debtDecimals,
|
|
1295
|
-
inputAmount:
|
|
1164
|
+
inputAmount: debtToRepay,
|
|
1296
1165
|
slippageBps,
|
|
1297
|
-
swapMode
|
|
1166
|
+
swapMode,
|
|
1298
1167
|
});
|
|
1299
|
-
//
|
|
1300
|
-
const
|
|
1301
|
-
|
|
1302
|
-
|
|
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(),
|
|
1168
|
+
// set collateralToSwap to the inAmount from the swap quote
|
|
1169
|
+
const collateralToSwap = swapQuote.inAmount;
|
|
1170
|
+
utils_1.logger.debug("withdrawLeverage collateralToSwap", {
|
|
1171
|
+
collateralToSwap,
|
|
1308
1172
|
});
|
|
1309
|
-
// Get
|
|
1310
|
-
const
|
|
1311
|
-
|
|
1312
|
-
|
|
1173
|
+
// Get swap instructions
|
|
1174
|
+
const swapInputs = {
|
|
1175
|
+
inputAmountLamports: new decimal_js_1.default(collateralToSwap.toString()),
|
|
1176
|
+
inputMint: collateralMint,
|
|
1177
|
+
outputMint: debtMint,
|
|
1178
|
+
};
|
|
1179
|
+
utils_1.logger.debug("withdrawLeverage swapInputs", {
|
|
1180
|
+
swapInputs,
|
|
1181
|
+
});
|
|
1182
|
+
const swapIxns = await this.swapper.getSwapIxns(clendAccountData.authority, swapQuote);
|
|
1183
|
+
utils_1.logger.info(`swapIxns fetched`);
|
|
1313
1184
|
const collateralTokenProgram = (0, utils_1.getTokenProgramForMint)(collateralMint);
|
|
1314
1185
|
const debtTokenProgram = (0, utils_1.getTokenProgramForMint)(debtMint);
|
|
1315
1186
|
const userCollateralAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collateralMint, clendAccountData.authority, true, collateralTokenProgram);
|
|
1316
1187
|
const userDebtAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtMint, clendAccountData.authority, true, debtTokenProgram);
|
|
1317
|
-
|
|
1188
|
+
utils_1.logger.info(`begin crafting ixns`);
|
|
1189
|
+
// Create withdraw instruction
|
|
1318
1190
|
const withdrawIx = await this.instructions.withdraw(clendGroup, clendAccount, clendAccountData.authority, collateralBank, userCollateralAta, collateralTokenProgram, collateralToWithdraw, withdrawAll, remainingAccounts);
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
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
|
|
1191
|
+
utils_1.logger.info(`withdrawIx set`);
|
|
1192
|
+
// Create repay instruction
|
|
1193
|
+
const repayIx = await this.instructions.repay(clendGroup, clendAccount, clendAccountData.authority, debtBank, userDebtAta, debtTokenProgram, debtToRepay, withdrawAll, remainingAccounts);
|
|
1194
|
+
utils_1.logger.info(`repayIx set`);
|
|
1195
|
+
// Assemble all instructions without flash loan
|
|
1355
1196
|
const ixnsWithoutFlashLoan = [
|
|
1356
|
-
...emissionsIxns,
|
|
1357
1197
|
withdrawIx,
|
|
1358
1198
|
...swapIxns.ixns,
|
|
1359
1199
|
repayIx,
|
|
1360
1200
|
];
|
|
1361
|
-
|
|
1201
|
+
utils_1.logger.info(`ixnsWithoutFlashLoan set`);
|
|
1202
|
+
// Create flash loan instructions
|
|
1362
1203
|
const cuIxns = 2;
|
|
1363
1204
|
const endIndex = new anchor_1.BN(cuIxns + ixnsWithoutFlashLoan.length + 1 + additionalIxnCount);
|
|
1364
1205
|
const { beginFlashLoanIx, endFlashLoanIx } = await this.instructions.createFlashLoanInstructions(clendAccount, clendAccountData.authority, endIndex, remainingAccounts);
|
|
1206
|
+
// Assemble all instructions in the correct order
|
|
1365
1207
|
const instructions = [
|
|
1366
1208
|
beginFlashLoanIx,
|
|
1367
1209
|
...ixnsWithoutFlashLoan,
|
|
@@ -1369,13 +1211,84 @@ class ClendClient {
|
|
|
1369
1211
|
];
|
|
1370
1212
|
return { ixns: instructions, luts: swapIxns.luts };
|
|
1371
1213
|
}
|
|
1372
|
-
async
|
|
1373
|
-
|
|
1214
|
+
async getWithdrawLeverageDebtIxns(clendGroup, clendAccount, collateralMint, debtMint, collateralDecimals, debtDecimals, withdrawAll, debtToRepay, collateralToWithdraw, desiredNetDebtToReceive, slippageBps, additionalIxnCount) {
|
|
1215
|
+
const debtBank = (0, addresses_1.getBankPda)(clendGroup, debtMint);
|
|
1216
|
+
const collateralBank = (0, addresses_1.getBankPda)(clendGroup, collateralMint);
|
|
1217
|
+
const clendAccountData = await this.getClendAccount(clendAccount);
|
|
1218
|
+
if (!clendAccountData) {
|
|
1219
|
+
throw new Error(`Clend account not found: ${clendAccount.toString()}`);
|
|
1220
|
+
}
|
|
1221
|
+
let activeBanks = (0, utils_1.getClendAccountActiveBanks)(clendAccountData);
|
|
1222
|
+
//if (withdrawAll) {
|
|
1223
|
+
// activeBanks = activeBanks.filter(
|
|
1224
|
+
// (b) => !b.equals(collateralBank) && !b.equals(debtBank),
|
|
1225
|
+
// );
|
|
1226
|
+
//}
|
|
1227
|
+
const activeBankData = await Promise.all(activeBanks.map((bankPk) => this.getBank(bankPk)));
|
|
1228
|
+
const remainingAccounts = (0, utils_1.getClendAccountRemainingAccounts)(activeBankData);
|
|
1229
|
+
// 1. Determine Total USDC Needed from the Swap
|
|
1230
|
+
// This is the amount needed to repay Clend's debt AND give the user their desired net withdrawal.
|
|
1231
|
+
const totalDebtToReceive = debtToRepay.add(desiredNetDebtToReceive);
|
|
1232
|
+
const swapMode = "ExactIn";
|
|
1233
|
+
const swapQuote = await this.swapper.getQuote({
|
|
1234
|
+
inputMint: collateralMint,
|
|
1235
|
+
inputMintDecimals: collateralDecimals,
|
|
1236
|
+
outputMint: debtMint,
|
|
1237
|
+
outputMintDecimals: debtDecimals,
|
|
1238
|
+
inputAmount: collateralToWithdraw,
|
|
1239
|
+
slippageBps,
|
|
1240
|
+
swapMode,
|
|
1241
|
+
});
|
|
1242
|
+
const minUsdcReceivedFromSwap_BN = new anchor_1.BN(swapQuote.otherAmountThreshold);
|
|
1243
|
+
utils_1.logger.debug("getWithdrawLeverageDebtIxns: collateral and debt details", {
|
|
1244
|
+
debtToRepay: debtToRepay.toString(),
|
|
1245
|
+
desiredNetDebtToReceive: desiredNetDebtToReceive.toString(),
|
|
1246
|
+
totalDebtToReceive: totalDebtToReceive.toString(),
|
|
1247
|
+
minUsdcReceivedFromSwap_BN: minUsdcReceivedFromSwap_BN.toString(),
|
|
1248
|
+
});
|
|
1249
|
+
// 3. Get Swap Instructions from swapper
|
|
1250
|
+
const swapIxns = await this.swapper.getSwapIxns(clendAccountData.authority, swapQuote);
|
|
1251
|
+
utils_1.logger.info(`swapIxns fetched for ReceiveDebtToken`);
|
|
1252
|
+
// --- ATAs and Clend Instructions (largely similar structure) ---
|
|
1253
|
+
const collateralTokenProgram = (0, utils_1.getTokenProgramForMint)(collateralMint);
|
|
1254
|
+
const debtTokenProgram = (0, utils_1.getTokenProgramForMint)(debtMint);
|
|
1255
|
+
const userCollateralAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collateralMint, clendAccountData.authority, true, collateralTokenProgram);
|
|
1256
|
+
const userDebtAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtMint, clendAccountData.authority, true, debtTokenProgram);
|
|
1257
|
+
// Create withdraw instruction: Withdraw ALL the JLP calculated by params function
|
|
1258
|
+
const withdrawIx = await this.instructions.withdraw(clendGroup, clendAccount, clendAccountData.authority, collateralBank, userCollateralAta, collateralTokenProgram, collateralToWithdraw, // Withdraw all the JLP needed for the full swap
|
|
1259
|
+
withdrawAll, // This flag is for Clend's internal logic
|
|
1260
|
+
remainingAccounts);
|
|
1261
|
+
// Create repay instruction: Repay the calculated debt portion to Clend
|
|
1262
|
+
const repayIx = await this.instructions.repay(clendGroup, clendAccount, clendAccountData.authority, debtBank, userDebtAta, debtTokenProgram, debtToRepay, // Repay the specified debt amount
|
|
1263
|
+
withdrawAll, // This flag is for Clend's internal logic
|
|
1264
|
+
remainingAccounts);
|
|
1265
|
+
// Assemble instructions
|
|
1266
|
+
const ixnsWithoutFlashLoan = [
|
|
1267
|
+
withdrawIx,
|
|
1268
|
+
...swapIxns.ixns, // Swap all withdrawn collateral to debt
|
|
1269
|
+
repayIx, // Repay the debt portion
|
|
1270
|
+
]; // User is left with the remaining debt in their ATA
|
|
1271
|
+
// Flash Loan Wrapping (same as before)
|
|
1272
|
+
const cuIxns = 2;
|
|
1273
|
+
const endIndex = new anchor_1.BN(cuIxns + ixnsWithoutFlashLoan.length + 1 + additionalIxnCount);
|
|
1274
|
+
const { beginFlashLoanIx, endFlashLoanIx } = await this.instructions.createFlashLoanInstructions(clendAccount, clendAccountData.authority, endIndex, remainingAccounts);
|
|
1275
|
+
const instructions = [
|
|
1276
|
+
beginFlashLoanIx,
|
|
1277
|
+
...ixnsWithoutFlashLoan,
|
|
1278
|
+
endFlashLoanIx,
|
|
1279
|
+
];
|
|
1280
|
+
return { ixns: instructions, luts: swapIxns.luts };
|
|
1281
|
+
}
|
|
1282
|
+
// caller will receive desiredNetWithdraw, the position will be adjusted accordingly
|
|
1283
|
+
// receive the collateral mint
|
|
1284
|
+
async getNetWithdrawLeverageCollateralParams(clendGroup, clendAccount, collateralMint, debtMint, desiredNetWithdraw, withdrawAll, slippageBps) {
|
|
1285
|
+
utils_1.logger.info("getNetWithdrawParams", {
|
|
1374
1286
|
desiredNetWithdraw: desiredNetWithdraw.toString(),
|
|
1375
1287
|
withdrawAll,
|
|
1376
1288
|
});
|
|
1289
|
+
// if desiredNetWithdraw is zero, throw an error
|
|
1377
1290
|
if (desiredNetWithdraw.isZero() && !withdrawAll) {
|
|
1378
|
-
throw new Error("Desired net withdrawal is zero
|
|
1291
|
+
throw new Error("Desired net withdrawal is zero");
|
|
1379
1292
|
}
|
|
1380
1293
|
const collateralBank = (0, addresses_1.getBankPda)(clendGroup, collateralMint);
|
|
1381
1294
|
const debtBank = (0, addresses_1.getBankPda)(clendGroup, debtMint);
|
|
@@ -1387,6 +1300,7 @@ class ClendClient {
|
|
|
1387
1300
|
if (!clendAccountData) {
|
|
1388
1301
|
throw new Error(`Clend account not found: ${clendAccount.toString()}`);
|
|
1389
1302
|
}
|
|
1303
|
+
// fetch balances and pricing
|
|
1390
1304
|
let debtBalance;
|
|
1391
1305
|
let debtPrice;
|
|
1392
1306
|
let collateralBalance;
|
|
@@ -1405,32 +1319,75 @@ class ClendClient {
|
|
|
1405
1319
|
throw new Error("Could not find debt or collateral position in account");
|
|
1406
1320
|
}
|
|
1407
1321
|
const collateralAmount = collateralBalance.assetBalance;
|
|
1408
|
-
const
|
|
1322
|
+
const collateralAmountUI = collateralBalance.assetBalanceUi;
|
|
1323
|
+
const collateralValue = collateralBalance.assetValue;
|
|
1409
1324
|
const collateralDecimals = collateralBankData.mintDecimals;
|
|
1325
|
+
const debtAmount = debtBalance.liabilityBalance;
|
|
1326
|
+
const debtAmountUI = debtBalance.liabilityBalanceUi;
|
|
1327
|
+
const debtValue = debtBalance.liabilityValue;
|
|
1410
1328
|
const debtDecimals = debtBankData.mintDecimals;
|
|
1329
|
+
const desiredNetWithdrawUi = (0, clend_common_1.amountToUi)(desiredNetWithdraw, debtDecimals);
|
|
1330
|
+
utils_1.logger.debug("Current amounts", {
|
|
1331
|
+
desiredNetWithdrawUi,
|
|
1332
|
+
collateralAmount: collateralAmount.toString(),
|
|
1333
|
+
collateralAmountUI,
|
|
1334
|
+
collateralValue,
|
|
1335
|
+
debtAmount: debtAmount.toString(),
|
|
1336
|
+
debtAmountUI,
|
|
1337
|
+
debtValue,
|
|
1338
|
+
});
|
|
1339
|
+
// --- Determine collateral to withdraw and debt to repay ---
|
|
1411
1340
|
let collateralToWithdraw;
|
|
1412
1341
|
let debtToRepay;
|
|
1413
1342
|
if (withdrawAll) {
|
|
1414
1343
|
utils_1.logger.info("WithdrawAll selected");
|
|
1415
1344
|
collateralToWithdraw = collateralAmount;
|
|
1416
|
-
debtToRepay = (0, clend_common_1.calculateLiabilityInterest)(debtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(),
|
|
1345
|
+
debtToRepay = (0, clend_common_1.calculateLiabilityInterest)(debtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), new Date().getTime() / 1000);
|
|
1346
|
+
utils_1.logger.info(`calculated debt to repay`, {
|
|
1347
|
+
debtToRepayWithInterest: debtToRepay.toString(),
|
|
1348
|
+
debtToRepayRaw: debtAmount.toString(),
|
|
1349
|
+
});
|
|
1417
1350
|
}
|
|
1418
1351
|
else {
|
|
1419
|
-
utils_1.logger.info("Calculating partial deleverage
|
|
1352
|
+
utils_1.logger.info("Calculating partial deleverage for net withdrawal");
|
|
1353
|
+
// 1. Calculate estimated current debt accurately
|
|
1420
1354
|
const estimatedCurrentDebt = (0, clend_common_1.calculateLiabilityInterest)(debtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), Math.floor(Date.now() / 1000));
|
|
1421
1355
|
const estimatedCurrentDebtUi = (0, clend_common_1.amountToUi)(estimatedCurrentDebt, debtDecimals);
|
|
1422
1356
|
const collateralAmountUI = (0, clend_common_1.amountToUi)(collateralAmount, collateralDecimals);
|
|
1423
|
-
const
|
|
1424
|
-
|
|
1425
|
-
|
|
1357
|
+
const { debtToRepayUi, totalCollateralToWithdrawUi, newLeverage, currentLeverage, } = (0, clend_common_1.computeWithdrawLeverageAmounts)(collateralAmountUI, estimatedCurrentDebtUi, desiredNetWithdrawUi, collateralPrice, debtPrice);
|
|
1358
|
+
utils_1.logger.info("computeWithdrawLeverageAmounts", {
|
|
1359
|
+
debtToRepayUi,
|
|
1360
|
+
totalCollateralToWithdrawUi,
|
|
1361
|
+
newLeverage,
|
|
1362
|
+
currentLeverage,
|
|
1363
|
+
});
|
|
1426
1364
|
const baseDebtToRepay = (0, clend_common_1.uiToAmount)(debtToRepayUi, debtDecimals);
|
|
1365
|
+
// --- Step 5: Add Buffers to the Debt Repayment Target ---
|
|
1366
|
+
// This adds a small buffer for interest that might accrue during the transaction's execution time.
|
|
1427
1367
|
const timeBufferSec = 15;
|
|
1428
1368
|
debtToRepay = (0, clend_common_1.addInterestAccrualBuffer)(baseDebtToRepay, debtBankData.borrowApy, timeBufferSec);
|
|
1369
|
+
// --- Step 6: Get a Jupiter Quote for the Swap ---
|
|
1370
|
+
// Query Jupiter for an EXACT_OUT swap to find out exactly how much collateral
|
|
1371
|
+
// is needed to get the final amount of debt we need to repay.
|
|
1372
|
+
const swapQuote = await this.swapper.getQuote({
|
|
1373
|
+
inputMint: collateralMint,
|
|
1374
|
+
inputMintDecimals: collateralDecimals,
|
|
1375
|
+
outputMint: debtMint,
|
|
1376
|
+
outputMintDecimals: debtDecimals,
|
|
1377
|
+
inputAmount: debtToRepay,
|
|
1378
|
+
slippageBps,
|
|
1379
|
+
swapMode: "ExactOut",
|
|
1380
|
+
});
|
|
1381
|
+
const collateralNeededForSwap = new anchor_1.BN(swapQuote.inAmount);
|
|
1382
|
+
// --- Step 7: Calculate the Final Total Collateral to Withdraw ---
|
|
1383
|
+
// This is the sum of what the user gets (desiredNetWithdraw) and what the swap needs.
|
|
1384
|
+
collateralToWithdraw = desiredNetWithdraw.add(collateralNeededForSwap);
|
|
1385
|
+
// Final sanity check to ensure we don't try to withdraw more than exists.
|
|
1429
1386
|
if (collateralToWithdraw.gt(collateralAmount)) {
|
|
1430
|
-
throw new Error("Total required collateral for withdrawal exceeds the available balance.");
|
|
1387
|
+
throw new Error("Total required collateral for withdrawal and swap exceeds the available balance. The position may be too leveraged for this withdrawal amount.");
|
|
1431
1388
|
}
|
|
1432
1389
|
}
|
|
1433
|
-
utils_1.logger.debug("Final calculated amounts
|
|
1390
|
+
utils_1.logger.debug("Final calculated amounts", {
|
|
1434
1391
|
collateralToWithdraw: collateralToWithdraw.toString(),
|
|
1435
1392
|
debtToRepay: debtToRepay.toString(),
|
|
1436
1393
|
});
|
|
@@ -1441,85 +1398,9 @@ class ClendClient {
|
|
|
1441
1398
|
debtBankData,
|
|
1442
1399
|
};
|
|
1443
1400
|
}
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
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) {
|
|
1518
|
-
// override swapper if provided
|
|
1519
|
-
let activeSwapper = this.swapper;
|
|
1520
|
-
if (swapperOverride) {
|
|
1521
|
-
activeSwapper = swapperOverride;
|
|
1522
|
-
}
|
|
1401
|
+
// caller will receive desiredNetWithdraw, the position will be adjusted accordingly
|
|
1402
|
+
// receive the debt mint
|
|
1403
|
+
async getNetWithdrawLeverageDebtParams(clendGroup, clendAccount, collateralMint, debtMint, desiredNetWithdraw, withdrawAll, slippageBps) {
|
|
1523
1404
|
utils_1.logger.info("getNetWithdrawDebtParams", {
|
|
1524
1405
|
desiredNetWithdraw: desiredNetWithdraw.toString(),
|
|
1525
1406
|
withdrawAll,
|
|
@@ -1565,31 +1446,52 @@ class ClendClient {
|
|
|
1565
1446
|
let debtToRepay;
|
|
1566
1447
|
if (withdrawAll) {
|
|
1567
1448
|
utils_1.logger.info("WithdrawAll selected for receiving debt token");
|
|
1568
|
-
collateralToWithdraw = collateralAmount; // All
|
|
1449
|
+
collateralToWithdraw = collateralAmount; // All JLP will be swapped
|
|
1569
1450
|
debtToRepay = (0, clend_common_1.calculateLiabilityInterest)(currentDebtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), Math.floor(Date.now() / 1000));
|
|
1570
|
-
utils_1.logger.info(`calculated debt to repay
|
|
1451
|
+
utils_1.logger.info(`calculated debt to repay`, {
|
|
1571
1452
|
debtToRepayWithInterest: debtToRepay.toString(),
|
|
1453
|
+
debtToRepayRaw: debtAmount.toString(),
|
|
1572
1454
|
});
|
|
1573
1455
|
}
|
|
1574
1456
|
else {
|
|
1575
|
-
utils_1.logger.info("Calculating partial deleverage for receiving debt token
|
|
1457
|
+
utils_1.logger.info("Calculating partial deleverage for receiving debt token");
|
|
1576
1458
|
// 1. Calculate estimated current debt accurately
|
|
1577
1459
|
const estimatedCurrentDebt = (0, clend_common_1.calculateLiabilityInterest)(currentDebtBalance.liabilityShares, debtBankData.liabilityShareValue, debtBankData.borrowApy, debtBankData.lastUpdate.toNumber(), Math.floor(Date.now() / 1000));
|
|
1578
1460
|
const estimatedCurrentDebtUi = (0, clend_common_1.amountToUi)(estimatedCurrentDebt, debtDecimals);
|
|
1579
1461
|
const collateralAmountUI = (0, clend_common_1.amountToUi)(collateralAmount, collateralDecimals);
|
|
1580
1462
|
const desiredNetWithdrawalOfDebtUi = (0, clend_common_1.amountToUi)(desiredNetWithdraw, debtDecimals);
|
|
1581
|
-
//
|
|
1463
|
+
// Convert the value of the desired debt withdrawal into an equivalent amount of collateral
|
|
1464
|
+
// This is the "collateral-equivalent" value of the equity being removed.
|
|
1582
1465
|
const netWithdrawAmountInCollateralUi = (0, clend_common_1.calculateWeightedValue)(desiredNetWithdrawalOfDebtUi, debtPrice, 1) /
|
|
1583
1466
|
collateralPrice;
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1467
|
+
const { debtToRepayUi, totalCollateralToWithdrawUi, newLeverage, currentLeverage, } = (0, clend_common_1.computeWithdrawLeverageAmounts)(collateralAmountUI, estimatedCurrentDebtUi, netWithdrawAmountInCollateralUi, collateralPrice, debtPrice);
|
|
1468
|
+
utils_1.logger.info("computeWithdrawLeverageAmounts", {
|
|
1469
|
+
debtToRepayUi,
|
|
1470
|
+
totalCollateralToWithdrawUi,
|
|
1471
|
+
newLeverage,
|
|
1472
|
+
currentLeverage,
|
|
1473
|
+
});
|
|
1589
1474
|
const baseDebtToRepay = (0, clend_common_1.uiToAmount)(debtToRepayUi, debtDecimals);
|
|
1475
|
+
// --- Step 5: Add Buffers to the Debt Repayment Target ---
|
|
1476
|
+
// This adds a small buffer for interest that might accrue during the transaction's execution time.
|
|
1590
1477
|
const timeBufferSec = 15;
|
|
1591
1478
|
debtToRepay = (0, clend_common_1.addInterestAccrualBuffer)(baseDebtToRepay, debtBankData.borrowApy, timeBufferSec);
|
|
1592
|
-
//
|
|
1479
|
+
// --- Step 2: Determine total USDC needed from the swap ---
|
|
1480
|
+
// The total value we need to get from selling JLP is the debt we must repay
|
|
1481
|
+
const totalDebtNeededFromSwap = debtToRepay.add(desiredNetWithdraw);
|
|
1482
|
+
// Now, find how much collateral is needed to produce this swap via swap
|
|
1483
|
+
const swapQuote = await this.swapper.getQuote({
|
|
1484
|
+
inputMint: collateralMint,
|
|
1485
|
+
inputMintDecimals: collateralDecimals,
|
|
1486
|
+
outputMint: debtMint,
|
|
1487
|
+
outputMintDecimals: debtDecimals,
|
|
1488
|
+
inputAmount: totalDebtNeededFromSwap,
|
|
1489
|
+
slippageBps,
|
|
1490
|
+
swapMode: "ExactOut",
|
|
1491
|
+
});
|
|
1492
|
+
// The answer from swapper is the total collateral we must withdraw.
|
|
1493
|
+
collateralToWithdraw = swapQuote.inAmount;
|
|
1494
|
+
// --- Step 4: Final Sanity Check ---
|
|
1593
1495
|
if (collateralToWithdraw.gt(collateralAmount)) {
|
|
1594
1496
|
throw new Error("Total required collateral for withdrawal and swap exceeds the available balance.");
|
|
1595
1497
|
}
|
|
@@ -1605,9 +1507,9 @@ class ClendClient {
|
|
|
1605
1507
|
debtBankData,
|
|
1606
1508
|
};
|
|
1607
1509
|
}
|
|
1608
|
-
async adjustLeverage(clendGroup, clendAccount, collateralMint, debtMint, targetLeverage, slippageBps
|
|
1510
|
+
async adjustLeverage(clendGroup, clendAccount, collateralMint, debtMint, targetLeverage, slippageBps) {
|
|
1609
1511
|
const params = await this.getAdjustLeverageParams(clendGroup, clendAccount, collateralMint, debtMint, targetLeverage, slippageBps);
|
|
1610
|
-
const { ixns, luts } = await this.getAdjustLeverageIxns(clendGroup, clendAccount, this.address(), params.debtBankData, params.collateralBankData, params.isIncrease, params.finalDebtDeltaUi, params.finalCollateralDeltaUi, slippageBps, 0
|
|
1512
|
+
const { ixns, luts } = await this.getAdjustLeverageIxns(clendGroup, clendAccount, this.address(), params.debtBankData, params.collateralBankData, params.isIncrease, params.finalDebtDeltaUi, params.finalCollateralDeltaUi, slippageBps, 0);
|
|
1611
1513
|
// Send transaction
|
|
1612
1514
|
return this.send(ixns, [], luts);
|
|
1613
1515
|
}
|
|
@@ -1751,7 +1653,7 @@ class ClendClient {
|
|
|
1751
1653
|
finalDebtToRepayTargetBN: finalDebtToRepayTargetBN.toString(),
|
|
1752
1654
|
finalAdjustedDebtDeltaUi: finalDebtDeltaUi,
|
|
1753
1655
|
});
|
|
1754
|
-
// 3. Recalculate Collateral Delta needed for the swap
|
|
1656
|
+
// 3. Recalculate Collateral Delta needed for the swap (ExactOut)
|
|
1755
1657
|
const slippageFactor = new decimal_js_1.default(slippageBps).div(10000);
|
|
1756
1658
|
if (slippageFactor.gte(1)) {
|
|
1757
1659
|
throw new Error("Slippage cannot be 100% or more.");
|
|
@@ -1815,12 +1717,7 @@ class ClendClient {
|
|
|
1815
1717
|
*/
|
|
1816
1718
|
async getAdjustLeverageIxns(clendGroup, clendAccount, user, debtBankData, collateralBankData, isIncrease, finalDebtDeltaUi, // Use the final adjusted value from getAdjustLeverageParams
|
|
1817
1719
|
finalCollateralDeltaUi, // Use the final adjusted value from getAdjustLeverageParams
|
|
1818
|
-
slippageBps, additionalIxnCount
|
|
1819
|
-
// override swapper if provided
|
|
1820
|
-
let activeSwapper = this.swapper;
|
|
1821
|
-
if (swapperOverride) {
|
|
1822
|
-
activeSwapper = swapperOverride;
|
|
1823
|
-
}
|
|
1720
|
+
slippageBps, additionalIxnCount) {
|
|
1824
1721
|
const clendAccountData = await this.getClendAccount(clendAccount);
|
|
1825
1722
|
if (!clendAccountData) {
|
|
1826
1723
|
throw new Error(`Clend account not found: ${clendAccount.toString()}`);
|
|
@@ -1849,60 +1746,90 @@ class ClendClient {
|
|
|
1849
1746
|
let swapLookupTables = [];
|
|
1850
1747
|
if (isIncrease) {
|
|
1851
1748
|
utils_1.logger.info("Building instructions for increasing leverage");
|
|
1852
|
-
//
|
|
1749
|
+
// Use the finalDebtDeltaUi passed in (already adjusted)
|
|
1853
1750
|
const additionalDebtAmount = (0, clend_common_1.uiToAmount)(finalDebtDeltaUi, debtBankData.mintDecimals);
|
|
1854
|
-
|
|
1855
|
-
|
|
1751
|
+
utils_1.logger.debug("additionalDebtAmount to borrow (final adjusted)", {
|
|
1752
|
+
additionalDebtAmount: additionalDebtAmount.toString(),
|
|
1753
|
+
});
|
|
1754
|
+
// Get Jupiter quote for swapping debt token to collateral token (ExactIn)
|
|
1755
|
+
const swapMode = "ExactIn";
|
|
1756
|
+
const swapQuote = await this.swapper.getQuote({
|
|
1856
1757
|
inputMint: debtBankData.mint,
|
|
1857
1758
|
inputMintDecimals: debtBankData.mintDecimals,
|
|
1858
1759
|
outputMint: collateralBankData.mint,
|
|
1859
1760
|
outputMintDecimals: collateralBankData.mintDecimals,
|
|
1860
1761
|
inputAmount: additionalDebtAmount,
|
|
1861
1762
|
slippageBps,
|
|
1862
|
-
swapMode
|
|
1763
|
+
swapMode,
|
|
1764
|
+
});
|
|
1765
|
+
utils_1.logger.debug("swapQuote calculated (Increase)", {
|
|
1766
|
+
inAmount: swapQuote.inAmount.toString(),
|
|
1767
|
+
outAmount: swapQuote.outAmount.toString(),
|
|
1768
|
+
otherAmountThreshold: swapQuote.otherAmountThreshold.toString(),
|
|
1863
1769
|
});
|
|
1864
|
-
|
|
1865
|
-
const
|
|
1770
|
+
// Get swap instructions
|
|
1771
|
+
const swapIxns = await this.swapper.getSwapIxns(user, swapQuote);
|
|
1772
|
+
// Borrow the final adjusted debt amount
|
|
1773
|
+
const borrowIx = await this.instructions.borrow(clendGroup, clendAccount, user, debtBankData.key, userDebtAta, debtTokenProgram, additionalDebtAmount, // Use final adjusted amount
|
|
1774
|
+
remainingAccounts);
|
|
1775
|
+
// Calculate additional collateral expected from swap (based on quote)
|
|
1866
1776
|
const additionalCollateralAmount = swapQuote.outAmount;
|
|
1867
|
-
|
|
1777
|
+
utils_1.logger.debug("additionalCollateralAmount expected from swap", {
|
|
1778
|
+
additionalCollateralAmount: additionalCollateralAmount.toString(),
|
|
1779
|
+
});
|
|
1780
|
+
// Deposit additional collateral received from swap
|
|
1781
|
+
const depositIx = await this.instructions.deposit(clendGroup, clendAccount, user, collateralBankData.key, userCollateralAta, collateralTokenProgram, additionalCollateralAmount, // Deposit what the swap provides
|
|
1782
|
+
true, remainingAccounts);
|
|
1868
1783
|
instructions.push(borrowIx, ...swapIxns.ixns, depositIx);
|
|
1869
1784
|
swapLookupTables = swapIxns.luts;
|
|
1870
1785
|
}
|
|
1871
1786
|
else {
|
|
1872
|
-
//
|
|
1787
|
+
// Decreasing Leverage
|
|
1873
1788
|
utils_1.logger.info("Building instructions for decreasing leverage");
|
|
1874
|
-
//
|
|
1875
|
-
const
|
|
1876
|
-
//
|
|
1877
|
-
const
|
|
1878
|
-
|
|
1789
|
+
// Use the finalDebtDeltaUi passed in (already includes future buffer)
|
|
1790
|
+
const finalDebtToRepayBN = (0, clend_common_1.uiToAmount)(finalDebtDeltaUi, debtBankData.mintDecimals);
|
|
1791
|
+
// Use the finalCollateralDeltaUi passed in (already includes slippage adjustment for swap)
|
|
1792
|
+
const finalCollateralToWithdrawBN = (0, clend_common_1.uiToAmount)(finalCollateralDeltaUi, collateralBankData.mintDecimals);
|
|
1793
|
+
utils_1.logger.debug("Final amounts for decrease operation (Native)", {
|
|
1794
|
+
finalDebtToRepayBN: finalDebtToRepayBN.toString(),
|
|
1795
|
+
finalCollateralToWithdrawBN: finalCollateralToWithdrawBN.toString(),
|
|
1796
|
+
});
|
|
1797
|
+
// Get Jupiter quote for swapping collateral token to debt token (ExactOut)
|
|
1798
|
+
// Target the final buffered debt repayment amount
|
|
1799
|
+
const swapMode = "ExactOut";
|
|
1800
|
+
const swapQuote = await this.swapper.getQuote({
|
|
1879
1801
|
inputMint: collateralBankData.mint,
|
|
1880
1802
|
inputMintDecimals: collateralBankData.mintDecimals,
|
|
1881
1803
|
outputMint: debtBankData.mint,
|
|
1882
1804
|
outputMintDecimals: debtBankData.mintDecimals,
|
|
1883
|
-
inputAmount:
|
|
1805
|
+
inputAmount: finalDebtToRepayBN,
|
|
1884
1806
|
slippageBps,
|
|
1885
|
-
swapMode
|
|
1807
|
+
swapMode,
|
|
1886
1808
|
});
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1809
|
+
const estimatedCollateralInput = swapQuote.inAmount;
|
|
1810
|
+
utils_1.logger.debug("swapQuote calculated (Decrease)", {
|
|
1811
|
+
targetOutput: finalDebtToRepayBN.toString(),
|
|
1812
|
+
estimatedInput: estimatedCollateralInput,
|
|
1813
|
+
maxInputCalculated: finalCollateralToWithdrawBN.toString(), // Compare Jupiter estimate vs our max calc
|
|
1814
|
+
});
|
|
1815
|
+
const swapIxns = await this.swapper.getSwapIxns(user, swapQuote);
|
|
1816
|
+
// Withdraw the CALCULATED MAXIMUM collateral needed (includes swap input + buffer)
|
|
1817
|
+
const withdrawIx = await this.instructions.withdraw(clendGroup, clendAccount, user, collateralBankData.key, userCollateralAta, collateralTokenProgram, finalCollateralToWithdrawBN, // Use the final adjusted amount from params
|
|
1818
|
+
false, // Not withdrawing all
|
|
1893
1819
|
remainingAccounts);
|
|
1894
|
-
// Repay the
|
|
1895
|
-
const repayIx = await this.instructions.repay(clendGroup, clendAccount, user, debtBankData.key, userDebtAta, debtTokenProgram,
|
|
1820
|
+
// Repay the CALCULATED BUFFERED debt amount
|
|
1821
|
+
const repayIx = await this.instructions.repay(clendGroup, clendAccount, user, debtBankData.key, userDebtAta, debtTokenProgram, finalDebtToRepayBN, // Use the final adjusted amount from params
|
|
1896
1822
|
false, // Not repaying all
|
|
1897
|
-
true, // Use the new repay_up_to_amount flag
|
|
1898
1823
|
remainingAccounts);
|
|
1899
1824
|
instructions.push(withdrawIx, ...swapIxns.ixns, repayIx);
|
|
1900
1825
|
swapLookupTables = swapIxns.luts;
|
|
1901
1826
|
}
|
|
1902
1827
|
// --- Flash Loan Wrapping ---
|
|
1903
|
-
const cuIxns = 2;
|
|
1828
|
+
const cuIxns = 2; // Assuming 2 compute budget instructions are added later
|
|
1829
|
+
// Calculate end index based on actual instructions generated + potential JIT ixns + flash loan ixns
|
|
1904
1830
|
const endIndex = new anchor_1.BN(cuIxns + instructions.length + 1 + additionalIxnCount);
|
|
1905
1831
|
const { beginFlashLoanIx, endFlashLoanIx } = await this.instructions.createFlashLoanInstructions(clendAccount, user, endIndex, remainingAccounts);
|
|
1832
|
+
// Create a new array with the flash loan instructions wrapping the existing instructions
|
|
1906
1833
|
const finalInstructions = [
|
|
1907
1834
|
beginFlashLoanIx,
|
|
1908
1835
|
...instructions,
|