@kamino-finance/klend-sdk 5.3.0 → 5.4.1
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/dist/classes/action.d.ts +0 -3
- package/dist/classes/action.d.ts.map +1 -1
- package/dist/classes/action.js +7 -10
- package/dist/classes/action.js.map +1 -1
- package/dist/classes/manager.d.ts +17 -5
- package/dist/classes/manager.d.ts.map +1 -1
- package/dist/classes/manager.js +25 -15
- package/dist/classes/manager.js.map +1 -1
- package/dist/classes/market.d.ts +2 -2
- package/dist/classes/market.d.ts.map +1 -1
- package/dist/classes/market.js +52 -20
- package/dist/classes/market.js.map +1 -1
- package/dist/classes/vault.d.ts +24 -4
- package/dist/classes/vault.d.ts.map +1 -1
- package/dist/classes/vault.js +54 -15
- package/dist/classes/vault.js.map +1 -1
- package/dist/client_kamino_manager.d.ts.map +1 -1
- package/dist/client_kamino_manager.js +14 -1
- package/dist/client_kamino_manager.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/accounts/VaultState.d.ts +6 -0
- package/dist/idl_codegen_kamino_vault/accounts/VaultState.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/accounts/VaultState.js +13 -1
- package/dist/idl_codegen_kamino_vault/accounts/VaultState.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/VaultAllocation.d.ts +5 -0
- package/dist/idl_codegen_kamino_vault/types/VaultAllocation.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/VaultAllocation.js +8 -1
- package/dist/idl_codegen_kamino_vault/types/VaultAllocation.js.map +1 -1
- package/dist/utils/constants.d.ts +4 -0
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +5 -1
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/rpc.d.ts +2 -1
- package/dist/utils/rpc.d.ts.map +1 -1
- package/dist/utils/rpc.js +8 -6
- package/dist/utils/rpc.js.map +1 -1
- package/package.json +9 -4
- package/src/classes/action.ts +1 -4
- package/src/classes/manager.ts +38 -13
- package/src/classes/market.ts +73 -21
- package/src/classes/vault.ts +86 -15
- package/src/client_kamino_manager.ts +21 -1
- package/src/idl_codegen_kamino_vault/accounts/VaultState.ts +17 -1
- package/src/idl_codegen_kamino_vault/types/VaultAllocation.ts +10 -1
- package/src/utils/constants.ts +6 -0
- package/src/utils/rpc.ts +8 -5
package/dist/utils/rpc.js
CHANGED
|
@@ -8,19 +8,21 @@ exports.getAccountOwner = getAccountOwner;
|
|
|
8
8
|
const web3_js_1 = require("@solana/web3.js");
|
|
9
9
|
const buffer_1 = require("buffer");
|
|
10
10
|
const axios_1 = __importDefault(require("axios"));
|
|
11
|
-
const zstd_wasm_1 = require("@bokuweb/zstd-wasm");
|
|
12
11
|
const uuid_1 = require("uuid");
|
|
12
|
+
const zstddec_1 = require("zstddec");
|
|
13
|
+
const decoder = new zstddec_1.ZSTDDecoder();
|
|
13
14
|
(async () => {
|
|
14
|
-
await
|
|
15
|
+
await decoder.init();
|
|
15
16
|
})();
|
|
16
17
|
/**
|
|
17
18
|
* Uses zstd compression when fetching all accounts owned by a program for a smaller response size
|
|
18
19
|
* Uses axios instead of node-fetch to work around a bug in node-fetch that causes subsequent requests with different encoding to fail
|
|
19
20
|
* @param connection
|
|
20
21
|
* @param programId
|
|
22
|
+
* @param structSize - the size of the decompressed account data struct
|
|
21
23
|
* @param configOrCommitment
|
|
22
24
|
*/
|
|
23
|
-
async function getProgramAccounts(connection, programId, configOrCommitment) {
|
|
25
|
+
async function getProgramAccounts(connection, programId, structSize, configOrCommitment) {
|
|
24
26
|
const programIdStr = programId.toBase58();
|
|
25
27
|
const { commitment, config } = extractCommitmentFromConfig(configOrCommitment);
|
|
26
28
|
const { encoding: _encoding, ...configWithoutEncoding } = config || {};
|
|
@@ -52,7 +54,7 @@ async function getProgramAccounts(connection, programId, configOrCommitment) {
|
|
|
52
54
|
}
|
|
53
55
|
const res = unsafeRes;
|
|
54
56
|
const deser = res.result.map(async (account) => ({
|
|
55
|
-
account: await deserializeAccountInfo(account.account),
|
|
57
|
+
account: await deserializeAccountInfo(account.account, structSize),
|
|
56
58
|
pubkey: new web3_js_1.PublicKey(account.pubkey),
|
|
57
59
|
}));
|
|
58
60
|
const x = await Promise.all(deser);
|
|
@@ -65,8 +67,8 @@ async function getAccountOwner(connection, address) {
|
|
|
65
67
|
}
|
|
66
68
|
return acc.owner;
|
|
67
69
|
}
|
|
68
|
-
async function deserializeAccountInfo(accountInfo) {
|
|
69
|
-
const data =
|
|
70
|
+
async function deserializeAccountInfo(accountInfo, size) {
|
|
71
|
+
const data = decoder.decode(buffer_1.Buffer.from(accountInfo.data[0], 'base64'), size);
|
|
70
72
|
return {
|
|
71
73
|
owner: accountInfo.owner,
|
|
72
74
|
lamports: accountInfo.lamports,
|
package/dist/utils/rpc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../src/utils/rpc.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../src/utils/rpc.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kamino-finance/klend-sdk",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.4.1",
|
|
4
4
|
"description": "Typescript SDK for interacting with the Kamino Lending (klend) protocol",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"docs": "typedoc",
|
|
30
30
|
"build": "rm -rf dist/; tsc",
|
|
31
31
|
"build:test": "yarn tsc --project ./tests/tsconfig.json",
|
|
32
|
+
"build:watch": "yarn tsc --watch",
|
|
32
33
|
"test": "npx ts-mocha tests/**/*.test.ts --exit",
|
|
33
34
|
"watch": "tsc --watch",
|
|
34
35
|
"coverage": "jest --coverage",
|
|
@@ -43,10 +44,11 @@
|
|
|
43
44
|
"start-validator": "solana-test-validator $(./deps/test-validator-params.sh)",
|
|
44
45
|
"start-validator-and-test": "yarn start-server-and-test 'yarn start-validator' http://127.0.0.1:8899/health test",
|
|
45
46
|
"dump-programs": "./deps/dump-from-mainnet.sh",
|
|
46
|
-
"kamino-manager": "tsx src/client_kamino_manager.ts"
|
|
47
|
+
"kamino-manager": "tsx src/client_kamino_manager.ts",
|
|
48
|
+
"dev": "concurrently \"yarn build:watch\" \"nodemon --watch dist -e js,ts --exec 'yarn dev:push-and-update'\"",
|
|
49
|
+
"dev:push-and-update": "yalc publish && cd examples && yalc update"
|
|
47
50
|
},
|
|
48
51
|
"dependencies": {
|
|
49
|
-
"@bokuweb/zstd-wasm": "^0.0.20",
|
|
50
52
|
"@coral-xyz/anchor": "^0.28.0",
|
|
51
53
|
"@coral-xyz/borsh": "^0.28.0",
|
|
52
54
|
"@kamino-finance/farms-sdk": "^2.0.3",
|
|
@@ -64,7 +66,8 @@
|
|
|
64
66
|
"commander": "^9.3.0",
|
|
65
67
|
"decimal.js": "^10.4.3",
|
|
66
68
|
"exponential-backoff": "^3.1.1",
|
|
67
|
-
"uuid": "^10.0.0"
|
|
69
|
+
"uuid": "^10.0.0",
|
|
70
|
+
"zstddec": "^0.1.0"
|
|
68
71
|
},
|
|
69
72
|
"devDependencies": {
|
|
70
73
|
"@orca-so/sdk": "^1.2.25",
|
|
@@ -81,6 +84,7 @@
|
|
|
81
84
|
"anchor-client-gen": "^0.28.1",
|
|
82
85
|
"chai": "^4.3.7",
|
|
83
86
|
"chai-bignumber": "^3.1.0",
|
|
87
|
+
"concurrently": "^9.1.0",
|
|
84
88
|
"eslint": "^8.4.0",
|
|
85
89
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
86
90
|
"eslint-config-airbnb-typescript": "^16.1.0",
|
|
@@ -90,6 +94,7 @@
|
|
|
90
94
|
"jest": "^27.4.3",
|
|
91
95
|
"jest-fetch-mock": "^3.0.3",
|
|
92
96
|
"mocha": "^10.2.0",
|
|
97
|
+
"nodemon": "^3.1.7",
|
|
93
98
|
"prettier": "^2.8.8",
|
|
94
99
|
"start-server-and-test": "^2.0.0",
|
|
95
100
|
"ts-jest": "^28.0.7",
|
package/src/classes/action.ts
CHANGED
|
@@ -58,6 +58,7 @@ import {
|
|
|
58
58
|
getAssociatedTokenAddress,
|
|
59
59
|
ScopeRefresh,
|
|
60
60
|
createAtasIdempotent,
|
|
61
|
+
POSITION_LIMIT,
|
|
61
62
|
} from '../utils';
|
|
62
63
|
import { KaminoMarket } from './market';
|
|
63
64
|
import { KaminoObligation } from './obligation';
|
|
@@ -69,10 +70,6 @@ import { VanillaObligation } from '../utils/ObligationType';
|
|
|
69
70
|
import { PROGRAM_ID } from '../lib';
|
|
70
71
|
import { U16_MAX } from '@kamino-finance/scope-sdk';
|
|
71
72
|
|
|
72
|
-
export const POSITION_LIMIT = 10;
|
|
73
|
-
export const BORROWS_LIMIT = 5;
|
|
74
|
-
export const DEPOSITS_LIMIT = 8;
|
|
75
|
-
|
|
76
73
|
const SOL_PADDING_FOR_INTEREST = new BN('1000000');
|
|
77
74
|
|
|
78
75
|
export type ActionType =
|
package/src/classes/manager.ts
CHANGED
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
MarketOverview,
|
|
18
18
|
ReserveAllocationConfig,
|
|
19
19
|
ReserveOverview,
|
|
20
|
+
SimulatedVaultHoldingsWithEarnedInterest,
|
|
21
|
+
VaultFees,
|
|
20
22
|
VaultFeesPct,
|
|
21
23
|
VaultHolder,
|
|
22
24
|
VaultHoldings,
|
|
@@ -495,20 +497,18 @@ export class KaminoManager {
|
|
|
495
497
|
|
|
496
498
|
/**
|
|
497
499
|
* Get all vaults
|
|
498
|
-
* @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
|
|
499
500
|
* @returns an array of all vaults
|
|
500
501
|
*/
|
|
501
|
-
async getAllVaults(
|
|
502
|
-
return this._vaultClient.getAllVaults(
|
|
502
|
+
async getAllVaults(): Promise<KaminoVault[]> {
|
|
503
|
+
return this._vaultClient.getAllVaults();
|
|
503
504
|
}
|
|
504
505
|
|
|
505
506
|
/**
|
|
506
507
|
* Get all vaults for owner
|
|
507
508
|
* @param owner the pubkey of the vaults owner
|
|
508
|
-
* @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
|
|
509
509
|
* @returns an array of all vaults owned by a given pubkey
|
|
510
510
|
*/
|
|
511
|
-
async getAllVaultsForOwner(owner: PublicKey
|
|
511
|
+
async getAllVaultsForOwner(owner: PublicKey): Promise<KaminoVault[]> {
|
|
512
512
|
const filters = [
|
|
513
513
|
{
|
|
514
514
|
dataSize: VaultState.layout.span + 8,
|
|
@@ -527,16 +527,15 @@ export class KaminoManager {
|
|
|
527
527
|
},
|
|
528
528
|
];
|
|
529
529
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
530
|
+
const kaminoVaults: GetProgramAccountsResponse = await getProgramAccounts(
|
|
531
|
+
this._connection,
|
|
532
|
+
this._kaminoVaultProgramId,
|
|
533
|
+
VaultState.layout.span + 8,
|
|
534
|
+
{
|
|
534
535
|
commitment: this._connection.commitment ?? 'processed',
|
|
535
536
|
filters,
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
kaminoVaults = await this._connection.getProgramAccounts(this._kaminoVaultProgramId, { filters });
|
|
539
|
-
}
|
|
537
|
+
}
|
|
538
|
+
);
|
|
540
539
|
|
|
541
540
|
return kaminoVaults.map((kaminoVault) => {
|
|
542
541
|
if (kaminoVault.account === null) {
|
|
@@ -704,6 +703,32 @@ export class KaminoManager {
|
|
|
704
703
|
return this._vaultClient.getVaultCumulativeInterest(vaultState);
|
|
705
704
|
}
|
|
706
705
|
|
|
706
|
+
/**
|
|
707
|
+
* Simulate the current holdings of the vault and the earned interest
|
|
708
|
+
* @param vaultState the kamino vault state to get simulated holdings and earnings for
|
|
709
|
+
* @param vaultReserves optional; the state of the reserves in the vault allocation
|
|
710
|
+
* @returns a struct of simulated vault holdings and earned interest
|
|
711
|
+
*/
|
|
712
|
+
async calculateSimulatedHoldingsWithInterest(
|
|
713
|
+
vaultState: VaultState,
|
|
714
|
+
vaultReserves?: PubkeyHashMap<PublicKey, KaminoReserve>
|
|
715
|
+
): Promise<SimulatedVaultHoldingsWithEarnedInterest> {
|
|
716
|
+
return this._vaultClient.calculateSimulatedHoldingsWithInterest(vaultState, vaultReserves);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Simulate the current holdings and compute the fees that would be charged
|
|
721
|
+
* @param vaultState the kamino vault state to get simulated fees for
|
|
722
|
+
* @param simulatedCurrentHoldingsWithInterest optional; the simulated holdings and interest earned by the vault
|
|
723
|
+
* @returns a struct of simulated management and interest fees
|
|
724
|
+
*/
|
|
725
|
+
async calculateSimulatedFees(
|
|
726
|
+
vaultState: VaultState,
|
|
727
|
+
simulatedCurrentHoldingsWithInterest?: SimulatedVaultHoldingsWithEarnedInterest
|
|
728
|
+
): Promise<VaultFees> {
|
|
729
|
+
return this._vaultClient.calculateSimulatedFees(vaultState, simulatedCurrentHoldingsWithInterest);
|
|
730
|
+
}
|
|
731
|
+
|
|
707
732
|
/**
|
|
708
733
|
* This will load the onchain state for all the reserves that the vault has allocations for
|
|
709
734
|
* @param vaultState - the vault state to load reserves for
|
package/src/classes/market.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AccountInfo, Connection,
|
|
1
|
+
import { AccountInfo, Connection, PublicKey } from '@solana/web3.js';
|
|
2
2
|
import { KaminoObligation } from './obligation';
|
|
3
3
|
import { KaminoReserve } from './reserve';
|
|
4
4
|
import { LendingMarket, Obligation, UserMetadata, ReferrerTokenState, Reserve } from '../idl_codegen/accounts';
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
PubkeyHashMap,
|
|
22
22
|
CandidatePrice,
|
|
23
23
|
PublicKeySet,
|
|
24
|
+
DEPOSITS_LIMIT,
|
|
24
25
|
} from '../utils';
|
|
25
26
|
import base58 from 'bs58';
|
|
26
27
|
import { BN } from '@coral-xyz/anchor';
|
|
@@ -531,9 +532,8 @@ export class KaminoMarket {
|
|
|
531
532
|
* This function will likely require an RPC capable of returning more than the default 100k rows in a single scan
|
|
532
533
|
*
|
|
533
534
|
* @param tag
|
|
534
|
-
* @param useOptimisedRPCCall - use the optimised RPC call (compressed) to get all obligations
|
|
535
535
|
*/
|
|
536
|
-
async getAllObligationsForMarket(tag?: number
|
|
536
|
+
async getAllObligationsForMarket(tag?: number): Promise<KaminoObligation[]> {
|
|
537
537
|
const filters = [
|
|
538
538
|
{
|
|
539
539
|
dataSize: Obligation.layout.span + 8,
|
|
@@ -558,26 +558,14 @@ export class KaminoMarket {
|
|
|
558
558
|
const collateralExchangeRates = new PubkeyHashMap<PublicKey, Decimal>();
|
|
559
559
|
const cumulativeBorrowRates = new PubkeyHashMap<PublicKey, Decimal>();
|
|
560
560
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
if (useOptimisedRPCCall) {
|
|
566
|
-
[slot, obligations] = await Promise.all([
|
|
567
|
-
slotPromise,
|
|
568
|
-
getProgramAccounts(this.connection, this.programId, {
|
|
569
|
-
commitment: this.connection.commitment ?? 'processed',
|
|
570
|
-
filters,
|
|
571
|
-
dataSlice: { offset: 0, length: ObligationZP.layout.span + 8 }, // truncate the padding
|
|
572
|
-
}),
|
|
573
|
-
]);
|
|
574
|
-
} else {
|
|
575
|
-
const obligationsPromise = this.connection.getProgramAccounts(this.programId, {
|
|
561
|
+
const [slot, obligations] = await Promise.all([
|
|
562
|
+
this.connection.getSlot(),
|
|
563
|
+
getProgramAccounts(this.connection, this.programId, ObligationZP.layout.span + 8, {
|
|
564
|
+
commitment: this.connection.commitment ?? 'processed',
|
|
576
565
|
filters,
|
|
577
566
|
dataSlice: { offset: 0, length: ObligationZP.layout.span + 8 }, // truncate the padding
|
|
578
|
-
})
|
|
579
|
-
|
|
580
|
-
}
|
|
567
|
+
}),
|
|
568
|
+
]);
|
|
581
569
|
|
|
582
570
|
return obligations.map((obligation) => {
|
|
583
571
|
if (obligation.account === null) {
|
|
@@ -740,6 +728,70 @@ export class KaminoMarket {
|
|
|
740
728
|
});
|
|
741
729
|
}
|
|
742
730
|
|
|
731
|
+
async getAllObligationsByDepositedReserve(reserve: PublicKey) {
|
|
732
|
+
const finalObligations: KaminoObligation[] = [];
|
|
733
|
+
for (let i = 0; i < DEPOSITS_LIMIT; i++) {
|
|
734
|
+
const [slot, obligations] = await Promise.all([
|
|
735
|
+
this.connection.getSlot(),
|
|
736
|
+
this.connection.getProgramAccounts(this.programId, {
|
|
737
|
+
filters: [
|
|
738
|
+
{
|
|
739
|
+
dataSize: Obligation.layout.span + 8,
|
|
740
|
+
},
|
|
741
|
+
{
|
|
742
|
+
memcmp: {
|
|
743
|
+
offset: 96 + 136 * i,
|
|
744
|
+
bytes: reserve.toBase58(),
|
|
745
|
+
},
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
memcmp: {
|
|
749
|
+
offset: 32,
|
|
750
|
+
bytes: this.address,
|
|
751
|
+
},
|
|
752
|
+
},
|
|
753
|
+
],
|
|
754
|
+
}),
|
|
755
|
+
]);
|
|
756
|
+
|
|
757
|
+
const collateralExchangeRates = new PubkeyHashMap<PublicKey, Decimal>();
|
|
758
|
+
const cumulativeBorrowRates = new PubkeyHashMap<PublicKey, Decimal>();
|
|
759
|
+
|
|
760
|
+
const obligationsBatch = obligations.map((obligation) => {
|
|
761
|
+
if (obligation.account === null) {
|
|
762
|
+
throw new Error('Invalid account');
|
|
763
|
+
}
|
|
764
|
+
if (!obligation.account.owner.equals(this.programId)) {
|
|
765
|
+
throw new Error("account doesn't belong to this program");
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
const obligationAccount = Obligation.decode(obligation.account.data);
|
|
769
|
+
|
|
770
|
+
if (!obligationAccount) {
|
|
771
|
+
throw Error('Could not parse obligation.');
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
KaminoObligation.addRatesForObligation(
|
|
775
|
+
this,
|
|
776
|
+
obligationAccount,
|
|
777
|
+
collateralExchangeRates,
|
|
778
|
+
cumulativeBorrowRates,
|
|
779
|
+
slot
|
|
780
|
+
);
|
|
781
|
+
|
|
782
|
+
return new KaminoObligation(
|
|
783
|
+
this,
|
|
784
|
+
obligation.pubkey,
|
|
785
|
+
obligationAccount,
|
|
786
|
+
collateralExchangeRates,
|
|
787
|
+
cumulativeBorrowRates
|
|
788
|
+
);
|
|
789
|
+
});
|
|
790
|
+
finalObligations.push(...obligationsBatch);
|
|
791
|
+
}
|
|
792
|
+
return finalObligations;
|
|
793
|
+
}
|
|
794
|
+
|
|
743
795
|
async getAllUserObligations(user: PublicKey) {
|
|
744
796
|
const [currentSlot, obligations] = await Promise.all([
|
|
745
797
|
this.connection.getSlot(),
|
package/src/classes/vault.ts
CHANGED
|
@@ -58,7 +58,7 @@ import { withdraw } from '../idl_codegen_kamino_vault/instructions';
|
|
|
58
58
|
import { PROGRAM_ID } from '../idl_codegen/programId';
|
|
59
59
|
import { DEFAULT_RECENT_SLOT_DURATION_MS, ReserveWithAddress } from './reserve';
|
|
60
60
|
import { Fraction } from './fraction';
|
|
61
|
-
import { createAtasIdempotent, lendingMarketAuthPda, PublicKeySet } from '../utils';
|
|
61
|
+
import { createAtasIdempotent, lendingMarketAuthPda, PublicKeySet, SECONDS_PER_YEAR } from '../utils';
|
|
62
62
|
import bs58 from 'bs58';
|
|
63
63
|
import { getAccountOwner, getProgramAccounts } from '../utils/rpc';
|
|
64
64
|
import {
|
|
@@ -69,6 +69,7 @@ import {
|
|
|
69
69
|
UpdateVaultConfigIxs,
|
|
70
70
|
} from './types';
|
|
71
71
|
import { collToLamportsDecimal } from '@kamino-finance/kliquidity-sdk';
|
|
72
|
+
import { FullBPSDecimal } from '@kamino-finance/kliquidity-sdk/dist/utils/CreationParameters';
|
|
72
73
|
|
|
73
74
|
export const kaminoVaultId = new PublicKey('kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr');
|
|
74
75
|
export const kaminoVaultStagingId = new PublicKey('STkvh7ostar39Fwr4uZKASs1RNNuYMFMTsE77FiRsL2');
|
|
@@ -1349,16 +1350,14 @@ export class KaminoVaultClient {
|
|
|
1349
1350
|
* @param vaultsOverride - a list of vaults to get the tokens per share for; if provided with state it will not fetch the state again
|
|
1350
1351
|
* @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
|
|
1351
1352
|
* @param slot - current slot, used to estimate the interest earned in the different reserves with allocation from the vault
|
|
1352
|
-
* @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
|
|
1353
1353
|
* @returns - token per share value
|
|
1354
1354
|
*/
|
|
1355
1355
|
async getTokensPerShareAllVaults(
|
|
1356
1356
|
slot: number,
|
|
1357
1357
|
vaultsOverride?: Array<KaminoVault>,
|
|
1358
|
-
vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve
|
|
1359
|
-
useOptimisedRPCCall: boolean = true
|
|
1358
|
+
vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve>
|
|
1360
1359
|
): Promise<PubkeyHashMap<PublicKey, Decimal>> {
|
|
1361
|
-
const vaults = vaultsOverride ? vaultsOverride : await this.getAllVaults(
|
|
1360
|
+
const vaults = vaultsOverride ? vaultsOverride : await this.getAllVaults();
|
|
1362
1361
|
const vaultTokensPerShare = new PubkeyHashMap<PublicKey, Decimal>();
|
|
1363
1362
|
for (const vault of vaults) {
|
|
1364
1363
|
const tokensPerShare = await this.getTokensPerShareSingleVault(vault, slot, vaultReservesMap);
|
|
@@ -1370,10 +1369,9 @@ export class KaminoVaultClient {
|
|
|
1370
1369
|
|
|
1371
1370
|
/**
|
|
1372
1371
|
* Get all vaults
|
|
1373
|
-
* @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
|
|
1374
1372
|
* @returns an array of all vaults
|
|
1375
1373
|
*/
|
|
1376
|
-
async getAllVaults(
|
|
1374
|
+
async getAllVaults(): Promise<KaminoVault[]> {
|
|
1377
1375
|
const filters = [
|
|
1378
1376
|
{
|
|
1379
1377
|
dataSize: VaultState.layout.span + 8,
|
|
@@ -1386,16 +1384,15 @@ export class KaminoVaultClient {
|
|
|
1386
1384
|
},
|
|
1387
1385
|
];
|
|
1388
1386
|
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1387
|
+
const kaminoVaults: GetProgramAccountsResponse = await getProgramAccounts(
|
|
1388
|
+
this._connection,
|
|
1389
|
+
this._kaminoVaultProgramId,
|
|
1390
|
+
VaultState.layout.span + 8,
|
|
1391
|
+
{
|
|
1393
1392
|
commitment: this._connection.commitment ?? 'processed',
|
|
1394
1393
|
filters,
|
|
1395
|
-
}
|
|
1396
|
-
|
|
1397
|
-
kaminoVaults = await this._connection.getProgramAccounts(this._kaminoVaultProgramId, { filters });
|
|
1398
|
-
}
|
|
1394
|
+
}
|
|
1395
|
+
);
|
|
1399
1396
|
|
|
1400
1397
|
return kaminoVaults.map((kaminoVault) => {
|
|
1401
1398
|
if (kaminoVault.account === null) {
|
|
@@ -1926,6 +1923,70 @@ export class KaminoVaultClient {
|
|
|
1926
1923
|
const netYieldLamports = new Fraction(vaultState.cumulativeEarnedInterestSf).toDecimal();
|
|
1927
1924
|
return lamportsToDecimal(netYieldLamports, vaultState.tokenMintDecimals.toString());
|
|
1928
1925
|
}
|
|
1926
|
+
|
|
1927
|
+
/**
|
|
1928
|
+
* Simulate the current holdings of the vault and the earned interest
|
|
1929
|
+
* @param vaultState the kamino vault state to get simulated holdings and earnings for
|
|
1930
|
+
* @param vaultReserves optional; the state of the reserves in the vault allocation
|
|
1931
|
+
* @returns a struct of simulated vault holdings and earned interest
|
|
1932
|
+
*/
|
|
1933
|
+
async calculateSimulatedHoldingsWithInterest(
|
|
1934
|
+
vaultState: VaultState,
|
|
1935
|
+
vaultReserves?: PubkeyHashMap<PublicKey, KaminoReserve>
|
|
1936
|
+
): Promise<SimulatedVaultHoldingsWithEarnedInterest> {
|
|
1937
|
+
const latestUpdateTs = vaultState.lastFeeChargeTimestamp.toNumber();
|
|
1938
|
+
const lastUpdateSlot = latestUpdateTs / this.recentSlotDurationMs;
|
|
1939
|
+
|
|
1940
|
+
const currentSlot = await this._connection.getSlot('confirmed');
|
|
1941
|
+
|
|
1942
|
+
const lastUpdateHoldingsPromise = this.getVaultHoldings(vaultState, lastUpdateSlot, vaultReserves);
|
|
1943
|
+
const currentHoldingsPromise = this.getVaultHoldings(vaultState, currentSlot, vaultReserves);
|
|
1944
|
+
const [lastUpdateHoldings, currentHoldings] = await Promise.all([
|
|
1945
|
+
lastUpdateHoldingsPromise,
|
|
1946
|
+
currentHoldingsPromise,
|
|
1947
|
+
]);
|
|
1948
|
+
|
|
1949
|
+
const earnedInterest = currentHoldings.total.sub(lastUpdateHoldings.total);
|
|
1950
|
+
|
|
1951
|
+
return {
|
|
1952
|
+
holdings: currentHoldings,
|
|
1953
|
+
earnedInterest: earnedInterest,
|
|
1954
|
+
};
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
/**
|
|
1958
|
+
* Simulate the current holdings and compute the fees that would be charged
|
|
1959
|
+
* @param vaultState the kamino vault state to get simulated fees for
|
|
1960
|
+
* @param simulatedCurrentHoldingsWithInterest optional; the simulated holdings and interest earned by the vault
|
|
1961
|
+
* @returns a struct of simulated management and interest fees
|
|
1962
|
+
*/
|
|
1963
|
+
async calculateSimulatedFees(
|
|
1964
|
+
vaultState: VaultState,
|
|
1965
|
+
simulatedCurrentHoldingsWithInterest?: SimulatedVaultHoldingsWithEarnedInterest
|
|
1966
|
+
): Promise<VaultFees> {
|
|
1967
|
+
const timestampNow = new Date().getTime();
|
|
1968
|
+
const timestampLastUpdate = vaultState.lastFeeChargeTimestamp.toNumber();
|
|
1969
|
+
const timeElapsed = timestampNow - timestampLastUpdate;
|
|
1970
|
+
|
|
1971
|
+
const simulatedCurrentHoldings = simulatedCurrentHoldingsWithInterest
|
|
1972
|
+
? simulatedCurrentHoldingsWithInterest
|
|
1973
|
+
: await this.calculateSimulatedHoldingsWithInterest(vaultState);
|
|
1974
|
+
|
|
1975
|
+
const performanceFee = simulatedCurrentHoldings.earnedInterest.mul(
|
|
1976
|
+
new Decimal(vaultState.performanceFeeBps.toString()).div(FullBPSDecimal)
|
|
1977
|
+
);
|
|
1978
|
+
|
|
1979
|
+
const managementFeeFactor = new Decimal(timeElapsed)
|
|
1980
|
+
.mul(new Decimal(vaultState.managementFeeBps.toString()))
|
|
1981
|
+
.div(new Decimal(SECONDS_PER_YEAR));
|
|
1982
|
+
const prevAUM = new Fraction(vaultState.prevAumSf).toDecimal();
|
|
1983
|
+
const mgmtFee = prevAUM.mul(managementFeeFactor);
|
|
1984
|
+
|
|
1985
|
+
return {
|
|
1986
|
+
managementFee: mgmtFee,
|
|
1987
|
+
performanceFee: performanceFee,
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1929
1990
|
} // KaminoVaultClient
|
|
1930
1991
|
|
|
1931
1992
|
export class KaminoVault {
|
|
@@ -2046,6 +2107,11 @@ export type VaultHoldings = {
|
|
|
2046
2107
|
total: Decimal;
|
|
2047
2108
|
};
|
|
2048
2109
|
|
|
2110
|
+
export type SimulatedVaultHoldingsWithEarnedInterest = {
|
|
2111
|
+
holdings: VaultHoldings;
|
|
2112
|
+
earnedInterest: Decimal;
|
|
2113
|
+
};
|
|
2114
|
+
|
|
2049
2115
|
export type VaultHoldingsWithUSDValue = {
|
|
2050
2116
|
holdings: VaultHoldings;
|
|
2051
2117
|
availableUSD: Decimal;
|
|
@@ -2095,3 +2161,8 @@ export type VaultFeesPct = {
|
|
|
2095
2161
|
managementFeePct: Decimal;
|
|
2096
2162
|
performanceFeePct: Decimal;
|
|
2097
2163
|
};
|
|
2164
|
+
|
|
2165
|
+
export type VaultFees = {
|
|
2166
|
+
managementFee: Decimal;
|
|
2167
|
+
performanceFee: Decimal;
|
|
2168
|
+
};
|
|
@@ -922,10 +922,30 @@ async function main() {
|
|
|
922
922
|
const env = initializeClient(false, false);
|
|
923
923
|
const kaminoManager = new KaminoManager(env.connection, env.kLendProgramId, env.kVaultProgramId);
|
|
924
924
|
|
|
925
|
-
const allVaults = await kaminoManager.getAllVaults(
|
|
925
|
+
const allVaults = await kaminoManager.getAllVaults();
|
|
926
926
|
console.log('all vaults', allVaults);
|
|
927
927
|
});
|
|
928
928
|
|
|
929
|
+
commands
|
|
930
|
+
.command('get-simulated-interest-and-fees')
|
|
931
|
+
.requiredOption('--vault <string>', 'Vault address')
|
|
932
|
+
.action(async ({ vault }) => {
|
|
933
|
+
const env = initializeClient(false, false);
|
|
934
|
+
|
|
935
|
+
const vaultAddress = new PublicKey(vault);
|
|
936
|
+
|
|
937
|
+
const kaminoManager = new KaminoManager(env.connection, env.kLendProgramId, env.kVaultProgramId);
|
|
938
|
+
const vaultState = await new KaminoVault(vaultAddress, undefined, env.kVaultProgramId).getState(env.connection);
|
|
939
|
+
|
|
940
|
+
const simulatedHoldings = await kaminoManager.calculateSimulatedHoldingsWithInterest(vaultState);
|
|
941
|
+
|
|
942
|
+
console.log('Simulated holdings with interest', simulatedHoldings);
|
|
943
|
+
|
|
944
|
+
const simulatedFees = await kaminoManager.calculateSimulatedFees(vaultState, simulatedHoldings);
|
|
945
|
+
|
|
946
|
+
console.log('Simulated fees', simulatedFees);
|
|
947
|
+
});
|
|
948
|
+
|
|
929
949
|
commands
|
|
930
950
|
.command('download-lending-market-config')
|
|
931
951
|
.requiredOption('--lending-market <string>', 'Lending Market Address')
|
|
@@ -36,6 +36,8 @@ export interface VaultStateFields {
|
|
|
36
36
|
name: Array<number>
|
|
37
37
|
vaultLookupTable: PublicKey
|
|
38
38
|
vaultFarm: PublicKey
|
|
39
|
+
creationTimestamp: BN
|
|
40
|
+
padding1: BN
|
|
39
41
|
padding2: Array<BN>
|
|
40
42
|
}
|
|
41
43
|
|
|
@@ -71,6 +73,8 @@ export interface VaultStateJSON {
|
|
|
71
73
|
name: Array<number>
|
|
72
74
|
vaultLookupTable: string
|
|
73
75
|
vaultFarm: string
|
|
76
|
+
creationTimestamp: string
|
|
77
|
+
padding1: string
|
|
74
78
|
padding2: Array<string>
|
|
75
79
|
}
|
|
76
80
|
|
|
@@ -106,6 +110,8 @@ export class VaultState {
|
|
|
106
110
|
readonly name: Array<number>
|
|
107
111
|
readonly vaultLookupTable: PublicKey
|
|
108
112
|
readonly vaultFarm: PublicKey
|
|
113
|
+
readonly creationTimestamp: BN
|
|
114
|
+
readonly padding1: BN
|
|
109
115
|
readonly padding2: Array<BN>
|
|
110
116
|
|
|
111
117
|
static readonly discriminator = Buffer.from([
|
|
@@ -144,7 +150,9 @@ export class VaultState {
|
|
|
144
150
|
borsh.array(borsh.u8(), 40, "name"),
|
|
145
151
|
borsh.publicKey("vaultLookupTable"),
|
|
146
152
|
borsh.publicKey("vaultFarm"),
|
|
147
|
-
borsh.
|
|
153
|
+
borsh.u64("creationTimestamp"),
|
|
154
|
+
borsh.u64("padding1"),
|
|
155
|
+
borsh.array(borsh.u128(), 244, "padding2"),
|
|
148
156
|
])
|
|
149
157
|
|
|
150
158
|
constructor(fields: VaultStateFields) {
|
|
@@ -181,6 +189,8 @@ export class VaultState {
|
|
|
181
189
|
this.name = fields.name
|
|
182
190
|
this.vaultLookupTable = fields.vaultLookupTable
|
|
183
191
|
this.vaultFarm = fields.vaultFarm
|
|
192
|
+
this.creationTimestamp = fields.creationTimestamp
|
|
193
|
+
this.padding1 = fields.padding1
|
|
184
194
|
this.padding2 = fields.padding2
|
|
185
195
|
}
|
|
186
196
|
|
|
@@ -263,6 +273,8 @@ export class VaultState {
|
|
|
263
273
|
name: dec.name,
|
|
264
274
|
vaultLookupTable: dec.vaultLookupTable,
|
|
265
275
|
vaultFarm: dec.vaultFarm,
|
|
276
|
+
creationTimestamp: dec.creationTimestamp,
|
|
277
|
+
padding1: dec.padding1,
|
|
266
278
|
padding2: dec.padding2,
|
|
267
279
|
})
|
|
268
280
|
}
|
|
@@ -302,6 +314,8 @@ export class VaultState {
|
|
|
302
314
|
name: this.name,
|
|
303
315
|
vaultLookupTable: this.vaultLookupTable.toString(),
|
|
304
316
|
vaultFarm: this.vaultFarm.toString(),
|
|
317
|
+
creationTimestamp: this.creationTimestamp.toString(),
|
|
318
|
+
padding1: this.padding1.toString(),
|
|
305
319
|
padding2: this.padding2.map((item) => item.toString()),
|
|
306
320
|
}
|
|
307
321
|
}
|
|
@@ -341,6 +355,8 @@ export class VaultState {
|
|
|
341
355
|
name: obj.name,
|
|
342
356
|
vaultLookupTable: new PublicKey(obj.vaultLookupTable),
|
|
343
357
|
vaultFarm: new PublicKey(obj.vaultFarm),
|
|
358
|
+
creationTimestamp: new BN(obj.creationTimestamp),
|
|
359
|
+
padding1: new BN(obj.padding1),
|
|
344
360
|
padding2: obj.padding2.map((item) => new BN(item)),
|
|
345
361
|
})
|
|
346
362
|
}
|