@kamino-finance/klend-sdk 5.1.3 → 5.1.5

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.
@@ -2,6 +2,7 @@ import { BN } from '@coral-xyz/anchor';
2
2
  import {
3
3
  AccountMeta,
4
4
  Connection,
5
+ GetProgramAccountsResponse,
5
6
  Keypair,
6
7
  PublicKey,
7
8
  SystemProgram,
@@ -414,12 +415,12 @@ export class KaminoVaultClient {
414
415
  ]
415
416
  );
416
417
  createAtasIxns.push(...createWsolAtaIxns);
417
- const depositWsolixn = getDepositWsolIxns(
418
+ const depositWsolIxn = getDepositWsolIxns(
418
419
  user,
419
420
  wsolAta[0],
420
421
  numberToLamportsDecimal(tokenAmount, vaultState.tokenMintDecimals.toNumber()).ceil()
421
422
  );
422
- createAtasIxns.push(...depositWsolixn);
423
+ createAtasIxns.push(...depositWsolIxn);
423
424
  }
424
425
 
425
426
  const { atas, createAtaIxs: createSharesAtaIxns } = await getAtasWithCreateIxnsIfMissing(this._connection, user, [
@@ -610,7 +611,6 @@ export class KaminoVaultClient {
610
611
  const cTokenVault = getCTokenVaultPda(vault.address, reserve.address, this._kaminoVaultProgramId);
611
612
  const lendingMarketAuth = lendingMarketAuthPda(reserve.state.lendingMarket, this._kaminoLendProgramId)[0];
612
613
 
613
- // todo: create ata if needed here
614
614
  const payerTokenAta = getAssociatedTokenAddress(vaultState.tokenMint, payer);
615
615
 
616
616
  const investAccounts: InvestAccounts = {
@@ -875,17 +875,19 @@ export class KaminoVaultClient {
875
875
 
876
876
  /**
877
877
  * This method calculates the token per share value. This will always change based on interest earned from the vault, but calculating it requires a bunch of rpc requests. Caching this for a short duration would be optimal
878
+ * @param vaultsOverride - a list of vaults to get the tokens per share for; if provided with state it will not fetch the state again
879
+ * @param vaultReservesMap - optional parameter; a hashmap from pubkey to reserve state. If provided the function will be significantly faster as it will not have to fetch the reserves
878
880
  * @param slot - current slot, used to estimate the interest earned in the different reserves with allocation from the vault
879
- * @param vaultsOverride
880
- * @param vaultReservesMap
881
+ * @param useOptimisedRPCCall - if set to true, it will use the optimized getProgramAccounts RPC call, which is more efficient but doesn't work in web environments
881
882
  * @returns - token per share value
882
883
  */
883
884
  async getTokensPerShareAllVaults(
884
885
  slot: number,
885
886
  vaultsOverride?: Array<KaminoVault>,
886
- vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve>
887
+ vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve>,
888
+ useOptimisedRPCCall: boolean = true
887
889
  ): Promise<PubkeyHashMap<PublicKey, Decimal>> {
888
- const vaults = vaultsOverride ? vaultsOverride : await this.getAllVaults();
890
+ const vaults = vaultsOverride ? vaultsOverride : await this.getAllVaults(useOptimisedRPCCall);
889
891
  const vaultTokensPerShare = new PubkeyHashMap<PublicKey, Decimal>();
890
892
  for (const vault of vaults) {
891
893
  const tokensPerShare = await this.getTokensPerShareSingleVault(vault, slot, vaultReservesMap);
@@ -897,9 +899,10 @@ export class KaminoVaultClient {
897
899
 
898
900
  /**
899
901
  * Get all vaults
902
+ * @param useOptimisedRPCCall - if set to true, it will use the optimized getProgramAccounts RPC call, which is more efficient but doesn't work in web environments
900
903
  * @returns an array of all vaults
901
904
  */
902
- async getAllVaults(): Promise<KaminoVault[]> {
905
+ async getAllVaults(useOptimisedRPCCall: boolean = true): Promise<KaminoVault[]> {
903
906
  const filters = [
904
907
  {
905
908
  dataSize: VaultState.layout.span + 8,
@@ -912,10 +915,16 @@ export class KaminoVaultClient {
912
915
  },
913
916
  ];
914
917
 
915
- const kaminoVaults = await getProgramAccounts(this._connection, this._kaminoVaultProgramId, {
916
- commitment: this._connection.commitment ?? 'processed',
917
- filters,
918
- });
918
+ let kaminoVaults: GetProgramAccountsResponse = [];
919
+
920
+ if (useOptimisedRPCCall) {
921
+ kaminoVaults = await getProgramAccounts(this._connection, this._kaminoVaultProgramId, {
922
+ commitment: this._connection.commitment ?? 'processed',
923
+ filters,
924
+ });
925
+ } else {
926
+ kaminoVaults = await this._connection.getProgramAccounts(this._kaminoVaultProgramId, { filters });
927
+ }
919
928
 
920
929
  return kaminoVaults.map((kaminoVault) => {
921
930
  if (kaminoVault.account === null) {
@@ -45,13 +45,13 @@ import Decimal from 'decimal.js';
45
45
  import { BN } from '@coral-xyz/anchor';
46
46
  import { PythConfiguration, SwitchboardConfiguration } from './idl_codegen_kamino_vault/types';
47
47
  import * as fs from 'fs';
48
- import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
49
48
  import { MarketWithAddress } from './utils/managerTypes';
50
49
  import {
51
50
  ManagementFeeBps,
52
51
  PendingVaultAdmin,
53
52
  PerformanceFeeBps,
54
53
  } from './idl_codegen_kamino_vault/types/VaultConfigField';
54
+ import { getAccountOwner } from './utils/rpc';
55
55
 
56
56
  dotenv.config({
57
57
  path: `.env${process.env.ENV ? '.' + process.env.ENV : ''}`,
@@ -237,10 +237,11 @@ async function main() {
237
237
  const multisigPk = multisig ? new PublicKey(multisig) : PublicKey.default;
238
238
  const kaminoManager = new KaminoManager(env.connection, env.kLendProgramId, env.kVaultProgramId);
239
239
 
240
+ const tokenProgramID = await getAccountOwner(env.connection, tokenMint);
240
241
  const kaminoVaultConfig = new KaminoVaultConfig({
241
242
  admin: mode === 'multisig' ? multisigPk : env.payer.publicKey,
242
243
  tokenMint: tokenMint,
243
- tokenMintProgramId: TOKEN_PROGRAM_ID,
244
+ tokenMintProgramId: tokenProgramID,
244
245
  performanceFeeRate: new Decimal(0.0),
245
246
  managementFeeRate: new Decimal(0.0),
246
247
  });
@@ -470,6 +471,67 @@ async function main() {
470
471
  mode === 'execute' && console.log('Vault allocation updated:', updateVaultAllocationSig);
471
472
  });
472
473
 
474
+ commands
475
+ .command('deposit')
476
+ .requiredOption('--vault <string>', 'Vault address')
477
+ .requiredOption('--amount <number>', 'Token amount to deposit, in decimals')
478
+ .requiredOption(
479
+ `--mode <string>`,
480
+ 'simulate - to print txn simulation, inspect - to get txn simulation in explorer, execute - execute txn, multisig - to get bs58 txn for multisig usage'
481
+ )
482
+ .option(`--staging`, 'If true, will use the staging programs')
483
+ .option(`--multisig <string>`, 'If using multisig mode this is required, otherwise will be ignored')
484
+ .action(async ({ vault, amount, mode, staging, multisig }) => {
485
+ const env = initializeClient(mode === 'multisig', staging);
486
+ const vaultAddress = new PublicKey(vault);
487
+
488
+ if (mode === 'multisig' && !multisig) {
489
+ throw new Error('If using multisig mode, multisig is required');
490
+ }
491
+
492
+ const kaminoManager = new KaminoManager(env.connection, env.kLendProgramId, env.kVaultProgramId);
493
+
494
+ const kaminoVault = new KaminoVault(vaultAddress, undefined, env.kVaultProgramId);
495
+ const instructions = await kaminoManager.depositToVaultIxs(env.payer.publicKey, kaminoVault, amount);
496
+
497
+ const depositSig = await processTxn(env.client, env.payer, instructions, mode, 2500, []);
498
+
499
+ mode === 'execute' && console.log('User deposit:', depositSig);
500
+ });
501
+
502
+ commands
503
+ .command('withdraw')
504
+ .requiredOption('--vault <string>', 'Vault address')
505
+ .requiredOption('--amount <number>', 'Shares amount to withdraw')
506
+ .requiredOption(
507
+ `--mode <string>`,
508
+ 'simulate - to print txn simulation, inspect - to get txn simulation in explorer, execute - execute txn, multisig - to get bs58 txn for multisig usage'
509
+ )
510
+ .option(`--staging`, 'If true, will use the staging programs')
511
+ .option(`--multisig <string>`, 'If using multisig mode this is required, otherwise will be ignored')
512
+ .action(async ({ vault, amount, mode, staging, multisig }) => {
513
+ const env = initializeClient(mode === 'multisig', staging);
514
+ const vaultAddress = new PublicKey(vault);
515
+
516
+ if (mode === 'multisig' && !multisig) {
517
+ throw new Error('If using multisig mode, multisig is required');
518
+ }
519
+
520
+ const kaminoManager = new KaminoManager(env.connection, env.kLendProgramId, env.kVaultProgramId);
521
+
522
+ const kaminoVault = new KaminoVault(vaultAddress, undefined, env.kVaultProgramId);
523
+ const instructions = await kaminoManager.withdrawFromVaultIxs(
524
+ env.payer.publicKey,
525
+ kaminoVault,
526
+ new Decimal(amount),
527
+ await env.connection.getSlot('confirmed')
528
+ );
529
+
530
+ const depositSig = await processTxn(env.client, env.payer, instructions, mode, 2500, []);
531
+
532
+ mode === 'execute' && console.log('User withdraw:', depositSig);
533
+ });
534
+
473
535
  // commands
474
536
  // .command('close-vault')
475
537
  // .requiredOption('--vault <string>', 'Vault address')
@@ -500,7 +562,7 @@ async function main() {
500
562
  const env = initializeClient(false, false);
501
563
  const kaminoManager = new KaminoManager(env.connection, env.kLendProgramId, env.kVaultProgramId);
502
564
 
503
- const allVaults = await kaminoManager.getAllVaults();
565
+ const allVaults = await kaminoManager.getAllVaults(true);
504
566
  console.log('all vaults', allVaults);
505
567
  });
506
568
 
package/src/utils/rpc.ts CHANGED
@@ -72,6 +72,14 @@ export async function getProgramAccounts(
72
72
  return x as GetProgramAccountsResponse;
73
73
  }
74
74
 
75
+ export async function getAccountOwner(connection: Connection, address: PublicKey): Promise<PublicKey> {
76
+ const acc = await connection.getAccountInfo(address);
77
+ if (acc == null) {
78
+ throw Error(`Could not fetch mint ${address.toString()}`);
79
+ }
80
+ return acc.owner;
81
+ }
82
+
75
83
  async function deserializeAccountInfo(accountInfo: AccountInfo<string[]>): Promise<AccountInfo<Buffer>> {
76
84
  const data = decompress(Buffer.from(accountInfo.data[0], 'base64'));
77
85
  return {