@kamino-finance/klend-sdk 5.2.13 → 5.2.14

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.
Files changed (29) hide show
  1. package/dist/classes/obligation.d.ts +2 -0
  2. package/dist/classes/obligation.d.ts.map +1 -1
  3. package/dist/classes/obligation.js +12 -6
  4. package/dist/classes/obligation.js.map +1 -1
  5. package/dist/classes/types.d.ts +1 -1
  6. package/dist/classes/types.d.ts.map +1 -1
  7. package/dist/classes/vault.d.ts +1 -1
  8. package/dist/classes/vault.d.ts.map +1 -1
  9. package/dist/classes/vault.js +16 -3
  10. package/dist/classes/vault.js.map +1 -1
  11. package/dist/client_kamino_manager.d.ts.map +1 -1
  12. package/dist/client_kamino_manager.js +38 -4
  13. package/dist/client_kamino_manager.js.map +1 -1
  14. package/dist/lending_operations/repay_with_collateral_calcs.d.ts +4 -2
  15. package/dist/lending_operations/repay_with_collateral_calcs.d.ts.map +1 -1
  16. package/dist/lending_operations/repay_with_collateral_calcs.js +45 -52
  17. package/dist/lending_operations/repay_with_collateral_calcs.js.map +1 -1
  18. package/dist/lending_operations/repay_with_collateral_operations.d.ts +7 -0
  19. package/dist/lending_operations/repay_with_collateral_operations.d.ts.map +1 -1
  20. package/dist/lending_operations/repay_with_collateral_operations.js +13 -3
  21. package/dist/lending_operations/repay_with_collateral_operations.js.map +1 -1
  22. package/package.json +1 -1
  23. package/src/classes/obligation.ts +15 -6
  24. package/src/classes/types.ts +16 -17
  25. package/src/classes/vault.ts +18 -4
  26. package/src/client_kamino_manager.ts +55 -4
  27. package/src/lending_operations/repay_with_collateral_calcs.ts +55 -61
  28. package/src/lending_operations/repay_with_collateral_operations.ts +24 -4
  29. package/src/leverage/operations.ts +1 -1
@@ -76,79 +76,73 @@ export const calcFlashRepayAmount = (props: {
76
76
  };
77
77
 
78
78
  export function calcMaxWithdrawCollateral(
79
- kaminoMarket: KaminoMarket,
79
+ market: KaminoMarket,
80
+ obligation: KaminoObligation,
80
81
  collReserveAddr: PublicKey,
81
82
  debtReserveAddr: PublicKey,
82
- obligation: KaminoObligation,
83
83
  repayAmountLamports: Decimal
84
84
  ): {
85
- canWithdrawRemainingColl: boolean;
85
+ repayAmountLamports: Decimal;
86
86
  withdrawableCollLamports: Decimal;
87
+ canWithdrawAllColl: boolean;
88
+ repayingAllDebt: boolean;
87
89
  } {
88
- const collReserve = kaminoMarket.getReserveByAddress(collReserveAddr)!;
89
- const collOraclePx = collReserve.getOracleMarketPrice();
90
- const { maxLtv: collMaxLtv } = obligation.getLtvForReserve(kaminoMarket, collReserve);
91
-
92
- const collPosition = obligation.getDepositByReserve(collReserve.address)!;
93
- const initialCollValue = collPosition.amount.floor().div(collReserve.getMintFactor()).mul(collOraclePx);
94
-
95
- let totalRemainingDebtValue = new Decimal(0);
96
- const borrows = obligation.getBorrows();
97
- for (const debtPosition of borrows) {
98
- const debtReserve = kaminoMarket.getReserveByAddress(debtPosition.reserveAddress)!;
99
- const debtOraclePx = debtReserve.getOracleMarketPrice();
100
- const debtBorrowFactor = debtReserve.getBorrowFactor();
101
- let remainingDebtAmountLamports = debtPosition.amount;
102
- if (debtPosition.reserveAddress.equals(debtReserveAddr)) {
103
- remainingDebtAmountLamports = remainingDebtAmountLamports.sub(repayAmountLamports);
104
- }
105
- const remainingDebtBfWeightedValue = remainingDebtAmountLamports
106
- .ceil()
107
- .div(debtReserve.getMintFactor())
108
- .mul(debtBorrowFactor)
109
- .mul(debtOraclePx);
110
- totalRemainingDebtValue = totalRemainingDebtValue.add(remainingDebtBfWeightedValue);
111
- }
112
-
113
- let canWithdrawRemainingColl = false;
114
- if (totalRemainingDebtValue.lte(new Decimal(0)) && borrows.length === 1) {
115
- canWithdrawRemainingColl = true;
90
+ const deposit = obligation.getDepositByReserve(collReserveAddr)!;
91
+ const borrow = obligation.getBorrowByReserve(debtReserveAddr)!;
92
+ const depositReserve = market.getReserveByAddress(deposit.reserveAddress)!;
93
+ const debtReserve = market.getReserveByAddress(borrow.reserveAddress)!;
94
+ const depositTotalLamports = deposit.amount.floor();
95
+
96
+ const remainingBorrowLamports = borrow.amount.sub(repayAmountLamports).ceil();
97
+ const remainingBorrowAmount = remainingBorrowLamports.div(debtReserve.getMintFactor());
98
+ let remainingBorrowsValue = remainingBorrowAmount.mul(debtReserve.getOracleMarketPrice());
99
+ if (obligation.getBorrows().length > 1) {
100
+ remainingBorrowsValue = obligation
101
+ .getBorrows()
102
+ .filter((p) => !p.reserveAddress.equals(borrow.reserveAddress))
103
+ .reduce((acc, b) => acc.add(b.marketValueRefreshed), new Decimal('0'));
116
104
  }
117
105
 
118
- const deposits = obligation.getDeposits();
119
- const otherCollDeposits = deposits.filter((deposit) => !deposit.reserveAddress.equals(collReserve.address));
120
-
121
- let totalOtherCollateralValue = new Decimal(0);
122
- for (const d of otherCollDeposits) {
123
- const otherCollReserve = kaminoMarket.getReserveByAddress(d.reserveAddress)!;
124
- const otherCollOraclePx = otherCollReserve.getOracleMarketPrice();
125
- const otherCollMaxLtv = obligation.getLtvForReserve(kaminoMarket, otherCollReserve).maxLtv;
126
- const otherCollValue = d.amount
127
- .floor()
128
- .div(otherCollReserve.getMintFactor())
129
- .mul(otherCollOraclePx)
130
- .mul(otherCollMaxLtv);
131
- totalOtherCollateralValue = totalOtherCollateralValue.add(otherCollValue);
106
+ let remainingDepositsValueWithLtv = new Decimal('0');
107
+ if (obligation.getDeposits().length > 1) {
108
+ remainingDepositsValueWithLtv = obligation
109
+ .getDeposits()
110
+ .filter((p) => !p.reserveAddress.equals(deposit.reserveAddress))
111
+ .reduce((acc, d) => {
112
+ const { maxLtv } = obligation.getLtvForReserve(market, market.getReserveByAddress(d.reserveAddress)!);
113
+ return acc.add(d.marketValueRefreshed.mul(maxLtv));
114
+ }, new Decimal('0'));
132
115
  }
133
116
 
134
- const numerator = initialCollValue.mul(collMaxLtv).add(totalOtherCollateralValue).sub(totalRemainingDebtValue);
135
-
136
- // If all collateral cannot cover the remaining debt
137
- if (numerator.lte('0')) {
138
- return { canWithdrawRemainingColl: false, withdrawableCollLamports: new Decimal(0) };
139
- }
140
-
141
- const denominator = collOraclePx.mul(collMaxLtv);
142
- const maxCollWithdrawAmount = numerator.div(denominator);
143
- const maxCollateralWithdrawalAmountLamports = maxCollWithdrawAmount.mul(collReserve.getMintFactor()).floor();
144
-
145
- let withdrawableCollLamports: Decimal;
146
- if (canWithdrawRemainingColl) {
147
- withdrawableCollLamports = Decimal.min(maxCollateralWithdrawalAmountLamports, collPosition.amount).floor();
117
+ // can withdraw all coll
118
+ if (remainingDepositsValueWithLtv.gte(remainingBorrowsValue)) {
119
+ return {
120
+ repayAmountLamports: repayAmountLamports,
121
+ withdrawableCollLamports: depositTotalLamports,
122
+ canWithdrawAllColl: true,
123
+ repayingAllDebt: repayAmountLamports.gte(borrow.amount),
124
+ };
148
125
  } else {
149
- withdrawableCollLamports = Decimal.max(new Decimal(0), maxCollateralWithdrawalAmountLamports);
126
+ const { maxLtv: collMaxLtv } = obligation.getLtvForReserve(
127
+ market,
128
+ market.getReserveByAddress(depositReserve.address)!
129
+ );
130
+ const numerator = deposit.marketValueRefreshed
131
+ .mul(collMaxLtv)
132
+ .add(remainingDepositsValueWithLtv)
133
+ .sub(remainingBorrowsValue);
134
+
135
+ const denominator = depositReserve.getOracleMarketPrice().mul(collMaxLtv);
136
+ const maxCollWithdrawAmount = numerator.div(denominator);
137
+ const withdrawableCollLamports = maxCollWithdrawAmount.mul(depositReserve.getMintFactor()).floor();
138
+
139
+ return {
140
+ repayAmountLamports: repayAmountLamports,
141
+ withdrawableCollLamports,
142
+ canWithdrawAllColl: false,
143
+ repayingAllDebt: repayAmountLamports.gte(borrow.amount),
144
+ };
150
145
  }
151
- return { canWithdrawRemainingColl, withdrawableCollLamports };
152
146
  }
153
147
 
154
148
  export function estimateDebtRepaymentWithColl(props: {
@@ -29,6 +29,13 @@ export type RepayWithCollIxsResponse<QuoteResponse> = {
29
29
  export type InitialInputs<QuoteResponse> = {
30
30
  debtRepayAmountLamports: Decimal;
31
31
  flashRepayAmountLamports: Decimal;
32
+ /**
33
+ * The amount of collateral available to withdraw, if this is less than the swap input amount, then the swap may fail due to slippage, or tokens may be debited from the user's ATA, so the caller needs to check this
34
+ */
35
+ maxCollateralWithdrawLamports: Decimal;
36
+ /**
37
+ * The quote from the provided quoter
38
+ */
32
39
  swapQuote: SwapQuote<QuoteResponse>;
33
40
  currentSlot: number;
34
41
  klendAccounts: Array<PublicKey>;
@@ -93,9 +100,9 @@ export async function getRepayWithCollSwapInputs<QuoteResponse>({
93
100
  }
94
101
  const { withdrawableCollLamports } = calcMaxWithdrawCollateral(
95
102
  kaminoMarket,
103
+ obligation,
96
104
  collReserve.address,
97
105
  debtReserve.address,
98
- obligation,
99
106
  repayAmountLamports
100
107
  );
101
108
 
@@ -156,6 +163,7 @@ export async function getRepayWithCollSwapInputs<QuoteResponse>({
156
163
  initialInputs: {
157
164
  debtRepayAmountLamports: repayAmountLamports,
158
165
  flashRepayAmountLamports,
166
+ maxCollateralWithdrawLamports: withdrawableCollLamports,
159
167
  swapQuote,
160
168
  currentSlot,
161
169
  klendAccounts: uniqueKlendAccounts,
@@ -196,18 +204,30 @@ export async function getRepayWithCollIxs<QuoteResponse>({
196
204
  budgetAndPriorityFeeIxs,
197
205
  scopeRefresh,
198
206
  });
199
- const { debtRepayAmountLamports, flashRepayAmountLamports, swapQuote } = initialInputs;
207
+ const { debtRepayAmountLamports, flashRepayAmountLamports, maxCollateralWithdrawLamports, swapQuote } = initialInputs;
200
208
  const { inputAmountLamports: collSwapInLamports } = swapInputs;
201
209
 
202
210
  const collReserve = kaminoMarket.getReserveByMint(collTokenMint)!;
203
211
  const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint)!;
204
212
 
213
+ // the client should use these values to prevent this input, but the tx may succeed, so we don't want to fail
214
+ // there is also a chance that the tx will consume debt token from the user's ata which they would not expect
215
+ if (collSwapInLamports.greaterThan(maxCollateralWithdrawLamports)) {
216
+ logger(
217
+ `Collateral swap in amount ${collSwapInLamports} exceeds max withdrawable collateral ${maxCollateralWithdrawLamports}, tx may fail with slippage`
218
+ );
219
+ swapInputs.inputAmountLamports = maxCollateralWithdrawLamports;
220
+ }
221
+
222
+ const actualSwapInLamports = Decimal.min(collSwapInLamports, maxCollateralWithdrawLamports);
205
223
  logger(
206
- `Expected to swap in: ${collSwapInLamports.div(collReserve.getMintFactor())} ${
224
+ `Expected to swap in: ${actualSwapInLamports.div(collReserve.getMintFactor())} ${
207
225
  collReserve.symbol
208
226
  }, for: ${flashRepayAmountLamports.div(debtReserve.getMintFactor())} ${debtReserve.symbol}, quoter px: ${
209
227
  swapQuote.priceAInB
210
- } ${debtReserve.symbol}/${collReserve.symbol}`
228
+ } ${debtReserve.symbol}/${collReserve.symbol}, required px: ${flashRepayAmountLamports
229
+ .div(debtReserve.getMintFactor())
230
+ .div(actualSwapInLamports.div(collReserve.getMintFactor()))} ${debtReserve.symbol}/${collReserve.symbol}`
211
231
  );
212
232
 
213
233
  const swapResponse = await swapper(swapInputs, initialInputs.klendAccounts, swapQuote);
@@ -1045,7 +1045,7 @@ export async function getAdjustLeverageSwapInputs<QuoteResponse>({
1045
1045
 
1046
1046
  const swapInputAmount = toLamports(
1047
1047
  !collIsKtoken ? calcs.borrowAmount : calcs.amountToFlashBorrowDebt,
1048
- debtReserve.stats.decimals,
1048
+ debtReserve.stats.decimals
1049
1049
  ).ceil();
1050
1050
 
1051
1051
  const swapInputsForQuote: SwapInputs = {