@kamino-finance/klend-sdk 5.13.7 → 5.13.8

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 (33) hide show
  1. package/dist/classes/obligation.d.ts +1 -0
  2. package/dist/classes/obligation.d.ts.map +1 -1
  3. package/dist/classes/obligation.js +7 -0
  4. package/dist/classes/obligation.js.map +1 -1
  5. package/dist/classes/vault.d.ts +9 -1
  6. package/dist/classes/vault.d.ts.map +1 -1
  7. package/dist/classes/vault.js +37 -0
  8. package/dist/classes/vault.js.map +1 -1
  9. package/dist/classes/vault_types.d.ts +4 -0
  10. package/dist/classes/vault_types.d.ts.map +1 -1
  11. package/dist/client_kamino_manager.d.ts.map +1 -1
  12. package/dist/client_kamino_manager.js +8 -0
  13. package/dist/client_kamino_manager.js.map +1 -1
  14. package/dist/lending_operations/repay_with_collateral_calcs.d.ts +3 -4
  15. package/dist/lending_operations/repay_with_collateral_calcs.d.ts.map +1 -1
  16. package/dist/lending_operations/repay_with_collateral_calcs.js +36 -34
  17. package/dist/lending_operations/repay_with_collateral_calcs.js.map +1 -1
  18. package/dist/lending_operations/repay_with_collateral_operations.d.ts +3 -2
  19. package/dist/lending_operations/repay_with_collateral_operations.d.ts.map +1 -1
  20. package/dist/lending_operations/repay_with_collateral_operations.js +40 -30
  21. package/dist/lending_operations/repay_with_collateral_operations.js.map +1 -1
  22. package/dist/utils/multisig.d.ts +11 -1
  23. package/dist/utils/multisig.d.ts.map +1 -1
  24. package/dist/utils/multisig.js +8 -5
  25. package/dist/utils/multisig.js.map +1 -1
  26. package/package.json +1 -1
  27. package/src/classes/obligation.ts +10 -0
  28. package/src/classes/vault.ts +51 -0
  29. package/src/classes/vault_types.ts +5 -0
  30. package/src/client_kamino_manager.ts +9 -0
  31. package/src/lending_operations/repay_with_collateral_calcs.ts +56 -39
  32. package/src/lending_operations/repay_with_collateral_operations.ts +84 -33
  33. package/src/utils/multisig.ts +19 -5
@@ -83,14 +83,8 @@ export async function getRepayWithCollSwapInputs<QuoteResponse>({
83
83
  flashLoanInfo: FlashLoanInfo;
84
84
  initialInputs: RepayWithCollInitialInputs<QuoteResponse>;
85
85
  }> {
86
- const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
87
- const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
88
- if (!collReserve) {
89
- throw new Error(`Collateral reserve with mint ${collTokenMint} not found in market ${kaminoMarket.getAddress()}`);
90
- }
91
- if (!debtReserve) {
92
- throw new Error(`Debt reserve with mint ${debtTokenMint} not found in market ${kaminoMarket.getAddress()}`);
93
- }
86
+ const collReserve = kaminoMarket.getExistingReserveByMint(collTokenMint);
87
+ const debtReserve = kaminoMarket.getExistingReserveByMint(debtTokenMint);
94
88
 
95
89
  const {
96
90
  repayAmountLamports,
@@ -110,7 +104,7 @@ export async function getRepayWithCollSwapInputs<QuoteResponse>({
110
104
  `Collateral position not found for ${collReserve.stats.symbol} reserve ${collReserve.address} in obligation ${obligation.obligationAddress}`
111
105
  );
112
106
  }
113
- const { withdrawableCollLamports } = calcMaxWithdrawCollateral(
107
+ const { maxWithdrawableCollLamports } = calcMaxWithdrawCollateral(
114
108
  kaminoMarket,
115
109
  obligation,
116
110
  collReserve.address,
@@ -118,14 +112,8 @@ export async function getRepayWithCollSwapInputs<QuoteResponse>({
118
112
  repayAmountLamports
119
113
  );
120
114
 
121
- // sanity check: we have extra collateral to swap, but we want to ensure we don't quote for way more than needed and get a bad px
122
- const maxCollNeededFromOracle = finalRepayAmount
123
- .mul(debtReserve.getOracleMarketPrice())
124
- .div(collReserve.getOracleMarketPrice())
125
- .mul('1.1')
126
- .mul(collReserve.getMintFactor())
127
- .ceil();
128
- const inputAmountLamports = Decimal.min(withdrawableCollLamports, maxCollNeededFromOracle);
115
+ const maxCollNeededFromOracle = getMaxCollateralFromRepayAmount(finalRepayAmount, debtReserve, collReserve);
116
+ const inputAmountLamports = Decimal.min(maxWithdrawableCollLamports, maxCollNeededFromOracle);
129
117
 
130
118
  // Build the repay & withdraw collateral tx to get the number of accounts
131
119
  const klendIxs: LeverageIxsOutput = await buildRepayWithCollateralIxs(
@@ -153,7 +141,7 @@ export async function getRepayWithCollSwapInputs<QuoteResponse>({
153
141
  inputAmountLamports,
154
142
  inputMint: collTokenMint,
155
143
  outputMint: debtTokenMint,
156
- amountDebtAtaBalance: new Decimal(0), // only used for kTokens
144
+ amountDebtAtaBalance: undefined, // only used for kTokens
157
145
  };
158
146
 
159
147
  const swapQuote = await quoter(swapQuoteInputs, uniqueKlendAccounts);
@@ -171,13 +159,13 @@ export async function getRepayWithCollSwapInputs<QuoteResponse>({
171
159
  minOutAmountLamports: flashRepayAmountLamports,
172
160
  inputMint: collTokenMint,
173
161
  outputMint: debtTokenMint,
174
- amountDebtAtaBalance: new Decimal(0), // only used for kTokens
162
+ amountDebtAtaBalance: undefined, // only used for kTokens
175
163
  },
176
164
  flashLoanInfo: klendIxs.flashLoanInfo,
177
165
  initialInputs: {
178
166
  debtRepayAmountLamports: repayAmountLamports,
179
167
  flashRepayAmountLamports,
180
- maxCollateralWithdrawLamports: withdrawableCollLamports,
168
+ maxCollateralWithdrawLamports: maxWithdrawableCollLamports,
181
169
  swapQuote,
182
170
  currentSlot,
183
171
  klendAccounts: uniqueKlendAccounts,
@@ -223,17 +211,8 @@ export async function getRepayWithCollIxs<QuoteResponse>({
223
211
  const { debtRepayAmountLamports, flashRepayAmountLamports, maxCollateralWithdrawLamports, swapQuote } = initialInputs;
224
212
  const { inputAmountLamports: collSwapInLamports } = swapInputs;
225
213
 
226
- const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
227
-
228
- if (!collReserve) {
229
- throw new Error(`Collateral reserve with mint ${collTokenMint} not found in market ${kaminoMarket.getAddress()}`);
230
- }
231
-
232
- const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
233
-
234
- if (!debtReserve) {
235
- throw new Error(`Debt reserve with mint ${debtTokenMint} not found in market ${kaminoMarket.getAddress()}`);
236
- }
214
+ const collReserve = kaminoMarket.getExistingReserveByMint(collTokenMint);
215
+ const debtReserve = kaminoMarket.getExistingReserveByMint(debtTokenMint);
237
216
 
238
217
  // the client should use these values to prevent this input, but the tx may succeed, so we don't want to fail
239
218
  // there is also a chance that the tx will consume debt token from the user's ata which they would not expect
@@ -326,7 +305,13 @@ async function buildRepayWithCollateralIxs(
326
305
 
327
306
  const requestElevationGroup = !isClosingPosition && obligation.state.elevationGroup !== 0;
328
307
 
329
- const maxWithdrawLtvCheck = getMaxWithdrawLtvCheck(obligation);
308
+ const maxWithdrawLtvCheck = getMaxWithdrawLtvCheck(
309
+ obligation,
310
+ debtRepayAmountLamports,
311
+ debtReserve,
312
+ collWithdrawLamports,
313
+ collReserve
314
+ );
330
315
 
331
316
  // 3. Repay using the flash borrowed funds & withdraw collateral to swap and pay the flash loan
332
317
  let repayAndWithdrawAction;
@@ -393,8 +378,74 @@ async function buildRepayWithCollateralIxs(
393
378
  return res;
394
379
  }
395
380
 
396
- export const getMaxWithdrawLtvCheck = (obligation: KaminoObligation) => {
381
+ export const getMaxWithdrawLtvCheck = (
382
+ obligation: KaminoObligation,
383
+ repayAmountLamports: Decimal,
384
+ debtReserve: KaminoReserve,
385
+ collWithdrawAmount: Decimal,
386
+ collReserve: KaminoReserve
387
+ ) => {
388
+ const [finalLtv, finalMaxLtv] = calculatePostOperationLtv(
389
+ obligation,
390
+ repayAmountLamports,
391
+ debtReserve,
392
+ collWithdrawAmount,
393
+ collReserve
394
+ );
395
+
396
+ if (finalLtv.lte(finalMaxLtv)) {
397
+ return MaxWithdrawLtvCheck.MAX_LTV;
398
+ }
399
+
397
400
  return obligation.refreshedStats.userTotalBorrowBorrowFactorAdjusted.gte(obligation.refreshedStats.borrowLimit)
398
401
  ? MaxWithdrawLtvCheck.LIQUIDATION_THRESHOLD
399
402
  : MaxWithdrawLtvCheck.MAX_LTV;
400
403
  };
404
+
405
+ function calculatePostOperationLtv(
406
+ obligation: KaminoObligation,
407
+ repayAmountLamports: Decimal,
408
+ debtReserve: KaminoReserve,
409
+ collWithdrawAmount: Decimal,
410
+ collReserve: KaminoReserve
411
+ ): [Decimal, Decimal] {
412
+ const repayValue = repayAmountLamports
413
+ .div(debtReserve.getMintFactor())
414
+ .mul(debtReserve.getOracleMarketPrice())
415
+ .mul(debtReserve.getBorrowFactor());
416
+ const collWithdrawValue = collWithdrawAmount.div(collReserve.getMintFactor()).mul(collReserve.getOracleMarketPrice());
417
+
418
+ // Calculate new borrow value and deposit value
419
+ const newBorrowBfValue = Decimal.max(
420
+ new Decimal(0),
421
+ obligation.refreshedStats.userTotalBorrowBorrowFactorAdjusted.sub(repayValue)
422
+ );
423
+ const newDepositValue = Decimal.max(
424
+ new Decimal(0),
425
+ obligation.refreshedStats.userTotalDeposit.sub(collWithdrawValue)
426
+ );
427
+
428
+ const newMaxBorrowableValue = Decimal.max(
429
+ new Decimal(0),
430
+ obligation.refreshedStats.borrowLimit.sub(collWithdrawValue.mul(collReserve.stats.loanToValue))
431
+ );
432
+
433
+ const newMaxLtv = newMaxBorrowableValue.div(newDepositValue);
434
+
435
+ // return final ltv and final max ltv
436
+ return [newBorrowBfValue.div(newDepositValue), newMaxLtv];
437
+ }
438
+
439
+ export function getMaxCollateralFromRepayAmount(
440
+ repayAmount: Decimal,
441
+ debtReserve: KaminoReserve,
442
+ collReserve: KaminoReserve
443
+ ) {
444
+ // sanity check: we have extra collateral to swap, but we want to ensure we don't quote for way more than needed and get a bad px
445
+ return repayAmount
446
+ .mul(debtReserve.getOracleMarketPrice())
447
+ .div(collReserve.getOracleMarketPrice())
448
+ .mul('1.1')
449
+ .mul(collReserve.getMintFactor())
450
+ .ceil();
451
+ }
@@ -1,22 +1,25 @@
1
1
  import { PublicKey } from '@solana/web3.js';
2
2
 
3
- const SQUADS_API_BASE_URL = 'https://4fnetmviidiqkjzenwxe66vgoa0soerr.lambda-url.us-east-1.on.aws/';
3
+ const SQUADS_API_BASE_URL = 'https://4fnetmviidiqkjzenwxe66vgoa0soerr.lambda-url.us-east-1.on.aws';
4
4
 
5
5
  export async function walletIsSquadsMultisig(wallet: PublicKey) {
6
- const response = await fetch(`${SQUADS_API_BASE_URL}/is-multisig?wallet=${wallet.toBase58()}`);
6
+ const response = await fetch(`${SQUADS_API_BASE_URL}/isSquad/${wallet.toBase58()}`);
7
7
  const data = await response.json();
8
8
  const squadsResponse = data as SquadsMultisigResponse;
9
9
  return squadsResponse.isSquad;
10
10
  }
11
11
 
12
12
  // todo: find a way to get the admins number and threshold
13
- export async function getSquadsMultisigAdminsAndThreshold(_wallet: PublicKey): Promise<{
13
+ export async function getSquadsMultisigAdminsAndThreshold(wallet: PublicKey): Promise<{
14
14
  adminsNumber: number;
15
15
  threshold: number;
16
16
  }> {
17
+ const response = await fetch(`${SQUADS_API_BASE_URL}/multisig/${wallet.toBase58()}`);
18
+ const data = await response.json();
19
+ const squadsResponse = data as SquadsMultisigAccountResponse;
17
20
  return {
18
- adminsNumber: 1,
19
- threshold: 1,
21
+ adminsNumber: squadsResponse.keys.length,
22
+ threshold: squadsResponse.threshold,
20
23
  };
21
24
  }
22
25
 
@@ -31,3 +34,14 @@ export interface WalletType {
31
34
  walletAdminsNumber: number;
32
35
  walletThreshold: number;
33
36
  }
37
+
38
+ export type SquadsMultisigAccountResponse = {
39
+ allow_external_execute: boolean;
40
+ authority_index: number;
41
+ bump: number;
42
+ create_key: string;
43
+ keys: number[][];
44
+ ms_change_index: number;
45
+ threshold: number;
46
+ transaction_index: number;
47
+ };