@kamino-finance/klend-sdk 7.2.1 → 7.2.3

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.
@@ -697,13 +697,17 @@ export class KaminoVaultClient {
697
697
  * @param mode the field to update (based on VaultConfigFieldKind enum)
698
698
  * @param value the value to update the field with
699
699
  * @param [vaultAdminAuthority] the signer of the transaction. Optional. If not provided the admin of the vault will be used. It should be used when changing the admin of the vault if we want to build or batch multiple ixs in the same tx
700
+ * @param [lutIxsSigner] the signer of the transaction to be used for the lookup table instructions. Optional. If not provided the admin of the vault will be used. It should be used when changing the admin of the vault if we want to build or batch multiple ixs in the same tx
701
+ * @param [skipLutUpdate] if true, the lookup table instructions will not be included in the returned instructions
700
702
  * @returns a struct that contains the instruction to update the field and an optional list of instructions to update the lookup table
701
703
  */
702
704
  async updateVaultConfigIxs(
703
705
  vault: KaminoVault,
704
706
  mode: VaultConfigFieldKind,
705
707
  value: string,
706
- vaultAdminAuthority?: TransactionSigner
708
+ vaultAdminAuthority?: TransactionSigner,
709
+ lutIxsSigner?: TransactionSigner,
710
+ skipLutUpdate: boolean = false
707
711
  ): Promise<UpdateVaultConfigIxs> {
708
712
  const vaultState: VaultState = await vault.getState();
709
713
  const admin = parseVaultAdmin(vaultState, vaultAdminAuthority);
@@ -753,36 +757,41 @@ export class KaminoVaultClient {
753
757
 
754
758
  const updateLUTIxs: Instruction[] = [];
755
759
 
756
- if (mode.kind === new VaultConfigField.PendingVaultAdmin().kind) {
757
- const newPubkey = address(value);
758
- const insertIntoLutIxs = await insertIntoLookupTableIxs(
759
- this.getConnection(),
760
- admin,
761
- vaultState.vaultLookupTable,
762
- [newPubkey]
763
- );
764
- updateLUTIxs.push(...insertIntoLutIxs);
765
- } else if (mode.kind === new VaultConfigField.Farm().kind) {
766
- const keysToAddToLUT = [address(value)];
767
- // if the farm already exist we want to read its state to add it to the LUT
768
- try {
769
- const farmState = await FarmState.fetch(this.getConnection(), keysToAddToLUT[0]);
770
- keysToAddToLUT.push(
771
- farmState!.farmVault,
772
- farmState!.farmVaultsAuthority,
773
- farmState!.token.mint,
774
- farmState!.scopePrices,
775
- farmState!.globalConfig
776
- );
760
+ if (!skipLutUpdate) {
761
+ const lutIxsSignerAccount = lutIxsSigner ? lutIxsSigner : admin;
762
+
763
+ if (mode.kind === new VaultConfigField.PendingVaultAdmin().kind) {
764
+ const newPubkey = address(value);
765
+
777
766
  const insertIntoLutIxs = await insertIntoLookupTableIxs(
778
767
  this.getConnection(),
779
- admin,
768
+ lutIxsSignerAccount,
780
769
  vaultState.vaultLookupTable,
781
- keysToAddToLUT
770
+ [newPubkey]
782
771
  );
783
772
  updateLUTIxs.push(...insertIntoLutIxs);
784
- } catch (error) {
785
- console.log(`Error fetching farm ${keysToAddToLUT[0].toString()} state`, error);
773
+ } else if (mode.kind === new VaultConfigField.Farm().kind) {
774
+ const keysToAddToLUT = [address(value)];
775
+ // if the farm already exist we want to read its state to add it to the LUT
776
+ try {
777
+ const farmState = await FarmState.fetch(this.getConnection(), keysToAddToLUT[0]);
778
+ keysToAddToLUT.push(
779
+ farmState!.farmVault,
780
+ farmState!.farmVaultsAuthority,
781
+ farmState!.token.mint,
782
+ farmState!.scopePrices,
783
+ farmState!.globalConfig
784
+ );
785
+ const insertIntoLutIxs = await insertIntoLookupTableIxs(
786
+ this.getConnection(),
787
+ lutIxsSignerAccount,
788
+ vaultState.vaultLookupTable,
789
+ keysToAddToLUT
790
+ );
791
+ updateLUTIxs.push(...insertIntoLutIxs);
792
+ } catch (error) {
793
+ console.log(`Error fetching farm ${keysToAddToLUT[0].toString()} state`, error);
794
+ }
786
795
  }
787
796
  }
788
797
 
@@ -799,19 +808,30 @@ export class KaminoVaultClient {
799
808
  * @param farm - the farm where the vault shares can be staked
800
809
  * @param [errorOnOverride] - if true, the function will throw an error if the vault already has a farm. If false, it will override the farm
801
810
  * @param [vaultAdminAuthority] - vault admin - a noop vaultAdminAuthority is provided when absent for multisigs
802
- *
811
+ * @param [lutIxsSigner] - the signer of the transaction to be used for the lookup table instructions. Optional. If not provided the admin of the vault will be used. It should be used when changing the admin of the vault if we want to build or batch multiple ixs in the same tx
812
+ * @param [skipLutUpdate] - if true, the lookup table instructions will not be included in the returned instructions
813
+ * @returns - a struct that contains the instruction to update the farm and an optional list of instructions to update the lookup table
803
814
  */
804
815
  async setVaultFarmIxs(
805
816
  vault: KaminoVault,
806
817
  farm: Address,
807
818
  errorOnOverride: boolean = true,
808
- vaultAdminAuthority?: TransactionSigner
819
+ vaultAdminAuthority?: TransactionSigner,
820
+ lutIxsSigner?: TransactionSigner,
821
+ skipLutUpdate: boolean = false
809
822
  ): Promise<UpdateVaultConfigIxs> {
810
823
  const vaultHasFarm = await vault.hasFarm();
811
824
  if (vaultHasFarm && errorOnOverride) {
812
825
  throw new Error('Vault already has a farm, if you want to override it set errorOnOverride to false');
813
826
  }
814
- return this.updateVaultConfigIxs(vault, new VaultConfigField.Farm(), farm, vaultAdminAuthority);
827
+ return this.updateVaultConfigIxs(
828
+ vault,
829
+ new VaultConfigField.Farm(),
830
+ farm,
831
+ vaultAdminAuthority,
832
+ lutIxsSigner,
833
+ skipLutUpdate
834
+ );
815
835
  }
816
836
 
817
837
  /**
@@ -2848,6 +2868,55 @@ export class KaminoVaultClient {
2848
2868
  };
2849
2869
  }
2850
2870
 
2871
+ /**
2872
+ * This will return a map of the cumulative rewards issued for all the delegated farms
2873
+ * @param [vaults] - the vaults to get the cumulative rewards for; if not provided, the function will get the cumulative rewards for all the vaults
2874
+ * @returns a map of the cumulative rewards issued for all the delegated farms, per token, in lamports
2875
+ */
2876
+ async getCumulativeDelegatedFarmsRewardsIssuedForAllVaults(vaults?: Address[]): Promise<Map<Address, Decimal>> {
2877
+ const vaultsWithDelegatedFarms = await this.getVaultsWithDelegatedFarm();
2878
+ const delegatedFarmsAddresses: Address[] = [];
2879
+ if (vaults) {
2880
+ vaults.forEach((vault) => {
2881
+ const delegatedFarm = vaultsWithDelegatedFarms.get(vault);
2882
+ if (delegatedFarm) {
2883
+ delegatedFarmsAddresses.push(delegatedFarm);
2884
+ }
2885
+ });
2886
+ } else {
2887
+ delegatedFarmsAddresses.push(...Array.from(vaultsWithDelegatedFarms.values()));
2888
+ }
2889
+
2890
+ const farmsSDK = new Farms(this.getConnection());
2891
+ const delegatedFarmsStates = await farmsSDK.fetchMultipleFarmStatesWithCheckedSize(delegatedFarmsAddresses);
2892
+
2893
+ const cumulativeRewardsPerToken = new Map<Address, Decimal>();
2894
+ for (const delegatedFarmState of delegatedFarmsStates) {
2895
+ if (!delegatedFarmState) {
2896
+ continue;
2897
+ }
2898
+
2899
+ delegatedFarmState.rewardInfos.forEach((rewardInfo) => {
2900
+ if (rewardInfo.token.mint === DEFAULT_PUBLIC_KEY) {
2901
+ return;
2902
+ }
2903
+ const rewardTokenMint = rewardInfo.token.mint;
2904
+ if (cumulativeRewardsPerToken.has(rewardTokenMint)) {
2905
+ cumulativeRewardsPerToken.set(
2906
+ rewardTokenMint,
2907
+ cumulativeRewardsPerToken
2908
+ .get(rewardTokenMint)!
2909
+ .add(new Decimal(rewardInfo.rewardsIssuedCumulative.toString()))
2910
+ );
2911
+ } else {
2912
+ cumulativeRewardsPerToken.set(rewardTokenMint, new Decimal(rewardInfo.rewardsIssuedCumulative.toString()));
2913
+ }
2914
+ });
2915
+ }
2916
+
2917
+ return cumulativeRewardsPerToken;
2918
+ }
2919
+
2851
2920
  /**
2852
2921
  * This will return an overview of each reserve that is part of the vault allocation
2853
2922
  * @param vault - the kamino vault to get available liquidity to withdraw for
@@ -3438,6 +3507,36 @@ export class KaminoVaultClient {
3438
3507
  return address(delegatedFarmWithVault.farm);
3439
3508
  }
3440
3509
 
3510
+ /**
3511
+ * gets all the delegated farms addresses
3512
+ * @returns a list of delegated farms addresses
3513
+ */
3514
+ async getAllDelegatedFarms(): Promise<Address[]> {
3515
+ const vaultsWithDelegatedFarm = await this.getVaultsWithDelegatedFarm();
3516
+ return Array.from(vaultsWithDelegatedFarm.values());
3517
+ }
3518
+
3519
+ /**
3520
+ * This will return a map of the vault address and the delegated farm address for that vault
3521
+ * @returns a map of the vault address and the delegated farm address for that vault
3522
+ */
3523
+ async getVaultsWithDelegatedFarm(): Promise<Map<Address, Address>> {
3524
+ const response = await fetch(`${CDN_ENDPOINT}/resources.json`);
3525
+ if (!response.ok) {
3526
+ console.log(`Failed to fetch CDN for get vaults with delegated farm`);
3527
+ return new Map<Address, Address>();
3528
+ }
3529
+ const data = (await response.json()) as { 'mainnet-beta'?: { delegatedVaultFarms: any } };
3530
+ const delegatedVaultFarms = data['mainnet-beta']?.delegatedVaultFarms;
3531
+ if (!delegatedVaultFarms) {
3532
+ return new Map<Address, Address>();
3533
+ }
3534
+
3535
+ return new Map(
3536
+ delegatedVaultFarms.map((delegatedFarm: any) => [address(delegatedFarm.vault), address(delegatedFarm.farm)])
3537
+ );
3538
+ }
3539
+
3441
3540
  /// reads the pending rewards for a user in the reserves farms of a vault
3442
3541
  /// @param user - the user address
3443
3542
  /// @param vault - the vault
@@ -48,7 +48,7 @@ import { initEnv, ManagerEnv } from './tx/ManagerEnv';
48
48
  import { processTx } from './tx/processor';
49
49
  import { getPriorityFeeAndCuIxs } from '../client/tx/priorityFee';
50
50
  import { fetchAddressLookupTable, fetchAllAddressLookupTable } from '@solana-program/address-lookup-table';
51
- import { noopSigner } from '../utils/signer';
51
+ import { noopSigner, parseKeypairFile } from '../utils/signer';
52
52
 
53
53
  dotenv.config({
54
54
  path: `.env${process.env.ENV ? '.' + process.env.ENV : ''}`,
@@ -455,7 +455,9 @@ async function main() {
455
455
  kaminoVault,
456
456
  new PendingVaultAdmin(),
457
457
  newAdmin,
458
- signer
458
+ signer,
459
+ undefined,
460
+ true
459
461
  );
460
462
 
461
463
  await processTx(
@@ -485,7 +487,12 @@ async function main() {
485
487
  'simulate|multisig|execute - simulate - to print txn simulation and to get tx simulation link in explorer, execute - execute tx, multisig - to get bs58 tx for multisig usage'
486
488
  )
487
489
  .option(`--staging`, 'If true, will use the staging programs')
488
- .action(async ({ vault, field, value, mode, staging }) => {
490
+ .option(`--skip-lut-update`, 'If set, it will skip the LUT update')
491
+ .option(
492
+ `--lutSigner <string>`,
493
+ 'If set, it will use the provided signer instead of the default one for the LUT update'
494
+ )
495
+ .action(async ({ vault, field, value, mode, staging, skipLutUpdate, lutSigner }) => {
489
496
  const env = await initEnv(staging);
490
497
  const vaultAddress = address(vault);
491
498
 
@@ -500,7 +507,20 @@ async function main() {
500
507
  const vaultState = await kaminoVault.getState();
501
508
  const signer = await env.getSigner({ vaultState });
502
509
 
503
- const instructions = await kaminoManager.updateVaultConfigIxs(kaminoVault, field, value, signer);
510
+ let lutSignerOrUndefined = undefined;
511
+ if (lutSigner) {
512
+ lutSignerOrUndefined = await parseKeypairFile(lutSigner as string);
513
+ }
514
+
515
+ const shouldSkipLutUpdate = !!skipLutUpdate;
516
+ const instructions = await kaminoManager.updateVaultConfigIxs(
517
+ kaminoVault,
518
+ field,
519
+ value,
520
+ signer,
521
+ lutSignerOrUndefined,
522
+ shouldSkipLutUpdate
523
+ );
504
524
 
505
525
  await processTx(
506
526
  env.c,
@@ -576,10 +596,15 @@ async function main() {
576
596
  )
577
597
  .option(`--staging`, 'If true, will use the staging programs')
578
598
  .option(`--multisig <string>`, 'If using multisig mode this is required, otherwise will be ignored')
579
- .action(async ({ lut, addresses, mode, staging, multisig }) => {
599
+ .option(`--signer <string>`, 'If set, it will use the provided signer instead of the default one')
600
+ .action(async ({ lut, addresses, mode, staging, multisig, signer }) => {
580
601
  const env = await initEnv(multisig, staging);
581
602
  const lutAddress = address(lut);
582
-
603
+ let txSigner = await env.getSigner();
604
+ // if the signer is provided (path to a keypair) we use it, otherwise we use the default one
605
+ if (signer) {
606
+ txSigner = await parseKeypairFile(signer as string);
607
+ }
583
608
  const addressesArr = addresses.split(' ').map((a: string) => address(a));
584
609
 
585
610
  if (mode === 'multisig' && !multisig) {
@@ -593,12 +618,11 @@ async function main() {
593
618
  env.kvaultProgramId
594
619
  );
595
620
 
596
- const signer = await env.getSigner();
597
- const instructions = await kaminoManager.insertIntoLutIxs(signer, lutAddress, addressesArr);
621
+ const instructions = await kaminoManager.insertIntoLutIxs(txSigner, lutAddress, addressesArr);
598
622
 
599
623
  await processTx(
600
624
  env.c,
601
- signer,
625
+ txSigner,
602
626
  [
603
627
  ...instructions,
604
628
  ...getPriorityFeeAndCuIxs({
@@ -640,7 +664,8 @@ async function main() {
640
664
  'simulate|multisig|execute - simulate - to print txn simulation and to get tx simulation link in explorer, execute - execute tx, multisig - to get bs58 tx for multisig usage'
641
665
  )
642
666
  .option(`--staging`, 'If true, will use the staging programs')
643
- .action(async ({ vault, mode, staging }) => {
667
+ .option(`--signer <string>`, 'If set, it will use the provided signer instead of the default one')
668
+ .action(async ({ vault, mode, staging, signer }) => {
644
669
  const env = await initEnv(staging);
645
670
  const vaultAddress = address(vault);
646
671
 
@@ -653,14 +678,18 @@ async function main() {
653
678
 
654
679
  const kaminoVault = new KaminoVault(env.c.rpc, vaultAddress, undefined, env.kvaultProgramId);
655
680
  const vaultState = await kaminoVault.getState();
656
- const signer = await env.getSigner({ vaultState });
657
- const syncLUTIxs = await kaminoManager.syncVaultLUTIxs(signer, kaminoVault);
681
+ let txSigner = await env.getSigner({ vaultState });
682
+ // if the signer is provided (path to a keypair) we use it, otherwise we use the default one
683
+ if (signer) {
684
+ txSigner = await parseKeypairFile(signer as string);
685
+ }
686
+ const syncLUTIxs = await kaminoManager.syncVaultLUTIxs(txSigner, kaminoVault);
658
687
 
659
688
  // if we need to create the LUT we have to do that in a separate tx and wait a little bit after
660
689
  if (syncLUTIxs.setupLUTIfNeededIxs.length > 0) {
661
690
  await processTx(
662
691
  env.c,
663
- signer,
692
+ txSigner,
664
693
  [
665
694
  ...syncLUTIxs.setupLUTIfNeededIxs,
666
695
  ...getPriorityFeeAndCuIxs({
@@ -677,7 +706,7 @@ async function main() {
677
706
  for (const ix of syncLUTIxs.syncLUTIxs) {
678
707
  await processTx(
679
708
  env.c,
680
- signer,
709
+ txSigner,
681
710
  [
682
711
  ix,
683
712
  ...getPriorityFeeAndCuIxs({
@@ -1353,6 +1382,7 @@ async function main() {
1353
1382
  console.log('reserve ', reserveAddress);
1354
1383
  console.log('reserve incentive', incentive);
1355
1384
  });
1385
+ console.log('totalIncentivesAPY', vaultOverview.reservesFarmsIncentives.totalIncentivesAPY.toString());
1356
1386
  });
1357
1387
 
1358
1388
  commands
@@ -1527,6 +1557,35 @@ async function main() {
1527
1557
  }
1528
1558
  });
1529
1559
 
1560
+ commands.command('get-cumulative-delegated-farms-rewards').action(async () => {
1561
+ const env = await initEnv();
1562
+ const kaminoManager = new KaminoManager(
1563
+ env.c.rpc,
1564
+ DEFAULT_RECENT_SLOT_DURATION_MS,
1565
+ env.klendProgramId,
1566
+ env.kvaultProgramId
1567
+ );
1568
+ const cumulativeRewards = await kaminoManager.getCumulativeDelegatedFarmsRewardsIssuedForAllVaults();
1569
+ cumulativeRewards.forEach((reward, tokenMint) => {
1570
+ console.log(`token mint ${tokenMint} rewards issued (lamports) ${reward}`);
1571
+ });
1572
+ });
1573
+
1574
+ commands.command('get-vaults-with-delegated-farm').action(async () => {
1575
+ const env = await initEnv();
1576
+ const kaminoManager = new KaminoManager(
1577
+ env.c.rpc,
1578
+ DEFAULT_RECENT_SLOT_DURATION_MS,
1579
+ env.klendProgramId,
1580
+ env.kvaultProgramId
1581
+ );
1582
+
1583
+ const vaultsWithDelegatedFarm = await kaminoManager.getVaultsWithDelegatedFarm();
1584
+ vaultsWithDelegatedFarm.forEach((delegatedFarm, vault) => {
1585
+ console.log(`vault ${vault} delegated farm ${delegatedFarm}`);
1586
+ });
1587
+ });
1588
+
1530
1589
  commands
1531
1590
  .command('simulate-reserve-apy')
1532
1591
  .requiredOption('--reserve <string>', 'Reserve address')
@@ -110,11 +110,16 @@ export function closeLookupTableIx(authority: TransactionSigner, lookupTable: Ad
110
110
  * Returns the accounts in a lookup table
111
111
  * @param rpc
112
112
  * @param lookupTable - lookup table to get the accounts from
113
- * @returns - an array of accounts in the lookup table
113
+ * @returns - an array of accounts in the lookup table or an empty array if the lookup table does not exist
114
114
  */
115
115
  export async function getAccountsInLut(rpc: Rpc<GetAccountInfoApi>, lookupTable: Address): Promise<Address[]> {
116
- const lutState = await fetchAddressLookupTable(rpc, lookupTable);
117
- return lutState.data.addresses;
116
+ try {
117
+ const lutState = await fetchAddressLookupTable(rpc, lookupTable);
118
+ return lutState.data.addresses;
119
+ } catch (error) {
120
+ console.error(`Error fetching accounts in lookup table ${lookupTable.toString()}`, error);
121
+ return [];
122
+ }
118
123
  }
119
124
  /**
120
125
  * This method inserts the missing keys from the provided keys into an existent lookup table