@kamino-finance/klend-sdk 4.0.1 → 4.1.0
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/README.md +2 -2
- package/dist/classes/action.d.ts +3 -1
- package/dist/classes/action.d.ts.map +1 -1
- package/dist/classes/action.js +62 -61
- package/dist/classes/action.js.map +1 -1
- package/dist/classes/market.d.ts +5 -5
- package/dist/classes/market.d.ts.map +1 -1
- package/dist/classes/market.js +14 -14
- package/dist/classes/market.js.map +1 -1
- package/dist/classes/obligation.d.ts +10 -1
- package/dist/classes/obligation.d.ts.map +1 -1
- package/dist/classes/obligation.js +48 -28
- package/dist/classes/obligation.js.map +1 -1
- package/dist/classes/reserve.d.ts +3 -1
- package/dist/classes/reserve.d.ts.map +1 -1
- package/dist/classes/reserve.js +39 -5
- package/dist/classes/reserve.js.map +1 -1
- package/dist/classes/shared.d.ts +8 -0
- package/dist/classes/shared.d.ts.map +1 -1
- package/dist/classes/shared.js +6 -1
- package/dist/classes/shared.js.map +1 -1
- package/dist/classes/vault.d.ts.map +1 -1
- package/dist/classes/vault.js +19 -5
- package/dist/classes/vault.js.map +1 -1
- package/dist/lending_operations/repay_with_collateral_calcs.d.ts +18 -1
- package/dist/lending_operations/repay_with_collateral_calcs.d.ts.map +1 -1
- package/dist/lending_operations/repay_with_collateral_calcs.js +62 -0
- package/dist/lending_operations/repay_with_collateral_calcs.js.map +1 -1
- package/dist/lending_operations/repay_with_collateral_operations.d.ts +27 -43
- package/dist/lending_operations/repay_with_collateral_operations.d.ts.map +1 -1
- package/dist/lending_operations/repay_with_collateral_operations.js +99 -152
- package/dist/lending_operations/repay_with_collateral_operations.js.map +1 -1
- package/dist/leverage/operations.d.ts +14 -3
- package/dist/leverage/operations.d.ts.map +1 -1
- package/dist/leverage/operations.js +184 -128
- package/dist/leverage/operations.js.map +1 -1
- package/dist/utils/ata.d.ts +12 -3
- package/dist/utils/ata.d.ts.map +1 -1
- package/dist/utils/ata.js +26 -39
- package/dist/utils/ata.js.map +1 -1
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +0 -2
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/instruction.d.ts +1 -0
- package/dist/utils/instruction.d.ts.map +1 -1
- package/dist/utils/instruction.js +18 -0
- package/dist/utils/instruction.js.map +1 -1
- package/package.json +3 -3
- package/src/classes/action.ts +92 -98
- package/src/classes/market.ts +19 -20
- package/src/classes/obligation.ts +80 -38
- package/src/classes/reserve.ts +56 -8
- package/src/classes/shared.ts +10 -0
- package/src/classes/vault.ts +23 -19
- package/src/lending_operations/repay_with_collateral_calcs.ts +98 -1
- package/src/lending_operations/repay_with_collateral_operations.ts +233 -253
- package/src/leverage/operations.ts +227 -140
- package/src/utils/ata.ts +33 -56
- package/src/utils/index.ts +0 -2
- package/src/utils/instruction.ts +22 -0
- package/dist/utils/layout.d.ts +0 -14
- package/dist/utils/layout.d.ts.map +0 -1
- package/dist/utils/layout.js +0 -123
- package/dist/utils/layout.js.map +0 -1
- package/dist/utils/syncNative.d.ts +0 -11
- package/dist/utils/syncNative.d.ts.map +0 -1
- package/dist/utils/syncNative.js +0 -45
- package/dist/utils/syncNative.js.map +0 -1
- package/src/global.d.ts +0 -1
- package/src/utils/layout.ts +0 -118
- package/src/utils/syncNative.ts +0 -22
package/src/classes/reserve.ts
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
TokenOracleData,
|
|
21
21
|
U64_MAX,
|
|
22
22
|
} from '../utils';
|
|
23
|
-
import { ReserveDataType, ReserveFarmInfo, ReserveRewardYield, ReserveStatus } from './shared';
|
|
23
|
+
import { FeeCalculation, Fees, ReserveDataType, ReserveFarmInfo, ReserveRewardYield, ReserveStatus } from './shared';
|
|
24
24
|
import { Reserve, ReserveFields } from '../idl_codegen/accounts';
|
|
25
25
|
import { BorrowRateCurve, CurvePointFields, ReserveConfig, UpdateConfigMode } from '../idl_codegen/types';
|
|
26
26
|
import { calculateAPYFromAPR, getBorrowRate, lamportsToNumberDecimal, parseTokenSymbol, positiveOrZero } from './utils';
|
|
@@ -642,6 +642,53 @@ export class KaminoReserve {
|
|
|
642
642
|
return this.state.collateral.mintPubkey;
|
|
643
643
|
}
|
|
644
644
|
|
|
645
|
+
calculateFees(
|
|
646
|
+
amountLamports: Decimal,
|
|
647
|
+
borrowFeeRate: Decimal,
|
|
648
|
+
feeCalculation: FeeCalculation,
|
|
649
|
+
referralFeeBps: number,
|
|
650
|
+
hasReferrer: boolean
|
|
651
|
+
): Fees {
|
|
652
|
+
const referralFeeRate = new Decimal(referralFeeBps).div(ONE_HUNDRED_PCT_IN_BPS);
|
|
653
|
+
if (borrowFeeRate.gt('0') && amountLamports.gt('0')) {
|
|
654
|
+
const needToAssessReferralFee = referralFeeRate.gt('0') && hasReferrer;
|
|
655
|
+
const minimumFee = new Decimal('1'); // 1 token to market owner, nothing to referrer
|
|
656
|
+
|
|
657
|
+
let borrowFeeAmount: Decimal;
|
|
658
|
+
if (feeCalculation === FeeCalculation.Exclusive) {
|
|
659
|
+
borrowFeeAmount = amountLamports.mul(borrowFeeRate);
|
|
660
|
+
} else {
|
|
661
|
+
const borrowFeeFactor = borrowFeeRate.div(borrowFeeRate.add('1'));
|
|
662
|
+
borrowFeeAmount = amountLamports.mul(borrowFeeFactor);
|
|
663
|
+
}
|
|
664
|
+
const borrowFee = Decimal.max(borrowFeeAmount, minimumFee);
|
|
665
|
+
if (borrowFee.gte(amountLamports)) {
|
|
666
|
+
throw Error('Borrow amount is too small to receive liquidity after fees');
|
|
667
|
+
}
|
|
668
|
+
const referralFee = needToAssessReferralFee
|
|
669
|
+
? referralFeeRate.eq(1)
|
|
670
|
+
? borrowFee
|
|
671
|
+
: borrowFee.mul(referralFeeRate).floor()
|
|
672
|
+
: new Decimal(0);
|
|
673
|
+
|
|
674
|
+
const protocolFee = borrowFee.sub(referralFee);
|
|
675
|
+
|
|
676
|
+
return { protocolFees: protocolFee, referrerFees: referralFee };
|
|
677
|
+
} else {
|
|
678
|
+
return { protocolFees: new Decimal(0), referrerFees: new Decimal(0) };
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
calculateFlashLoanFees(flashLoanAmountLamports: Decimal, referralFeeBps: number, hasReferrer: boolean): Fees {
|
|
683
|
+
return this.calculateFees(
|
|
684
|
+
flashLoanAmountLamports,
|
|
685
|
+
this.getFlashLoanFee(),
|
|
686
|
+
FeeCalculation.Exclusive,
|
|
687
|
+
referralFeeBps,
|
|
688
|
+
hasReferrer
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
|
|
645
692
|
setBuffer(buffer: AccountInfo<Buffer> | null) {
|
|
646
693
|
this.buffer = buffer;
|
|
647
694
|
}
|
|
@@ -935,9 +982,9 @@ export class KaminoReserve {
|
|
|
935
982
|
currentValue: Decimal;
|
|
936
983
|
}[] = market
|
|
937
984
|
.getMarketElevationGroupDescriptions()
|
|
938
|
-
.filter((x) => x.debtReserve
|
|
985
|
+
.filter((x) => x.debtReserve.equals(this.address))
|
|
939
986
|
.map((elevationGroupDescription: ElevationGroupDescription) =>
|
|
940
|
-
elevationGroupDescription.collateralReserves.map((collateralReserveAddress) => {
|
|
987
|
+
elevationGroupDescription.collateralReserves.toArray().map((collateralReserveAddress) => {
|
|
941
988
|
const collRes = market.reserves.get(new PublicKey(collateralReserveAddress))!;
|
|
942
989
|
|
|
943
990
|
const debtLimitAgainstThisCollInGroup =
|
|
@@ -1015,14 +1062,15 @@ export class KaminoReserve {
|
|
|
1015
1062
|
positiveOrZero(remainingGlobalCap),
|
|
1016
1063
|
positiveOrZero(liquidityGivenUtilizationCap)
|
|
1017
1064
|
);
|
|
1018
|
-
|
|
1019
1065
|
return availableInCrossMode;
|
|
1020
1066
|
} else {
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
.map((x) => x.maxDebt.minus(x.currentValue))
|
|
1067
|
+
let remainingInsideEmodeCaps = new Decimal(0);
|
|
1068
|
+
const capsGivenEgroup = caps.debtAgainstCollateralReserveCaps.filter(
|
|
1069
|
+
(x) => x.elevationGroup === elevationGroup
|
|
1025
1070
|
);
|
|
1071
|
+
if (capsGivenEgroup.length > 0) {
|
|
1072
|
+
remainingInsideEmodeCaps = Decimal.min(...capsGivenEgroup.map((x) => x.maxDebt.minus(x.currentValue)));
|
|
1073
|
+
}
|
|
1026
1074
|
return Decimal.min(
|
|
1027
1075
|
positiveOrZero(liquidityAvailable),
|
|
1028
1076
|
positiveOrZero(remainingInsideEmodeCaps),
|
package/src/classes/shared.ts
CHANGED
|
@@ -55,3 +55,13 @@ export type ReserveFarmInfo = {
|
|
|
55
55
|
fetched: boolean;
|
|
56
56
|
farmStates: FarmState[];
|
|
57
57
|
};
|
|
58
|
+
|
|
59
|
+
export enum FeeCalculation {
|
|
60
|
+
Inclusive = 'Inclusive',
|
|
61
|
+
Exclusive = 'Exclusive',
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type Fees = {
|
|
65
|
+
protocolFees: Decimal;
|
|
66
|
+
referrerFees: Decimal;
|
|
67
|
+
};
|
package/src/classes/vault.ts
CHANGED
|
@@ -185,11 +185,16 @@ export class KaminoVaultClient {
|
|
|
185
185
|
const createAtasIxns: TransactionInstruction[] = [];
|
|
186
186
|
const closeAtasIxns: TransactionInstruction[] = [];
|
|
187
187
|
if (vaultState.tokenMint.equals(WRAPPED_SOL_MINT)) {
|
|
188
|
-
const {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
188
|
+
const { atas: wsolAta, createAtaIxs: createWsolAtaIxns } = await getAtasWithCreateIxnsIfMissing(
|
|
189
|
+
this._connection,
|
|
190
|
+
user,
|
|
191
|
+
[
|
|
192
|
+
{
|
|
193
|
+
mint: WRAPPED_SOL_MINT,
|
|
194
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
195
|
+
},
|
|
196
|
+
]
|
|
197
|
+
);
|
|
193
198
|
createAtasIxns.push(...createWsolAtaIxns);
|
|
194
199
|
const depositWsolixn = getDepositWsolIxns(
|
|
195
200
|
user,
|
|
@@ -197,15 +202,14 @@ export class KaminoVaultClient {
|
|
|
197
202
|
numberToLamportsDecimal(tokenAmount, vaultState.tokenMintDecimals.toNumber()).ceil()
|
|
198
203
|
);
|
|
199
204
|
createAtasIxns.push(...depositWsolixn);
|
|
200
|
-
closeAtasIxns.push(...closeWsolAtaIxns);
|
|
201
205
|
}
|
|
202
206
|
|
|
203
|
-
const { atas,
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
);
|
|
207
|
+
const { atas, createAtaIxs: createSharesAtaIxns } = await getAtasWithCreateIxnsIfMissing(this._connection, user, [
|
|
208
|
+
{
|
|
209
|
+
mint: vaultState.sharesMint,
|
|
210
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
211
|
+
},
|
|
212
|
+
]);
|
|
209
213
|
createAtasIxns.push(...createSharesAtaIxns);
|
|
210
214
|
|
|
211
215
|
const userSharesAta = atas[0];
|
|
@@ -256,12 +260,12 @@ export class KaminoVaultClient {
|
|
|
256
260
|
const vaultState = await vault.getState(this._connection);
|
|
257
261
|
|
|
258
262
|
const userSharesAta = getAssociatedTokenAddress(vaultState.sharesMint, user);
|
|
259
|
-
const { atas,
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
);
|
|
263
|
+
const { atas, createAtaIxs } = await getAtasWithCreateIxnsIfMissing(this._connection, user, [
|
|
264
|
+
{
|
|
265
|
+
mint: vaultState.tokenMint,
|
|
266
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
267
|
+
},
|
|
268
|
+
]);
|
|
265
269
|
const userTokenAta = atas[0];
|
|
266
270
|
|
|
267
271
|
const tokensToWithdraw = shareAmount.div(await this.getTokensPerShareSingleVault(vault, slot));
|
|
@@ -329,7 +333,7 @@ export class KaminoVaultClient {
|
|
|
329
333
|
})
|
|
330
334
|
);
|
|
331
335
|
|
|
332
|
-
return [...
|
|
336
|
+
return [...createAtaIxs, ...withdrawIxns];
|
|
333
337
|
}
|
|
334
338
|
|
|
335
339
|
/**
|
|
@@ -1,7 +1,104 @@
|
|
|
1
1
|
import Decimal from 'decimal.js';
|
|
2
|
-
import { KaminoMarket, KaminoObligation } from '../classes';
|
|
2
|
+
import { KaminoMarket, KaminoObligation, KaminoReserve, numberToLamportsDecimal } from '../classes';
|
|
3
3
|
import { PublicKey } from '@solana/web3.js';
|
|
4
4
|
|
|
5
|
+
export function calcRepayAmountWithSlippage(
|
|
6
|
+
kaminoMarket: KaminoMarket,
|
|
7
|
+
debtReserve: KaminoReserve,
|
|
8
|
+
currentSlot: number,
|
|
9
|
+
obligation: KaminoObligation,
|
|
10
|
+
amount: Decimal,
|
|
11
|
+
referrer: PublicKey
|
|
12
|
+
): {
|
|
13
|
+
repayAmount: Decimal;
|
|
14
|
+
repayAmountLamports: Decimal;
|
|
15
|
+
flashRepayAmountLamports: Decimal;
|
|
16
|
+
} {
|
|
17
|
+
const irSlippageBpsForDebt = obligation
|
|
18
|
+
.estimateObligationInterestRate(
|
|
19
|
+
kaminoMarket,
|
|
20
|
+
debtReserve,
|
|
21
|
+
obligation.state.borrows.find((borrow) => borrow.borrowReserve.equals(debtReserve.address))!,
|
|
22
|
+
currentSlot
|
|
23
|
+
)
|
|
24
|
+
.toDecimalPlaces(debtReserve.state.liquidity.mintDecimals.toNumber(), Decimal.ROUND_CEIL);
|
|
25
|
+
// add 0.1% to irSlippageBpsForDebt because we don't want to estimate slightly less than SC and end up not reapying enough
|
|
26
|
+
const repayAmount = amount
|
|
27
|
+
.mul(irSlippageBpsForDebt.mul(new Decimal('1.001')))
|
|
28
|
+
.toDecimalPlaces(debtReserve.state.liquidity.mintDecimals.toNumber(), Decimal.ROUND_CEIL);
|
|
29
|
+
const repayAmountLamports = numberToLamportsDecimal(repayAmount, debtReserve.stats.decimals);
|
|
30
|
+
|
|
31
|
+
const { flashRepayAmountLamports } = calcFlashRepayAmount({
|
|
32
|
+
reserve: debtReserve,
|
|
33
|
+
referralFeeBps: kaminoMarket.state.referralFeeBps,
|
|
34
|
+
hasReferral: !referrer.equals(PublicKey.default),
|
|
35
|
+
flashBorrowAmountLamports: repayAmountLamports,
|
|
36
|
+
});
|
|
37
|
+
return { repayAmount, repayAmountLamports, flashRepayAmountLamports };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const calcFlashRepayAmount = (props: {
|
|
41
|
+
reserve: KaminoReserve;
|
|
42
|
+
referralFeeBps: number;
|
|
43
|
+
hasReferral: boolean;
|
|
44
|
+
flashBorrowAmountLamports: Decimal;
|
|
45
|
+
}): {
|
|
46
|
+
flashRepayAmountLamports: Decimal;
|
|
47
|
+
} => {
|
|
48
|
+
const { reserve, referralFeeBps, hasReferral, flashBorrowAmountLamports } = props;
|
|
49
|
+
const { referrerFees, protocolFees } = reserve.calculateFlashLoanFees(
|
|
50
|
+
flashBorrowAmountLamports,
|
|
51
|
+
referralFeeBps,
|
|
52
|
+
hasReferral
|
|
53
|
+
);
|
|
54
|
+
const flashRepayAmountLamports = flashBorrowAmountLamports.add(referrerFees).add(protocolFees);
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
flashRepayAmountLamports,
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export function calcMaxWithdrawCollateral(
|
|
62
|
+
kaminoMarket: KaminoMarket,
|
|
63
|
+
collReserveAddr: PublicKey,
|
|
64
|
+
debtReserveAddr: PublicKey,
|
|
65
|
+
obligation: KaminoObligation,
|
|
66
|
+
repayAmountLamports: Decimal
|
|
67
|
+
) {
|
|
68
|
+
const collReserve = kaminoMarket.getReserveByAddress(collReserveAddr)!;
|
|
69
|
+
const debtReserve = kaminoMarket.getReserveByAddress(debtReserveAddr)!;
|
|
70
|
+
|
|
71
|
+
const debtOraclePx = debtReserve.getOracleMarketPrice();
|
|
72
|
+
const collOraclePx = collReserve.getOracleMarketPrice();
|
|
73
|
+
const { maxLtv: collMaxLtv } = obligation.getLtvForReserve(kaminoMarket, collReserve);
|
|
74
|
+
const debtBorrowFactor = debtReserve.getBorrowFactor();
|
|
75
|
+
|
|
76
|
+
const debtPosition = obligation.getBorrowByReserve(debtReserve.address)!;
|
|
77
|
+
const collPosition = obligation.getDepositByReserve(collReserve.address)!;
|
|
78
|
+
const initialCollValue = collPosition.amount.floor().div(collReserve.getMintFactor()).mul(collOraclePx);
|
|
79
|
+
const remainingDebtAmountLamports = debtPosition.amount.sub(repayAmountLamports);
|
|
80
|
+
const remainingDebtBfWeightedValue = remainingDebtAmountLamports.ceil().div(debtReserve.getMintFactor()).mul(debtBorrowFactor).mul(debtOraclePx);
|
|
81
|
+
|
|
82
|
+
let isClosingPosition = false;
|
|
83
|
+
if (remainingDebtAmountLamports.lte(new Decimal(0)) && obligation.getBorrows().length === 1) {
|
|
84
|
+
isClosingPosition = true;
|
|
85
|
+
}
|
|
86
|
+
const numerator = initialCollValue.mul(collMaxLtv).sub(remainingDebtBfWeightedValue);
|
|
87
|
+
const denominator = collOraclePx.mul(collMaxLtv);
|
|
88
|
+
const maxCollWithdrawAmount = numerator.div(denominator);
|
|
89
|
+
const maxCollateralWithdrawalAmountLamports = maxCollWithdrawAmount.mul(collReserve.getMintFactor()).floor();
|
|
90
|
+
|
|
91
|
+
let withdrawableCollLamports: Decimal;
|
|
92
|
+
if (isClosingPosition) {
|
|
93
|
+
// 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
|
|
94
|
+
const maxSwapCollLamportsWithBuffer = maxCollateralWithdrawalAmountLamports.mul('1.1');
|
|
95
|
+
withdrawableCollLamports = Decimal.min(maxSwapCollLamportsWithBuffer, collPosition.amount).floor();
|
|
96
|
+
} else {
|
|
97
|
+
withdrawableCollLamports = Decimal.max(new Decimal(0), maxCollateralWithdrawalAmountLamports);
|
|
98
|
+
}
|
|
99
|
+
return { isClosingPosition, withdrawableCollLamports };
|
|
100
|
+
}
|
|
101
|
+
|
|
5
102
|
export function estimateDebtRepaymentWithColl(props: {
|
|
6
103
|
collAmount: Decimal; // in decimals
|
|
7
104
|
priceDebtToColl: Decimal;
|