@kamino-finance/klend-sdk 5.10.32 → 5.10.34

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.
@@ -70,6 +70,7 @@ import bs58 from 'bs58';
70
70
  import { getAccountOwner, getProgramAccounts } from '../utils/rpc';
71
71
  import {
72
72
  AcceptVaultOwnershipIxs,
73
+ APYs,
73
74
  DepositIxs,
74
75
  InitVaultIxs,
75
76
  ReserveAllocationOverview,
@@ -98,7 +99,7 @@ export const kaminoVaultStagingId = new PublicKey('STkvh7ostar39Fwr4uZKASs1RNNuY
98
99
  const TOKEN_VAULT_SEED = 'token_vault';
99
100
  const CTOKEN_VAULT_SEED = 'ctoken_vault';
100
101
  const BASE_VAULT_AUTHORITY_SEED = 'authority';
101
- const SHARES_SEEDS = 'shares';
102
+ const SHARES_SEED = 'shares';
102
103
 
103
104
  /**
104
105
  * KaminoVaultClient is a class that provides a high-level interface to interact with the Kamino Vault program.
@@ -191,7 +192,7 @@ export class KaminoVaultClient {
191
192
  )[0];
192
193
 
193
194
  const sharesMint = PublicKey.findProgramAddressSync(
194
- [Buffer.from(SHARES_SEEDS), vaultState.publicKey.toBytes()],
195
+ [Buffer.from(SHARES_SEED), vaultState.publicKey.toBytes()],
195
196
  this._kaminoVaultProgramId
196
197
  )[0];
197
198
 
@@ -1005,6 +1006,7 @@ export class KaminoVaultClient {
1005
1006
  */
1006
1007
  async investAllReservesIxs(payer: PublicKey, vault: KaminoVault): Promise<TransactionInstruction[]> {
1007
1008
  const vaultState = await vault.getState(this.getConnection());
1009
+ const minInvestAmount = vaultState.minInvestAmount;
1008
1010
  const allReserves = this.getVaultReserves(vaultState);
1009
1011
  if (allReserves.length === 0) {
1010
1012
  throw new Error('No reserves found for the vault, please select at least one reserve for the vault');
@@ -1024,6 +1026,7 @@ export class KaminoVaultClient {
1024
1026
  const curentVaultAllocations = this.getVaultAllocations(vaultState);
1025
1027
 
1026
1028
  const reservesToDisinvestFrom: PublicKey[] = [];
1029
+ const reservesToInvestInto: PublicKey[] = [];
1027
1030
 
1028
1031
  for (let index = 0; index < allReserves.length; index++) {
1029
1032
  const reservePubkey = allReserves[index];
@@ -1037,8 +1040,15 @@ export class KaminoVaultClient {
1037
1040
  vaultState.tokenMintDecimals.toNumber()
1038
1041
  );
1039
1042
 
1040
- if (computedAllocation.lt(reserveAllocationLiquidityAmount)) {
1041
- reservesToDisinvestFrom.push(reservePubkey);
1043
+ const diffInReserveTokens = computedAllocation.sub(reserveAllocationLiquidityAmount);
1044
+ const diffInReserveLamports = collToLamportsDecimal(diffInReserveTokens, vaultState.tokenMintDecimals.toNumber());
1045
+ // if the diff for the reserve is smaller than the min invest amount, we do not need to invest or disinvest
1046
+ if (diffInReserveLamports.abs().gte(new Decimal(minInvestAmount.toString()))) {
1047
+ if (computedAllocation.lt(reserveAllocationLiquidityAmount)) {
1048
+ reservesToDisinvestFrom.push(reservePubkey);
1049
+ } else {
1050
+ reservesToInvestInto.push(reservePubkey);
1051
+ }
1042
1052
  }
1043
1053
  }
1044
1054
 
@@ -1062,24 +1072,22 @@ export class KaminoVaultClient {
1062
1072
  investIxnsPromises.push(investIxsPromise);
1063
1073
  }
1064
1074
 
1065
- for (const reserve of allReserves) {
1066
- if (!reservesToDisinvestFrom.includes(reserve)) {
1067
- const reserveState = allReservesStateMap.get(reserve);
1068
- if (reserveState === null) {
1069
- throw new Error(`Reserve ${reserve.toBase58()} not found`);
1070
- }
1071
- const investIxsPromise = this.investSingleReserveIxs(
1072
- payer,
1073
- vault,
1074
- {
1075
- address: reserve,
1076
- state: reserveState!.state,
1077
- },
1078
- allReservesStateMap,
1079
- false
1080
- );
1081
- investIxnsPromises.push(investIxsPromise);
1075
+ for (const reserve of reservesToInvestInto) {
1076
+ const reserveState = allReservesStateMap.get(reserve);
1077
+ if (reserveState === null) {
1078
+ throw new Error(`Reserve ${reserve.toBase58()} not found`);
1082
1079
  }
1080
+ const investIxsPromise = this.investSingleReserveIxs(
1081
+ payer,
1082
+ vault,
1083
+ {
1084
+ address: reserve,
1085
+ state: reserveState!.state,
1086
+ },
1087
+ allReservesStateMap,
1088
+ false
1089
+ );
1090
+ investIxnsPromises.push(investIxsPromise);
1083
1091
  }
1084
1092
 
1085
1093
  let investIxns: TransactionInstruction[] = [];
@@ -1500,7 +1508,7 @@ export class KaminoVaultClient {
1500
1508
  const allocation = reservesAllocations.get(reserve);
1501
1509
  return allocation?.targetWeight.isZero();
1502
1510
  });
1503
- if (allReservesHaveWeight0 || allReservesPubkeys.length === 0) {
1511
+ if (allReservesPubkeys.length === 0 || allReservesHaveWeight0) {
1504
1512
  const computedHoldings = new PubkeyHashMap<PublicKey, Decimal>();
1505
1513
  allReservesPubkeys.forEach((reserve) => {
1506
1514
  computedHoldings.set(reserve, new Decimal(0));
@@ -1528,7 +1536,6 @@ export class KaminoVaultClient {
1528
1536
  const reserveWithWeight = initialVaultAllocations.get(reserve);
1529
1537
  const targetAllocation = reserveWithWeight!.targetWeight.mul(totalLeftover).div(currentAllocationSum);
1530
1538
  const reserveCap = reserveWithWeight!.tokenAllocationCap;
1531
- // todo: check if both target and reserveCap
1532
1539
  const amountToInvest = Decimal.min(targetAllocation, reserveCap, totalLeftToInvest);
1533
1540
  totalLeftToInvest = totalLeftToInvest.sub(amountToInvest);
1534
1541
  if (amountToInvest.eq(reserveCap)) {
@@ -2224,7 +2231,7 @@ export class KaminoVaultClient {
2224
2231
  // all the async part of the functions above just read the vaultReservesState which is read beforehand, so excepting vaultCollateralsPromise they should do no additional network calls
2225
2232
  const [
2226
2233
  vaultHoldingsWithUSDValue,
2227
- vaultTheoreticalAPY,
2234
+ vaultTheoreticalAPYs,
2228
2235
  totalInvestedAndBorrowed,
2229
2236
  vaultCollaterals,
2230
2237
  reservesOverview,
@@ -2240,7 +2247,7 @@ export class KaminoVaultClient {
2240
2247
  holdingsUSD: vaultHoldingsWithUSDValue,
2241
2248
  reservesOverview: reservesOverview,
2242
2249
  vaultCollaterals: vaultCollaterals,
2243
- theoreticalSupplyAPY: vaultTheoreticalAPY,
2250
+ theoreticalSupplyAPY: vaultTheoreticalAPYs,
2244
2251
  totalBorrowed: totalInvestedAndBorrowed.totalBorrowed,
2245
2252
  utilizationRatio: totalInvestedAndBorrowed.utilizationRatio,
2246
2253
  totalSupplied: totalInvestedAndBorrowed.totalInvested,
@@ -2351,13 +2358,13 @@ export class KaminoVaultClient {
2351
2358
  * @param vault - the kamino vault to get APY for
2352
2359
  * @param slot - current slot
2353
2360
  * @param [vaultReservesMap] - hashmap from each reserve pubkey to the reserve state. Optional. If provided the function will be significantly faster as it will not have to fetch the reserves
2354
- * @returns APY for the vault
2361
+ * @returns a struct containing estimated gross APY and net APY (gross - vault fees) for the vault
2355
2362
  */
2356
2363
  async getVaultTheoreticalAPY(
2357
2364
  vault: VaultState,
2358
2365
  slot: number,
2359
2366
  vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve>
2360
- ): Promise<Decimal> {
2367
+ ): Promise<APYs> {
2361
2368
  const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vault);
2362
2369
 
2363
2370
  let totalWeights = new Decimal(0);
@@ -2379,14 +2386,20 @@ export class KaminoVaultClient {
2379
2386
  totalWeights = totalWeights.add(weight);
2380
2387
  });
2381
2388
  if (totalWeights.isZero()) {
2382
- return new Decimal(0);
2389
+ return {
2390
+ grossAPY: new Decimal(0),
2391
+ netAPY: new Decimal(0),
2392
+ };
2383
2393
  }
2384
2394
 
2385
2395
  const grossAPY = totalAPY.div(totalWeights);
2386
2396
  const netAPY = grossAPY
2387
2397
  .mul(new Decimal(1).sub(new Decimal(vault.performanceFeeBps.toString()).div(FullBPSDecimal)))
2388
2398
  .mul(new Decimal(1).sub(new Decimal(vault.managementFeeBps.toString()).div(FullBPSDecimal)));
2389
- return netAPY;
2399
+ return {
2400
+ grossAPY,
2401
+ netAPY,
2402
+ };
2390
2403
  }
2391
2404
 
2392
2405
  /**
@@ -2655,7 +2668,7 @@ export type VaultOverview = {
2655
2668
  holdingsUSD: VaultHoldingsWithUSDValue;
2656
2669
  reservesOverview: PubkeyHashMap<PublicKey, ReserveOverview>;
2657
2670
  vaultCollaterals: PubkeyHashMap<PublicKey, MarketOverview>;
2658
- theoreticalSupplyAPY: Decimal;
2671
+ theoreticalSupplyAPY: APYs;
2659
2672
  totalBorrowed: Decimal;
2660
2673
  totalSupplied: Decimal;
2661
2674
  utilizationRatio: Decimal;
@@ -260,7 +260,7 @@ async function main() {
260
260
  2500,
261
261
  [vaultKp]
262
262
  );
263
- await sleep(5000);
263
+ await sleep(2000);
264
264
  const _populateLUTSig = await processTxn(env.client, env.payer, instructions.populateLUTIxs, mode, 2500, []);
265
265
 
266
266
  mode === 'execute' && console.log('Vault created:', vaultKp.publicKey.toBase58());
@@ -426,7 +426,7 @@ async function main() {
426
426
  // if we need to create the LUT we have to do that in a separate tx and wait a little bit after
427
427
  if (syncLUTIxs.setupLUTIfNeededIxs.length > 0) {
428
428
  const setupLUTSig = await processTxn(env.client, env.payer, syncLUTIxs.setupLUTIfNeededIxs, mode, 2500, []);
429
- await sleep(5000);
429
+ await sleep(2000);
430
430
  mode === 'execute' && console.log('LUT created and set to the vault:', setupLUTSig);
431
431
  }
432
432
  // if there are accounts to be added to the LUT we have to do that in a separate tx
@@ -38,7 +38,7 @@ export async function buildAndSendTxnWithLogs(
38
38
  });
39
39
  console.log('Transaction Hash:', withDescription, sig);
40
40
  if (withLogsIfSuccess) {
41
- await sleep(5000);
41
+ await sleep(1000);
42
42
  const res = await c.getTransaction(sig, {
43
43
  commitment: 'confirmed',
44
44
  maxSupportedTransactionVersion: 6,
@@ -49,7 +49,6 @@ export async function buildAndSendTxnWithLogs(
49
49
  } catch (e: any) {
50
50
  console.log(e);
51
51
  process.stdout.write(e.logs.toString());
52
- await sleep(5000);
53
52
  const sig = e.toString().split(' failed ')[0].split('Transaction ')[1];
54
53
  const res: VersionedTransactionResponse | null = await c.getTransaction(sig, {
55
54
  commitment: 'confirmed',
@@ -115,7 +114,6 @@ export async function simulateTxn(c: Connection, tx: Transaction, owner: Keypair
115
114
  } catch (e: any) {
116
115
  console.log(e);
117
116
  process.stdout.write(e.logs.toString());
118
- await sleep(5000);
119
117
  const sig = e.toString().split(' failed ')[0].split('Transaction ')[1];
120
118
  const res: TransactionResponse | null = await c.getTransaction(sig, {
121
119
  commitment: 'confirmed',