@kamino-finance/klend-sdk 7.2.2 → 7.2.4

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.
@@ -80,6 +80,7 @@ import { VaultConfigField, VaultConfigFieldKind } from '../@codegen/kvault/types
80
80
  import {
81
81
  AcceptVaultOwnershipIxs,
82
82
  APYs,
83
+ CreateVaultFarm,
83
84
  DepositIxs,
84
85
  DisinvestAllReservesIxs,
85
86
  InitVaultIxs,
@@ -238,6 +239,23 @@ export class KaminoManager {
238
239
  return this._vaultClient.createVaultIxs(vaultConfig);
239
240
  }
240
241
 
242
+ /**
243
+ * This method creates a farm for a vault
244
+ * @param admin - the admin of the vault
245
+ * @param vault - the vault to create a farm for (the vault should be already initialized)
246
+ * @returns a struct with the farm, the setup farm ixs and the update farm ixs
247
+ */
248
+ async createVaultFarmIxs(admin: TransactionSigner, vault: KaminoVault): Promise<CreateVaultFarm> {
249
+ const vaultState = await vault.getState();
250
+ if (!vaultState) {
251
+ throw new Error('Vault not initialized');
252
+ }
253
+ if (vaultState.vaultFarm !== DEFAULT_PUBLIC_KEY) {
254
+ throw new Error('Vault already has a farm');
255
+ }
256
+ return this._vaultClient.createVaultFarm(admin, vault.address, vaultState.sharesMint);
257
+ }
258
+
241
259
  /**
242
260
  * This method creates an instruction to set the shares metadata for a vault
243
261
  * @param authority - the vault admin
@@ -581,20 +599,24 @@ export class KaminoManager {
581
599
  * @param mode the field to update (based on VaultConfigFieldKind enum)
582
600
  * @param value the value to update the field with
583
601
  * @param [signer] 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
602
+ * @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
603
+ * @param [skipLutUpdate] if true, the lookup table instructions will not be included in the returned instructions
584
604
  * @returns a struct that contains the instruction to update the field and an optional list of instructions to update the lookup table
585
605
  */
586
606
  async updateVaultConfigIxs(
587
607
  vault: KaminoVault,
588
608
  mode: VaultConfigFieldKind | string,
589
609
  value: string,
590
- signer?: TransactionSigner
610
+ signer?: TransactionSigner,
611
+ lutIxsSigner?: TransactionSigner,
612
+ skipLutUpdate: boolean = false
591
613
  ): Promise<UpdateVaultConfigIxs> {
592
614
  if (typeof mode === 'string') {
593
615
  const field = VaultConfigField.fromDecoded({ [mode]: '' });
594
- return this._vaultClient.updateVaultConfigIxs(vault, field, value, signer);
616
+ return this._vaultClient.updateVaultConfigIxs(vault, field, value, signer, lutIxsSigner, skipLutUpdate);
595
617
  }
596
618
 
597
- return this._vaultClient.updateVaultConfigIxs(vault, mode, value, signer);
619
+ return this._vaultClient.updateVaultConfigIxs(vault, mode, value, signer, lutIxsSigner, skipLutUpdate);
598
620
  }
599
621
 
600
622
  /** Sets the farm where the shares can be staked. This is store in vault state and a vault can only have one farm, so the new farm will ovveride the old farm
@@ -607,9 +629,18 @@ export class KaminoManager {
607
629
  vault: KaminoVault,
608
630
  farm: Address,
609
631
  errorOnOverride: boolean = true,
610
- vaultAdminAuthority?: TransactionSigner
632
+ vaultAdminAuthority?: TransactionSigner,
633
+ lutIxsSigner?: TransactionSigner,
634
+ skipLutUpdate: boolean = false
611
635
  ): Promise<UpdateVaultConfigIxs> {
612
- return this._vaultClient.setVaultFarmIxs(vault, farm, errorOnOverride, vaultAdminAuthority);
636
+ return this._vaultClient.setVaultFarmIxs(
637
+ vault,
638
+ farm,
639
+ errorOnOverride,
640
+ vaultAdminAuthority,
641
+ lutIxsSigner,
642
+ skipLutUpdate
643
+ );
613
644
  }
614
645
 
615
646
  /**
@@ -93,6 +93,7 @@ import { getAccountOwner, getProgramAccounts } from '../utils/rpc';
93
93
  import {
94
94
  AcceptVaultOwnershipIxs,
95
95
  APYs,
96
+ CreateVaultFarm,
96
97
  DepositIxs,
97
98
  DisinvestAllReservesIxs,
98
99
  InitVaultIxs,
@@ -107,9 +108,11 @@ import {
107
108
  } from './vault_types';
108
109
  import { batchFetch, collToLamportsDecimal, ZERO } from '@kamino-finance/kliquidity-sdk';
109
110
  import { FullBPSDecimal } from '@kamino-finance/kliquidity-sdk/dist/utils/CreationParameters';
110
- import { FarmIncentives, FarmState, getUserStatePDA } from '@kamino-finance/farms-sdk/dist';
111
+ import { FarmConfigOption, FarmIncentives, FarmState, getUserStatePDA } from '@kamino-finance/farms-sdk/dist';
111
112
  import { getAccountsInLut, initLookupTableIx, insertIntoLookupTableIxs } from '../utils/lookupTable';
112
113
  import {
114
+ FARMS_ADMIN_MAINNET,
115
+ FARMS_GLOBAL_CONFIG_MAINNET,
113
116
  getFarmStakeIxs,
114
117
  getFarmUnstakeAndWithdrawIxs,
115
118
  getSharesInFarmUserPosition,
@@ -285,6 +288,8 @@ export class KaminoVaultClient {
285
288
  };
286
289
  const initVaultIx = initVault(initVaultAccounts, undefined, this._kaminoVaultProgramId);
287
290
 
291
+ const createVaultFarm = await this.createVaultFarm(vaultConfig.admin, vaultState.address, sharesMint);
292
+
288
293
  // create and set up the vault lookup table
289
294
  const [createLUTIx, lut] = await initLookupTableIx(vaultConfig.admin, slot);
290
295
 
@@ -300,6 +305,8 @@ export class KaminoVaultClient {
300
305
  TOKEN_PROGRAM_ADDRESS,
301
306
  this._kaminoLendProgramId,
302
307
  SYSVAR_INSTRUCTIONS_ADDRESS,
308
+ createVaultFarm.farm.address,
309
+ FARMS_GLOBAL_CONFIG_MAINNET,
303
310
  ];
304
311
  const insertIntoLUTIxs = await insertIntoLookupTableIxs(
305
312
  this.getConnection(),
@@ -345,6 +352,12 @@ export class KaminoVaultClient {
345
352
  );
346
353
  ixs.push(setNameIx);
347
354
  }
355
+ const setFarmIx = this.updateUninitialisedVaultConfigIx(
356
+ vaultConfig.admin,
357
+ vaultState.address,
358
+ new VaultConfigField.Farm(),
359
+ createVaultFarm.farm.address
360
+ );
348
361
 
349
362
  const metadataIx = await this.getSetSharesMetadataIx(
350
363
  this.getConnection(),
@@ -366,10 +379,57 @@ export class KaminoVaultClient {
366
379
  populateLUTIxs: insertIntoLUTIxs,
367
380
  cleanupIxs,
368
381
  initSharesMetadataIx: metadataIx,
382
+ createVaultFarm,
383
+ setFarmToVaultIx: setFarmIx,
369
384
  },
370
385
  };
371
386
  }
372
387
 
388
+ /**
389
+ * This method creates a farm for a vault
390
+ * @param signer - the signer of the transaction
391
+ * @param vaultSharesMint - the mint of the vault shares
392
+ * @param vaultAddress - the address of the vault (it doesn't need to be already initialized)
393
+ * @returns a struct with the farm, the setup farm ixs and the update farm ixs
394
+ */
395
+ async createVaultFarm(
396
+ signer: TransactionSigner,
397
+ vaultAddress: Address,
398
+ vaultSharesMint: Address
399
+ ): Promise<CreateVaultFarm> {
400
+ const farmsSDK = new Farms(this._rpc);
401
+
402
+ const farm = await generateKeyPairSigner();
403
+ const ixs = await farmsSDK.createFarmIxs(signer, farm, FARMS_GLOBAL_CONFIG_MAINNET, vaultSharesMint);
404
+
405
+ const updatePendingFarmAdminIx = await farmsSDK.updateFarmConfigIx(
406
+ signer,
407
+ farm.address,
408
+ DEFAULT_PUBLIC_KEY,
409
+ new FarmConfigOption.UpdatePendingFarmAdmin(),
410
+ FARMS_ADMIN_MAINNET,
411
+ undefined,
412
+ undefined,
413
+ true
414
+ );
415
+ const updateFarmVaultIdIx = await farmsSDK.updateFarmConfigIx(
416
+ signer,
417
+ farm.address,
418
+ DEFAULT_PUBLIC_KEY,
419
+ new FarmConfigOption.UpdateVaultId(),
420
+ vaultAddress,
421
+ undefined,
422
+ undefined,
423
+ true
424
+ );
425
+
426
+ return {
427
+ farm,
428
+ setupFarmIxs: ixs,
429
+ updateFarmIxs: [updatePendingFarmAdminIx, updateFarmVaultIdIx],
430
+ };
431
+ }
432
+
373
433
  /**
374
434
  * This method creates an instruction to set the shares metadata for a vault
375
435
  * @param rpc
@@ -697,13 +757,17 @@ export class KaminoVaultClient {
697
757
  * @param mode the field to update (based on VaultConfigFieldKind enum)
698
758
  * @param value the value to update the field with
699
759
  * @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
760
+ * @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
761
+ * @param [skipLutUpdate] if true, the lookup table instructions will not be included in the returned instructions
700
762
  * @returns a struct that contains the instruction to update the field and an optional list of instructions to update the lookup table
701
763
  */
702
764
  async updateVaultConfigIxs(
703
765
  vault: KaminoVault,
704
766
  mode: VaultConfigFieldKind,
705
767
  value: string,
706
- vaultAdminAuthority?: TransactionSigner
768
+ vaultAdminAuthority?: TransactionSigner,
769
+ lutIxsSigner?: TransactionSigner,
770
+ skipLutUpdate: boolean = false
707
771
  ): Promise<UpdateVaultConfigIxs> {
708
772
  const vaultState: VaultState = await vault.getState();
709
773
  const admin = parseVaultAdmin(vaultState, vaultAdminAuthority);
@@ -753,36 +817,41 @@ export class KaminoVaultClient {
753
817
 
754
818
  const updateLUTIxs: Instruction[] = [];
755
819
 
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
- );
820
+ if (!skipLutUpdate) {
821
+ const lutIxsSignerAccount = lutIxsSigner ? lutIxsSigner : admin;
822
+
823
+ if (mode.kind === new VaultConfigField.PendingVaultAdmin().kind) {
824
+ const newPubkey = address(value);
825
+
777
826
  const insertIntoLutIxs = await insertIntoLookupTableIxs(
778
827
  this.getConnection(),
779
- admin,
828
+ lutIxsSignerAccount,
780
829
  vaultState.vaultLookupTable,
781
- keysToAddToLUT
830
+ [newPubkey]
782
831
  );
783
832
  updateLUTIxs.push(...insertIntoLutIxs);
784
- } catch (error) {
785
- console.log(`Error fetching farm ${keysToAddToLUT[0].toString()} state`, error);
833
+ } else if (mode.kind === new VaultConfigField.Farm().kind) {
834
+ const keysToAddToLUT = [address(value)];
835
+ // if the farm already exist we want to read its state to add it to the LUT
836
+ try {
837
+ const farmState = await FarmState.fetch(this.getConnection(), keysToAddToLUT[0]);
838
+ keysToAddToLUT.push(
839
+ farmState!.farmVault,
840
+ farmState!.farmVaultsAuthority,
841
+ farmState!.token.mint,
842
+ farmState!.scopePrices,
843
+ farmState!.globalConfig
844
+ );
845
+ const insertIntoLutIxs = await insertIntoLookupTableIxs(
846
+ this.getConnection(),
847
+ lutIxsSignerAccount,
848
+ vaultState.vaultLookupTable,
849
+ keysToAddToLUT
850
+ );
851
+ updateLUTIxs.push(...insertIntoLutIxs);
852
+ } catch (error) {
853
+ console.log(`Error fetching farm ${keysToAddToLUT[0].toString()} state`, error);
854
+ }
786
855
  }
787
856
  }
788
857
 
@@ -799,19 +868,30 @@ export class KaminoVaultClient {
799
868
  * @param farm - the farm where the vault shares can be staked
800
869
  * @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
870
  * @param [vaultAdminAuthority] - vault admin - a noop vaultAdminAuthority is provided when absent for multisigs
802
- *
871
+ * @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
872
+ * @param [skipLutUpdate] - if true, the lookup table instructions will not be included in the returned instructions
873
+ * @returns - a struct that contains the instruction to update the farm and an optional list of instructions to update the lookup table
803
874
  */
804
875
  async setVaultFarmIxs(
805
876
  vault: KaminoVault,
806
877
  farm: Address,
807
878
  errorOnOverride: boolean = true,
808
- vaultAdminAuthority?: TransactionSigner
879
+ vaultAdminAuthority?: TransactionSigner,
880
+ lutIxsSigner?: TransactionSigner,
881
+ skipLutUpdate: boolean = false
809
882
  ): Promise<UpdateVaultConfigIxs> {
810
883
  const vaultHasFarm = await vault.hasFarm();
811
884
  if (vaultHasFarm && errorOnOverride) {
812
885
  throw new Error('Vault already has a farm, if you want to override it set errorOnOverride to false');
813
886
  }
814
- return this.updateVaultConfigIxs(vault, new VaultConfigField.Farm(), farm, vaultAdminAuthority);
887
+ return this.updateVaultConfigIxs(
888
+ vault,
889
+ new VaultConfigField.Farm(),
890
+ farm,
891
+ vaultAdminAuthority,
892
+ lutIxsSigner,
893
+ skipLutUpdate
894
+ );
815
895
  }
816
896
 
817
897
  /**
@@ -1916,7 +1996,8 @@ export class KaminoVaultClient {
1916
1996
  holdings.totalAUMIncludingFees.sub(holdings.pendingFees),
1917
1997
  new Decimal(vaultState.unallocatedWeight.toString()),
1918
1998
  new Decimal(vaultState.unallocatedTokensCap.toString()),
1919
- initialVaultAllocations
1999
+ initialVaultAllocations,
2000
+ vaultState.tokenMintDecimals.toNumber()
1920
2001
  );
1921
2002
  }
1922
2003
 
@@ -1924,9 +2005,16 @@ export class KaminoVaultClient {
1924
2005
  vaultAUM: Decimal,
1925
2006
  vaultUnallocatedWeight: Decimal,
1926
2007
  vaultUnallocatedCap: Decimal,
1927
- initialVaultAllocations: Map<Address, ReserveAllocationOverview>
2008
+ initialVaultAllocations: Map<Address, ReserveAllocationOverview>,
2009
+ vaultTokenDecimals: number
1928
2010
  ) {
1929
- return computeReservesAllocation(vaultAUM, vaultUnallocatedWeight, vaultUnallocatedCap, initialVaultAllocations);
2011
+ return computeReservesAllocation(
2012
+ vaultAUM,
2013
+ vaultUnallocatedWeight,
2014
+ vaultUnallocatedCap,
2015
+ initialVaultAllocations,
2016
+ vaultTokenDecimals
2017
+ );
1930
2018
  }
1931
2019
 
1932
2020
  /**
@@ -2018,9 +2106,7 @@ export class KaminoVaultClient {
2018
2106
  if (await vault.hasFarm()) {
2019
2107
  const userFarmState = allUserFarmStatesMap.get(state.vaultFarm);
2020
2108
  if (userFarmState) {
2021
- console.log('there is a farm state for vault', vault.address);
2022
2109
  const stakedShares = getSharesInFarmUserPosition(userFarmState, state.sharesMintDecimals.toNumber());
2023
- console.log('staked shares', stakedShares);
2024
2110
  const userSharesBalance = vaultUserShareBalance.get(vault.address);
2025
2111
  if (userSharesBalance) {
2026
2112
  userSharesBalance.stakedShares = stakedShares;
@@ -1,4 +1,4 @@
1
- import { Address, Instruction } from '@solana/kit';
1
+ import { Address, Instruction, TransactionSigner } from '@solana/kit';
2
2
  import Decimal from 'decimal.js/decimal';
3
3
 
4
4
  /** the populateLUTIxs should be executed in a separate transaction as we cannot create and populate a lookup table in the same tx */
@@ -9,6 +9,8 @@ export type InitVaultIxs = {
9
9
  populateLUTIxs: Instruction[];
10
10
  cleanupIxs: Instruction[];
11
11
  initSharesMetadataIx: Instruction;
12
+ createVaultFarm: CreateVaultFarm;
13
+ setFarmToVaultIx: Instruction;
12
14
  };
13
15
 
14
16
  export type AcceptVaultOwnershipIxs = {
@@ -78,3 +80,9 @@ export type APYs = {
78
80
  grossAPY: Decimal;
79
81
  netAPY: Decimal;
80
82
  };
83
+
84
+ export type CreateVaultFarm = {
85
+ farm: TransactionSigner;
86
+ setupFarmIxs: Instruction[];
87
+ updateFarmIxs: Instruction[];
88
+ };
@@ -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 : ''}`,
@@ -352,6 +352,22 @@ async function main() {
352
352
  ...instructions.createAtaIfNeededIxs,
353
353
  ...instructions.initVaultIxs,
354
354
  instructions.createLUTIx,
355
+ instructions.setFarmToVaultIx,
356
+ ...getPriorityFeeAndCuIxs({
357
+ priorityFeeMultiplier: 2500,
358
+ }),
359
+ ],
360
+ mode,
361
+ []
362
+ );
363
+ await sleep(2000);
364
+ // create the farm
365
+ await processTx(
366
+ env.c,
367
+ admin,
368
+ [
369
+ ...instructions.createVaultFarm.setupFarmIxs,
370
+ ...instructions.createVaultFarm.updateFarmIxs,
355
371
  ...getPriorityFeeAndCuIxs({
356
372
  priorityFeeMultiplier: 2500,
357
373
  }),
@@ -455,7 +471,9 @@ async function main() {
455
471
  kaminoVault,
456
472
  new PendingVaultAdmin(),
457
473
  newAdmin,
458
- signer
474
+ signer,
475
+ undefined,
476
+ true
459
477
  );
460
478
 
461
479
  await processTx(
@@ -485,7 +503,12 @@ async function main() {
485
503
  '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
504
  )
487
505
  .option(`--staging`, 'If true, will use the staging programs')
488
- .action(async ({ vault, field, value, mode, staging }) => {
506
+ .option(`--skip-lut-update`, 'If set, it will skip the LUT update')
507
+ .option(
508
+ `--lutSigner <string>`,
509
+ 'If set, it will use the provided signer instead of the default one for the LUT update'
510
+ )
511
+ .action(async ({ vault, field, value, mode, staging, skipLutUpdate, lutSigner }) => {
489
512
  const env = await initEnv(staging);
490
513
  const vaultAddress = address(vault);
491
514
 
@@ -500,7 +523,20 @@ async function main() {
500
523
  const vaultState = await kaminoVault.getState();
501
524
  const signer = await env.getSigner({ vaultState });
502
525
 
503
- const instructions = await kaminoManager.updateVaultConfigIxs(kaminoVault, field, value, signer);
526
+ let lutSignerOrUndefined = undefined;
527
+ if (lutSigner) {
528
+ lutSignerOrUndefined = await parseKeypairFile(lutSigner as string);
529
+ }
530
+
531
+ const shouldSkipLutUpdate = !!skipLutUpdate;
532
+ const instructions = await kaminoManager.updateVaultConfigIxs(
533
+ kaminoVault,
534
+ field,
535
+ value,
536
+ signer,
537
+ lutSignerOrUndefined,
538
+ shouldSkipLutUpdate
539
+ );
504
540
 
505
541
  await processTx(
506
542
  env.c,
@@ -576,10 +612,15 @@ async function main() {
576
612
  )
577
613
  .option(`--staging`, 'If true, will use the staging programs')
578
614
  .option(`--multisig <string>`, 'If using multisig mode this is required, otherwise will be ignored')
579
- .action(async ({ lut, addresses, mode, staging, multisig }) => {
615
+ .option(`--signer <string>`, 'If set, it will use the provided signer instead of the default one')
616
+ .action(async ({ lut, addresses, mode, staging, multisig, signer }) => {
580
617
  const env = await initEnv(multisig, staging);
581
618
  const lutAddress = address(lut);
582
-
619
+ let txSigner = await env.getSigner();
620
+ // if the signer is provided (path to a keypair) we use it, otherwise we use the default one
621
+ if (signer) {
622
+ txSigner = await parseKeypairFile(signer as string);
623
+ }
583
624
  const addressesArr = addresses.split(' ').map((a: string) => address(a));
584
625
 
585
626
  if (mode === 'multisig' && !multisig) {
@@ -593,12 +634,11 @@ async function main() {
593
634
  env.kvaultProgramId
594
635
  );
595
636
 
596
- const signer = await env.getSigner();
597
- const instructions = await kaminoManager.insertIntoLutIxs(signer, lutAddress, addressesArr);
637
+ const instructions = await kaminoManager.insertIntoLutIxs(txSigner, lutAddress, addressesArr);
598
638
 
599
639
  await processTx(
600
640
  env.c,
601
- signer,
641
+ txSigner,
602
642
  [
603
643
  ...instructions,
604
644
  ...getPriorityFeeAndCuIxs({
@@ -640,7 +680,8 @@ async function main() {
640
680
  '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
681
  )
642
682
  .option(`--staging`, 'If true, will use the staging programs')
643
- .action(async ({ vault, mode, staging }) => {
683
+ .option(`--signer <string>`, 'If set, it will use the provided signer instead of the default one')
684
+ .action(async ({ vault, mode, staging, signer }) => {
644
685
  const env = await initEnv(staging);
645
686
  const vaultAddress = address(vault);
646
687
 
@@ -653,14 +694,18 @@ async function main() {
653
694
 
654
695
  const kaminoVault = new KaminoVault(env.c.rpc, vaultAddress, undefined, env.kvaultProgramId);
655
696
  const vaultState = await kaminoVault.getState();
656
- const signer = await env.getSigner({ vaultState });
657
- const syncLUTIxs = await kaminoManager.syncVaultLUTIxs(signer, kaminoVault);
697
+ let txSigner = await env.getSigner({ vaultState });
698
+ // if the signer is provided (path to a keypair) we use it, otherwise we use the default one
699
+ if (signer) {
700
+ txSigner = await parseKeypairFile(signer as string);
701
+ }
702
+ const syncLUTIxs = await kaminoManager.syncVaultLUTIxs(txSigner, kaminoVault);
658
703
 
659
704
  // if we need to create the LUT we have to do that in a separate tx and wait a little bit after
660
705
  if (syncLUTIxs.setupLUTIfNeededIxs.length > 0) {
661
706
  await processTx(
662
707
  env.c,
663
- signer,
708
+ txSigner,
664
709
  [
665
710
  ...syncLUTIxs.setupLUTIfNeededIxs,
666
711
  ...getPriorityFeeAndCuIxs({
@@ -677,7 +722,7 @@ async function main() {
677
722
  for (const ix of syncLUTIxs.syncLUTIxs) {
678
723
  await processTx(
679
724
  env.c,
680
- signer,
725
+ txSigner,
681
726
  [
682
727
  ix,
683
728
  ...getPriorityFeeAndCuIxs({
@@ -1353,6 +1398,7 @@ async function main() {
1353
1398
  console.log('reserve ', reserveAddress);
1354
1399
  console.log('reserve incentive', incentive);
1355
1400
  });
1401
+ console.log('totalIncentivesAPY', vaultOverview.reservesFarmsIncentives.totalIncentivesAPY.toString());
1356
1402
  });
1357
1403
 
1358
1404
  commands
@@ -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
@@ -1,5 +1,6 @@
1
1
  import Decimal from 'decimal.js';
2
2
  import { Address } from '@solana/kit';
3
+ import { lamportsToCollDecimal } from '@kamino-finance/farms-sdk';
3
4
 
4
5
  export interface ReserveAllocationOverview {
5
6
  targetWeight: Decimal;
@@ -13,21 +14,22 @@ export interface VaultAllocationResult {
13
14
  }
14
15
 
15
16
  const ZERO = new Decimal(0);
16
- const ONE = new Decimal(1);
17
17
 
18
18
  /**
19
19
  * Computes the allocation of vault funds across reserves based on weights and caps
20
- * @param vaultAUM - Total AUM of the vault
20
+ * @param vaultAUM - Total AUM of the vault, in tokens
21
21
  * @param vaultUnallocatedWeight - Weight for unallocated funds
22
22
  * @param vaultUnallocatedCap - Maximum amount that can remain unallocated
23
23
  * @param initialVaultAllocations - Map of reserve addresses to their allocation configurations
24
- * @returns Object containing target unallocated amount and target allocations per reserve
24
+ * @param vaultTokenDecimals - The number of decimals of the vault token, needed to compute the min amount
25
+ * @returns Object containing target unallocated amount and target allocations per reserve, in tokens
25
26
  */
26
27
  export function computeReservesAllocation(
27
28
  vaultAUM: Decimal,
28
29
  vaultUnallocatedWeight: Decimal,
29
30
  vaultUnallocatedCap: Decimal,
30
- initialVaultAllocations: Map<Address, ReserveAllocationOverview>
31
+ initialVaultAllocations: Map<Address, ReserveAllocationOverview>,
32
+ vaultTokenDecimals: number
31
33
  ): VaultAllocationResult {
32
34
  let totalAllocation = new Decimal(0);
33
35
  const allReserves = Array.from(initialVaultAllocations.keys());
@@ -51,8 +53,10 @@ export function computeReservesAllocation(
51
53
 
52
54
  let currentAllocationSum = totalAllocation;
53
55
 
56
+ const reservesCount = allReserves.length;
57
+ const maxRemainedUninvestedLamports = lamportsToCollDecimal(new Decimal(reservesCount), vaultTokenDecimals); // invest only if the AUM has more lamports than the number of reserves
54
58
  // Iteratively allocate funds to reserves based on weights and caps
55
- while (totalLeftToInvest.gt(ONE) && currentAllocationSum.gt(ZERO)) {
59
+ while (totalLeftToInvest.gt(maxRemainedUninvestedLamports) && currentAllocationSum.gt(ZERO)) {
56
60
  const totalLeftover = totalLeftToInvest;
57
61
 
58
62
  for (const reserve of allReserves) {