@kamino-finance/klend-sdk 5.10.9 → 5.10.11
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/README.md +26 -2
- package/dist/classes/manager.d.ts +9 -3
- package/dist/classes/manager.d.ts.map +1 -1
- package/dist/classes/manager.js +10 -2
- package/dist/classes/manager.js.map +1 -1
- package/dist/classes/market.d.ts.map +1 -1
- package/dist/classes/market.js +4 -1
- package/dist/classes/market.js.map +1 -1
- package/dist/classes/types.d.ts +7 -0
- package/dist/classes/types.d.ts.map +1 -1
- package/dist/classes/vault.d.ts +23 -11
- package/dist/classes/vault.d.ts.map +1 -1
- package/dist/classes/vault.js +141 -29
- package/dist/classes/vault.js.map +1 -1
- package/dist/client_kamino_manager.d.ts.map +1 -1
- package/dist/client_kamino_manager.js +5 -5
- package/dist/client_kamino_manager.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/errors/custom.d.ts +17 -1
- package/dist/idl_codegen_kamino_vault/errors/custom.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/errors/custom.js +29 -1
- package/dist/idl_codegen_kamino_vault/errors/custom.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/deposit.d.ts +0 -1
- package/dist/idl_codegen_kamino_vault/instructions/deposit.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/deposit.js +0 -5
- package/dist/idl_codegen_kamino_vault/instructions/deposit.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/updateReserveAllocation.d.ts +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/updateReserveAllocation.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/updateReserveAllocation.js +5 -1
- package/dist/idl_codegen_kamino_vault/instructions/updateReserveAllocation.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/ReserveConfig.d.ts +8 -5
- package/dist/idl_codegen_kamino_vault/types/ReserveConfig.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/ReserveConfig.js +9 -8
- package/dist/idl_codegen_kamino_vault/types/ReserveConfig.js.map +1 -1
- package/dist/utils/accountListing.d.ts +4 -0
- package/dist/utils/accountListing.d.ts.map +1 -0
- package/dist/utils/accountListing.js +37 -0
- package/dist/utils/accountListing.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/package.json +2 -1
- package/src/classes/manager.ts +12 -2
- package/src/classes/market.ts +7 -1
- package/src/classes/types.ts +9 -1
- package/src/classes/vault.ts +182 -32
- package/src/client.ts +11 -0
- package/src/client_kamino_manager.ts +12 -5
- package/src/idl_codegen_kamino_vault/errors/custom.ts +28 -0
- package/src/idl_codegen_kamino_vault/instructions/deposit.ts +0 -6
- package/src/idl_codegen_kamino_vault/instructions/updateReserveAllocation.ts +6 -2
- package/src/idl_codegen_kamino_vault/types/ReserveConfig.ts +17 -14
- package/src/idl_kamino_vault.json +17 -9
- package/src/utils/accountListing.ts +32 -0
- package/src/utils/index.ts +1 -0
package/src/classes/types.ts
CHANGED
|
@@ -4,12 +4,14 @@ import Decimal from 'decimal.js/decimal';
|
|
|
4
4
|
/** the populateLUTIxs should be executed in a separate transaction as we cannot create and populate a lookup table in the same tx */
|
|
5
5
|
export type InitVaultIxs = {
|
|
6
6
|
initVaultIxs: TransactionInstruction[];
|
|
7
|
+
createLUTIx: TransactionInstruction;
|
|
7
8
|
populateLUTIxs: TransactionInstruction[];
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
export type AcceptVaultOwnershipIxs = {
|
|
11
12
|
acceptVaultOwnershipIx: TransactionInstruction;
|
|
12
|
-
|
|
13
|
+
initNewLUTIx: TransactionInstruction;
|
|
14
|
+
updateLUTIxs: TransactionInstruction[]; // this has to be executed in a transaction after the initNewLUTIx is executed
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
export type UpdateReserveAllocationIxs = {
|
|
@@ -46,3 +48,9 @@ export type UserSharesForVault = {
|
|
|
46
48
|
stakedShares: Decimal;
|
|
47
49
|
totalShares: Decimal;
|
|
48
50
|
};
|
|
51
|
+
|
|
52
|
+
export type ReserveAllocationOverview = {
|
|
53
|
+
targetWeight: Decimal;
|
|
54
|
+
tokenAllocationCap: Decimal;
|
|
55
|
+
ctokenAllocation: Decimal;
|
|
56
|
+
};
|
package/src/classes/vault.ts
CHANGED
|
@@ -72,6 +72,7 @@ import {
|
|
|
72
72
|
AcceptVaultOwnershipIxs,
|
|
73
73
|
DepositIxs,
|
|
74
74
|
InitVaultIxs,
|
|
75
|
+
ReserveAllocationOverview,
|
|
75
76
|
SyncVaultLUTIxs,
|
|
76
77
|
UpdateReserveAllocationIxs,
|
|
77
78
|
UpdateVaultConfigIxs,
|
|
@@ -234,7 +235,7 @@ export class KaminoVaultClient {
|
|
|
234
235
|
lut.toString()
|
|
235
236
|
);
|
|
236
237
|
|
|
237
|
-
const ixns = [createVaultIx, initVaultIx,
|
|
238
|
+
const ixns = [createVaultIx, initVaultIx, setLUTIx];
|
|
238
239
|
|
|
239
240
|
if (vaultConfig.getPerformanceFeeBps() > 0) {
|
|
240
241
|
const setPerformanceFeeIx = this.updateUninitialisedVaultConfigIx(
|
|
@@ -264,7 +265,7 @@ export class KaminoVaultClient {
|
|
|
264
265
|
ixns.push(setNameIx);
|
|
265
266
|
}
|
|
266
267
|
|
|
267
|
-
return { vault: vaultState, initVaultIxs: { initVaultIxs: ixns, populateLUTIxs: insertIntoLUTIxs } };
|
|
268
|
+
return { vault: vaultState, initVaultIxs: { initVaultIxs: ixns, createLUTIx, populateLUTIxs: insertIntoLUTIxs } };
|
|
268
269
|
}
|
|
269
270
|
|
|
270
271
|
/**
|
|
@@ -295,7 +296,7 @@ export class KaminoVaultClient {
|
|
|
295
296
|
ctokenVault: cTokenVault,
|
|
296
297
|
systemProgram: SystemProgram.programId,
|
|
297
298
|
rent: SYSVAR_RENT_PUBKEY,
|
|
298
|
-
|
|
299
|
+
reserveCollateralTokenProgram: vaultState.tokenProgram,
|
|
299
300
|
};
|
|
300
301
|
|
|
301
302
|
const updateReserveAllocationArgs: UpdateReserveAllocationArgs = {
|
|
@@ -337,12 +338,14 @@ export class KaminoVaultClient {
|
|
|
337
338
|
* @param vault the vault to update
|
|
338
339
|
* @param mode the field to update (based on VaultConfigFieldKind enum)
|
|
339
340
|
* @param value the value to update the field with
|
|
341
|
+
* @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 batch multiple ixs in the same tx
|
|
340
342
|
* @returns a struct that contains the instruction to update the field and an optional list of instructions to update the lookup table
|
|
341
343
|
*/
|
|
342
344
|
async updateVaultConfigIxs(
|
|
343
345
|
vault: KaminoVault,
|
|
344
346
|
mode: VaultConfigFieldKind,
|
|
345
|
-
value: string
|
|
347
|
+
value: string,
|
|
348
|
+
signer?: PublicKey
|
|
346
349
|
): Promise<UpdateVaultConfigIxs> {
|
|
347
350
|
const vaultState: VaultState = await vault.getState(this.getConnection());
|
|
348
351
|
|
|
@@ -351,6 +354,9 @@ export class KaminoVaultClient {
|
|
|
351
354
|
vaultState: vault.address,
|
|
352
355
|
klendProgram: this._kaminoLendProgramId,
|
|
353
356
|
};
|
|
357
|
+
if (signer) {
|
|
358
|
+
updateVaultConfigAccs.adminAuthority = signer;
|
|
359
|
+
}
|
|
354
360
|
|
|
355
361
|
const updateVaultConfigArgs: UpdateVaultConfigArgs = {
|
|
356
362
|
entry: mode,
|
|
@@ -525,12 +531,12 @@ export class KaminoVaultClient {
|
|
|
525
531
|
|
|
526
532
|
const LUTIxs = [];
|
|
527
533
|
const [initNewLUTIx, newLUT] = initLookupTableIx(vaultState.pendingAdmin, await this.getConnection().getSlot());
|
|
528
|
-
LUTIxs.push(initNewLUTIx);
|
|
529
534
|
|
|
530
535
|
const insertIntoLUTIxs = await this.insertIntoLookupTableIxs(
|
|
531
536
|
vaultState.pendingAdmin,
|
|
532
537
|
newLUT,
|
|
533
|
-
accountsInExistentLUT
|
|
538
|
+
accountsInExistentLUT,
|
|
539
|
+
[]
|
|
534
540
|
);
|
|
535
541
|
|
|
536
542
|
LUTIxs.push(...insertIntoLUTIxs);
|
|
@@ -538,13 +544,15 @@ export class KaminoVaultClient {
|
|
|
538
544
|
const updateVaultConfigIxs = await this.updateVaultConfigIxs(
|
|
539
545
|
vault,
|
|
540
546
|
new VaultConfigField.LookupTable(),
|
|
541
|
-
newLUT.toString()
|
|
547
|
+
newLUT.toString(),
|
|
548
|
+
vaultState.pendingAdmin
|
|
542
549
|
);
|
|
543
550
|
LUTIxs.push(updateVaultConfigIxs.updateVaultConfigIx);
|
|
544
551
|
LUTIxs.push(...updateVaultConfigIxs.updateLUTIxs);
|
|
545
552
|
|
|
546
553
|
const acceptVaultOwnershipIxs: AcceptVaultOwnershipIxs = {
|
|
547
554
|
acceptVaultOwnershipIx,
|
|
555
|
+
initNewLUTIx,
|
|
548
556
|
updateLUTIxs: LUTIxs,
|
|
549
557
|
};
|
|
550
558
|
|
|
@@ -720,7 +728,6 @@ export class KaminoVaultClient {
|
|
|
720
728
|
userTokenAta: userTokenAta,
|
|
721
729
|
userSharesAta: userSharesAta,
|
|
722
730
|
tokenProgram: tokenProgramID,
|
|
723
|
-
instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
|
|
724
731
|
klendProgram: this._kaminoLendProgramId,
|
|
725
732
|
sharesTokenProgram: TOKEN_PROGRAM_ID,
|
|
726
733
|
};
|
|
@@ -802,7 +809,7 @@ export class KaminoVaultClient {
|
|
|
802
809
|
* This function will return a struct with the instructions to unstake from the farm if necessary and the instructions for the missing ATA creation instructions, as well as one or multiple withdraw instructions, based on how many reserves it's needed to withdraw from. This might have to be split in multiple transactions
|
|
803
810
|
* @param user - user to withdraw
|
|
804
811
|
* @param vault - vault to withdraw from
|
|
805
|
-
* @param shareAmount - share amount to withdraw, in order to withdraw everything, any value > user share amount
|
|
812
|
+
* @param shareAmount - share amount to withdraw (in tokens, not lamports), in order to withdraw everything, any value > user share amount
|
|
806
813
|
* @param slot - current slot, used to estimate the interest earned in the different reserves with allocation from the vault
|
|
807
814
|
* @param [vaultReservesMap] - optional parameter; a hashmap from each reserve pubkey to the reserve state. If provided the function will be significantly faster as it will not have to fetch the reserves
|
|
808
815
|
* @param [farmState] - the state of the vault farm, if the vault has a farm. Optional. If not provided, it will be fetched
|
|
@@ -839,8 +846,8 @@ export class KaminoVaultClient {
|
|
|
839
846
|
}
|
|
840
847
|
|
|
841
848
|
// if the vault has allocations withdraw otherwise wtihdraw from available ix
|
|
842
|
-
const vaultAllocation = vaultState.vaultAllocationStrategy.find(
|
|
843
|
-
allocation.reserve.equals(PublicKey.default)
|
|
849
|
+
const vaultAllocation = vaultState.vaultAllocationStrategy.find(
|
|
850
|
+
(allocation) => !allocation.reserve.equals(PublicKey.default)
|
|
844
851
|
);
|
|
845
852
|
|
|
846
853
|
if (vaultAllocation) {
|
|
@@ -990,7 +997,6 @@ export class KaminoVaultClient {
|
|
|
990
997
|
return withdrawIxns;
|
|
991
998
|
}
|
|
992
999
|
|
|
993
|
-
// todo: make sure we also check the ata of the investor for the vault token exists
|
|
994
1000
|
/**
|
|
995
1001
|
* This will trigger invest by balancing, based on weights, the reserve allocations of the vault. It can either withdraw or deposit into reserves to balance them. This is a function that should be cranked
|
|
996
1002
|
* @param payer wallet that pays the tx
|
|
@@ -998,20 +1004,87 @@ export class KaminoVaultClient {
|
|
|
998
1004
|
* @returns - an array of invest instructions for each invest action required for the vault reserves
|
|
999
1005
|
*/
|
|
1000
1006
|
async investAllReservesIxs(payer: PublicKey, vault: KaminoVault): Promise<TransactionInstruction[]> {
|
|
1001
|
-
//TODO: Order invest ixns by - invest that removes first, then invest that adds
|
|
1002
1007
|
const vaultState = await vault.getState(this.getConnection());
|
|
1003
|
-
const
|
|
1008
|
+
const allReserves = this.getAllVaultReserves(vaultState);
|
|
1009
|
+
if (allReserves.length === 0) {
|
|
1010
|
+
throw new Error('No reserves found for the vault, please select at least one reserve for the vault');
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
const [allReservesStateMap, computedReservesAllocation] = await Promise.all([
|
|
1014
|
+
this.loadVaultReserves(vaultState),
|
|
1015
|
+
this.getVaultComputedReservesAllocation(vaultState),
|
|
1016
|
+
]);
|
|
1017
|
+
|
|
1018
|
+
const tokenProgram = await getAccountOwner(this.getConnection(), vaultState.tokenMint);
|
|
1019
|
+
const [{ ata: _payerTokenAta, createAtaIx }] = createAtasIdempotent(payer, [
|
|
1020
|
+
{ mint: vaultState.tokenMint, tokenProgram },
|
|
1021
|
+
]);
|
|
1022
|
+
|
|
1023
|
+
// compute total vault holdings and expected distribution based on weights
|
|
1024
|
+
const curentVaultAllocations = this.getVaultAllocations(vaultState);
|
|
1025
|
+
|
|
1026
|
+
const reservesToDisinvestFrom: PublicKey[] = [];
|
|
1027
|
+
|
|
1028
|
+
for (let index = 0; index < allReserves.length; index++) {
|
|
1029
|
+
const reservePubkey = allReserves[index];
|
|
1030
|
+
const reserveState = allReservesStateMap.get(reservePubkey)!;
|
|
1031
|
+
const computedAllocation = computedReservesAllocation.get(reservePubkey)!;
|
|
1032
|
+
const currentCTokenAllocation = curentVaultAllocations.get(reservePubkey)!.ctokenAllocation;
|
|
1033
|
+
|
|
1034
|
+
const reserveCollExchangeRate = reserveState.getCollateralExchangeRate();
|
|
1035
|
+
const reserveAllocationLiquidityAmount = lamportsToDecimal(
|
|
1036
|
+
currentCTokenAllocation.div(reserveCollExchangeRate),
|
|
1037
|
+
vaultState.tokenMintDecimals.toNumber()
|
|
1038
|
+
);
|
|
1039
|
+
|
|
1040
|
+
if (computedAllocation.lt(reserveAllocationLiquidityAmount)) {
|
|
1041
|
+
reservesToDisinvestFrom.push(reservePubkey);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1004
1045
|
const investIxnsPromises: Promise<TransactionInstruction[]>[] = [];
|
|
1005
|
-
|
|
1006
|
-
|
|
1046
|
+
// invest first the reserves from which we disinvest, then the other ones
|
|
1047
|
+
for (const reserve of reservesToDisinvestFrom) {
|
|
1048
|
+
const reserveState = allReservesStateMap.get(reserve);
|
|
1007
1049
|
if (reserveState === null) {
|
|
1008
1050
|
throw new Error(`Reserve ${reserve.toBase58()} not found`);
|
|
1009
1051
|
}
|
|
1010
|
-
const investIxsPromise = this.investSingleReserveIxs(
|
|
1052
|
+
const investIxsPromise = this.investSingleReserveIxs(
|
|
1053
|
+
payer,
|
|
1054
|
+
vault,
|
|
1055
|
+
{
|
|
1056
|
+
address: reserve,
|
|
1057
|
+
state: reserveState!.state,
|
|
1058
|
+
},
|
|
1059
|
+
allReservesStateMap,
|
|
1060
|
+
false
|
|
1061
|
+
);
|
|
1011
1062
|
investIxnsPromises.push(investIxsPromise);
|
|
1012
1063
|
}
|
|
1013
1064
|
|
|
1014
|
-
const
|
|
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);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
let investIxns: TransactionInstruction[] = [];
|
|
1086
|
+
investIxns.push(createAtaIx);
|
|
1087
|
+
investIxns = await Promise.all(investIxnsPromises).then((ixns) => ixns.flat());
|
|
1015
1088
|
|
|
1016
1089
|
return investIxns;
|
|
1017
1090
|
}
|
|
@@ -1029,16 +1102,23 @@ export class KaminoVaultClient {
|
|
|
1029
1102
|
payer: PublicKey,
|
|
1030
1103
|
vault: KaminoVault,
|
|
1031
1104
|
reserve: ReserveWithAddress,
|
|
1032
|
-
vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve
|
|
1105
|
+
vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve>,
|
|
1106
|
+
createAtaIfNeeded: boolean = true
|
|
1033
1107
|
): Promise<TransactionInstruction[]> {
|
|
1108
|
+
console.log('create invest ix for reserve', reserve.address.toBase58());
|
|
1034
1109
|
const vaultState = await vault.getState(this.getConnection());
|
|
1035
1110
|
const cTokenVault = getCTokenVaultPda(vault.address, reserve.address, this._kaminoVaultProgramId);
|
|
1036
1111
|
const lendingMarketAuth = lendingMarketAuthPda(reserve.state.lendingMarket, this._kaminoLendProgramId)[0];
|
|
1037
1112
|
|
|
1113
|
+
const ixs: TransactionInstruction[] = [];
|
|
1114
|
+
|
|
1038
1115
|
const tokenProgram = await getAccountOwner(this.getConnection(), vaultState.tokenMint);
|
|
1039
1116
|
const [{ ata: payerTokenAta, createAtaIx }] = createAtasIdempotent(payer, [
|
|
1040
1117
|
{ mint: vaultState.tokenMint, tokenProgram },
|
|
1041
1118
|
]);
|
|
1119
|
+
if (createAtaIfNeeded) {
|
|
1120
|
+
ixs.push(createAtaIx);
|
|
1121
|
+
}
|
|
1042
1122
|
|
|
1043
1123
|
const investAccounts: InvestAccounts = {
|
|
1044
1124
|
payer,
|
|
@@ -1400,6 +1480,49 @@ export class KaminoVaultClient {
|
|
|
1400
1480
|
return ixns;
|
|
1401
1481
|
}
|
|
1402
1482
|
|
|
1483
|
+
/** Read the total holdings of a vault and the reserve weights and returns a map from each reserve to how many tokens should be deposited.
|
|
1484
|
+
* @param vaultState - the vault state to calculate the allocation for
|
|
1485
|
+
* @returns - a map from each reserve to how many tokens should be invested into
|
|
1486
|
+
*/
|
|
1487
|
+
async getVaultComputedReservesAllocation(vaultState: VaultState): Promise<PubkeyHashMap<PublicKey, Decimal>> {
|
|
1488
|
+
const holdings = await this.getVaultHoldings(vaultState);
|
|
1489
|
+
const initialVaultAllocations = this.getVaultAllocations(vaultState);
|
|
1490
|
+
|
|
1491
|
+
const allReserves = this.getAllVaultReserves(vaultState);
|
|
1492
|
+
|
|
1493
|
+
let totalAllocation = new Decimal(0);
|
|
1494
|
+
initialVaultAllocations.forEach((allocation) => {
|
|
1495
|
+
totalAllocation = totalAllocation.add(allocation.targetWeight);
|
|
1496
|
+
});
|
|
1497
|
+
const expectedHoldingsDistribution = new PubkeyHashMap<PublicKey, Decimal>();
|
|
1498
|
+
|
|
1499
|
+
let totalLeftToInvest = holdings.total;
|
|
1500
|
+
let currentAllocationSum = totalAllocation;
|
|
1501
|
+
const ZERO = new Decimal(0);
|
|
1502
|
+
while (totalLeftToInvest.gt(ZERO)) {
|
|
1503
|
+
const totalLeftover = totalLeftToInvest;
|
|
1504
|
+
for (const reserve of allReserves) {
|
|
1505
|
+
const reserveWithWeight = initialVaultAllocations.get(reserve);
|
|
1506
|
+
const targetAllocation = reserveWithWeight!.targetWeight.mul(totalLeftover).div(currentAllocationSum);
|
|
1507
|
+
const reserveCap = reserveWithWeight!.tokenAllocationCap;
|
|
1508
|
+
// todo: check if both target and reserveCap
|
|
1509
|
+
const amountToInvest = Decimal.min(targetAllocation, reserveCap, totalLeftToInvest);
|
|
1510
|
+
totalLeftToInvest = totalLeftToInvest.sub(amountToInvest);
|
|
1511
|
+
if (amountToInvest.eq(reserveCap)) {
|
|
1512
|
+
currentAllocationSum = currentAllocationSum.sub(reserveWithWeight!.targetWeight);
|
|
1513
|
+
}
|
|
1514
|
+
const reserveHasPreallocation = expectedHoldingsDistribution.has(reserve);
|
|
1515
|
+
if (reserveHasPreallocation) {
|
|
1516
|
+
expectedHoldingsDistribution.set(reserve, expectedHoldingsDistribution.get(reserve)!.add(amountToInvest));
|
|
1517
|
+
} else {
|
|
1518
|
+
expectedHoldingsDistribution.set(reserve, amountToInvest);
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
return expectedHoldingsDistribution;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1403
1526
|
/**
|
|
1404
1527
|
* This method returns the user shares balance for a given vault
|
|
1405
1528
|
* @param user - user to calculate the shares balance for
|
|
@@ -1652,7 +1775,7 @@ export class KaminoVaultClient {
|
|
|
1652
1775
|
/**
|
|
1653
1776
|
* This will return the a map between reserve pubkey and the pct of the vault invested amount in each reserve
|
|
1654
1777
|
* @param vaultState - the kamino vault to get reserves distribution for
|
|
1655
|
-
* @returns a
|
|
1778
|
+
* @returns a map between reserve pubkey and the allocation pct for the reserve
|
|
1656
1779
|
*/
|
|
1657
1780
|
getAllocationsDistribuionPct(vaultState: VaultState): PubkeyHashMap<PublicKey, Decimal> {
|
|
1658
1781
|
const allocationsDistributionPct = new PubkeyHashMap<PublicKey, Decimal>();
|
|
@@ -1661,7 +1784,6 @@ export class KaminoVaultClient {
|
|
|
1661
1784
|
const filteredAllocations = vaultState.vaultAllocationStrategy.filter(
|
|
1662
1785
|
(allocation) => !allocation.reserve.equals(PublicKey.default)
|
|
1663
1786
|
);
|
|
1664
|
-
console.log('filteredAllocations length', filteredAllocations.length);
|
|
1665
1787
|
filteredAllocations.forEach((allocation) => {
|
|
1666
1788
|
totalAllocation = totalAllocation.add(new Decimal(allocation.targetAllocationWeight.toString()));
|
|
1667
1789
|
});
|
|
@@ -1676,6 +1798,30 @@ export class KaminoVaultClient {
|
|
|
1676
1798
|
return allocationsDistributionPct;
|
|
1677
1799
|
}
|
|
1678
1800
|
|
|
1801
|
+
/**
|
|
1802
|
+
* This will return the a map between reserve pubkey and the allocation overview for the reserve
|
|
1803
|
+
* @param vaultState - the kamino vault to get reserves allocation overview for
|
|
1804
|
+
* @returns a map between reserve pubkey and the allocation overview for the reserve
|
|
1805
|
+
*/
|
|
1806
|
+
getVaultAllocations(vaultState: VaultState): PubkeyHashMap<PublicKey, ReserveAllocationOverview> {
|
|
1807
|
+
const vaultAllocations = new PubkeyHashMap<PublicKey, ReserveAllocationOverview>();
|
|
1808
|
+
|
|
1809
|
+
vaultState.vaultAllocationStrategy.map((allocation) => {
|
|
1810
|
+
if (allocation.reserve.equals(PublicKey.default)) {
|
|
1811
|
+
return;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
const allocationOverview: ReserveAllocationOverview = {
|
|
1815
|
+
targetWeight: new Decimal(allocation.targetAllocationWeight.toString()),
|
|
1816
|
+
tokenAllocationCap: new Decimal(allocation.tokenAllocationCap.toString()),
|
|
1817
|
+
ctokenAllocation: new Decimal(allocation.ctokenAllocation.toString()),
|
|
1818
|
+
};
|
|
1819
|
+
vaultAllocations.set(allocation.reserve, allocationOverview);
|
|
1820
|
+
});
|
|
1821
|
+
|
|
1822
|
+
return vaultAllocations;
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1679
1825
|
/**
|
|
1680
1826
|
* This will return an unsorted hash map of all reserves that the given vault has allocations for, toghether with the amount that can be withdrawn from each of the reserves
|
|
1681
1827
|
* @param vault - the kamino vault to get available liquidity to withdraw for
|
|
@@ -1728,7 +1874,11 @@ export class KaminoVaultClient {
|
|
|
1728
1874
|
* @returns a hashmap from each reserve pubkey to the reserve state
|
|
1729
1875
|
*/
|
|
1730
1876
|
getAllVaultReserves(vault: VaultState): PublicKey[] {
|
|
1731
|
-
return vault.vaultAllocationStrategy
|
|
1877
|
+
return vault.vaultAllocationStrategy
|
|
1878
|
+
.map((vaultAllocation) => vaultAllocation.reserve)
|
|
1879
|
+
.filter((reserve) => {
|
|
1880
|
+
return !reserve.equals(PublicKey.default);
|
|
1881
|
+
});
|
|
1732
1882
|
}
|
|
1733
1883
|
|
|
1734
1884
|
/**
|
|
@@ -2283,10 +2433,10 @@ export class KaminoVaultConfig {
|
|
|
2283
2433
|
readonly tokenMint: PublicKey;
|
|
2284
2434
|
/** The token mint program id */
|
|
2285
2435
|
readonly tokenMintProgramId: PublicKey;
|
|
2286
|
-
/** The performance fee rate of the vault, expressed as a decimal */
|
|
2287
|
-
readonly
|
|
2288
|
-
/** The management fee rate of the vault, expressed as a decimal */
|
|
2289
|
-
readonly
|
|
2436
|
+
/** The performance fee rate of the vault, as percents, expressed as a decimal */
|
|
2437
|
+
readonly performanceFeeRatePercentage: Decimal;
|
|
2438
|
+
/** The management fee rate of the vault, as percents, expressed as a decimal */
|
|
2439
|
+
readonly managementFeeRatePercentage: Decimal;
|
|
2290
2440
|
/** The name to be stored on cain for the vault (max 40 characters). */
|
|
2291
2441
|
readonly name: string;
|
|
2292
2442
|
|
|
@@ -2294,24 +2444,24 @@ export class KaminoVaultConfig {
|
|
|
2294
2444
|
admin: PublicKey;
|
|
2295
2445
|
tokenMint: PublicKey;
|
|
2296
2446
|
tokenMintProgramId: PublicKey;
|
|
2297
|
-
|
|
2298
|
-
|
|
2447
|
+
performanceFeeRatePercentage: Decimal;
|
|
2448
|
+
managementFeeRatePercentage: Decimal;
|
|
2299
2449
|
name: string;
|
|
2300
2450
|
}) {
|
|
2301
2451
|
this.admin = args.admin;
|
|
2302
2452
|
this.tokenMint = args.tokenMint;
|
|
2303
|
-
this.
|
|
2304
|
-
this.
|
|
2453
|
+
this.performanceFeeRatePercentage = args.performanceFeeRatePercentage;
|
|
2454
|
+
this.managementFeeRatePercentage = args.managementFeeRatePercentage;
|
|
2305
2455
|
this.tokenMintProgramId = args.tokenMintProgramId;
|
|
2306
2456
|
this.name = args.name;
|
|
2307
2457
|
}
|
|
2308
2458
|
|
|
2309
2459
|
getPerformanceFeeBps(): number {
|
|
2310
|
-
return this.
|
|
2460
|
+
return this.performanceFeeRatePercentage.mul(100).toNumber();
|
|
2311
2461
|
}
|
|
2312
2462
|
|
|
2313
2463
|
getManagementFeeBps(): number {
|
|
2314
|
-
return this.
|
|
2464
|
+
return this.managementFeeRatePercentage.mul(100).toNumber();
|
|
2315
2465
|
}
|
|
2316
2466
|
}
|
|
2317
2467
|
|
package/src/client.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getAllUserMetadatasWithFilter,
|
|
10
10
|
getProgramId,
|
|
11
11
|
toJson,
|
|
12
|
+
getAllObligationAccounts,
|
|
12
13
|
} from './lib';
|
|
13
14
|
import * as fs from 'fs';
|
|
14
15
|
import { Connection, GetProgramAccountsFilter, Keypair, PublicKey } from '@solana/web3.js';
|
|
@@ -65,6 +66,16 @@ async function main() {
|
|
|
65
66
|
console.log(toJson(kaminoObligation?.refreshedStats));
|
|
66
67
|
});
|
|
67
68
|
|
|
69
|
+
commands
|
|
70
|
+
.command('print-all-obligation-accounts')
|
|
71
|
+
.option(`--rpc <string>`, 'The RPC URL')
|
|
72
|
+
.action(async ({ rpc }) => {
|
|
73
|
+
const connection = new Connection(rpc, {});
|
|
74
|
+
for await (const obligationAccount of getAllObligationAccounts(connection)) {
|
|
75
|
+
console.log(toJson(obligationAccount.toJSON()));
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
68
79
|
commands
|
|
69
80
|
.command('print-reserve')
|
|
70
81
|
.option(`--url <string>`, 'The admin keypair file')
|
|
@@ -245,14 +245,21 @@ async function main() {
|
|
|
245
245
|
admin: mode === 'multisig' ? multisigPk : env.payer.publicKey,
|
|
246
246
|
tokenMint: tokenMint,
|
|
247
247
|
tokenMintProgramId: tokenProgramID,
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
performanceFeeRatePercentage: new Decimal(0.0),
|
|
249
|
+
managementFeeRatePercentage: new Decimal(0.0),
|
|
250
250
|
name,
|
|
251
251
|
});
|
|
252
252
|
|
|
253
253
|
const { vault: vaultKp, initVaultIxs: instructions } = await kaminoManager.createVaultIxs(kaminoVaultConfig);
|
|
254
254
|
|
|
255
|
-
const _createVaultSig = await processTxn(
|
|
255
|
+
const _createVaultSig = await processTxn(
|
|
256
|
+
env.client,
|
|
257
|
+
env.payer,
|
|
258
|
+
[...instructions.initVaultIxs, instructions.createLUTIx],
|
|
259
|
+
mode,
|
|
260
|
+
2500,
|
|
261
|
+
[vaultKp]
|
|
262
|
+
);
|
|
256
263
|
await sleep(5000);
|
|
257
264
|
const _populateLUTSig = await processTxn(env.client, env.payer, instructions.populateLUTIxs, mode, 2500, []);
|
|
258
265
|
|
|
@@ -327,7 +334,7 @@ async function main() {
|
|
|
327
334
|
[]
|
|
328
335
|
);
|
|
329
336
|
|
|
330
|
-
mode === 'execute' && console.log('
|
|
337
|
+
mode === 'execute' && console.log('Vault updated:', updateVaultPendingAdminSig);
|
|
331
338
|
});
|
|
332
339
|
|
|
333
340
|
commands
|
|
@@ -777,7 +784,7 @@ async function main() {
|
|
|
777
784
|
kaminoVault,
|
|
778
785
|
reserveWithAddress
|
|
779
786
|
);
|
|
780
|
-
const investReserveSig = await processTxn(env.client, env.payer, instructions, mode, 2500, [],
|
|
787
|
+
const investReserveSig = await processTxn(env.client, env.payer, instructions, mode, 2500, [], 800_000);
|
|
781
788
|
|
|
782
789
|
mode === 'execute' && console.log('Reserve invested:', investReserveSig);
|
|
783
790
|
});
|
|
@@ -41,6 +41,8 @@ export type CustomError =
|
|
|
41
41
|
| WithdrawResultsInZeroShares
|
|
42
42
|
| CannotWithdrawZeroShares
|
|
43
43
|
| ManagementFeeGreaterThanMaxAllowed
|
|
44
|
+
| VaultAUMZero
|
|
45
|
+
| MissingReserveForBatchRefresh
|
|
44
46
|
|
|
45
47
|
export class DepositAmountsZero extends Error {
|
|
46
48
|
static readonly code = 7000
|
|
@@ -504,6 +506,28 @@ export class ManagementFeeGreaterThanMaxAllowed extends Error {
|
|
|
504
506
|
}
|
|
505
507
|
}
|
|
506
508
|
|
|
509
|
+
export class VaultAUMZero extends Error {
|
|
510
|
+
static readonly code = 7042
|
|
511
|
+
readonly code = 7042
|
|
512
|
+
readonly name = "VaultAUMZero"
|
|
513
|
+
readonly msg = "Vault assets under management are empty"
|
|
514
|
+
|
|
515
|
+
constructor(readonly logs?: string[]) {
|
|
516
|
+
super("7042: Vault assets under management are empty")
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
export class MissingReserveForBatchRefresh extends Error {
|
|
521
|
+
static readonly code = 7043
|
|
522
|
+
readonly code = 7043
|
|
523
|
+
readonly name = "MissingReserveForBatchRefresh"
|
|
524
|
+
readonly msg = "Missing reserve for batch refresh"
|
|
525
|
+
|
|
526
|
+
constructor(readonly logs?: string[]) {
|
|
527
|
+
super("7043: Missing reserve for batch refresh")
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
507
531
|
export function fromCode(code: number, logs?: string[]): CustomError | null {
|
|
508
532
|
switch (code) {
|
|
509
533
|
case 7000:
|
|
@@ -590,6 +614,10 @@ export function fromCode(code: number, logs?: string[]): CustomError | null {
|
|
|
590
614
|
return new CannotWithdrawZeroShares(logs)
|
|
591
615
|
case 7041:
|
|
592
616
|
return new ManagementFeeGreaterThanMaxAllowed(logs)
|
|
617
|
+
case 7042:
|
|
618
|
+
return new VaultAUMZero(logs)
|
|
619
|
+
case 7043:
|
|
620
|
+
return new MissingReserveForBatchRefresh(logs)
|
|
593
621
|
}
|
|
594
622
|
|
|
595
623
|
return null
|
|
@@ -20,7 +20,6 @@ export interface DepositAccounts {
|
|
|
20
20
|
klendProgram: PublicKey
|
|
21
21
|
tokenProgram: PublicKey
|
|
22
22
|
sharesTokenProgram: PublicKey
|
|
23
|
-
instructionSysvarAccount: PublicKey
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
export const layout = borsh.struct([borsh.u64("maxAmount")])
|
|
@@ -42,11 +41,6 @@ export function deposit(
|
|
|
42
41
|
{ pubkey: accounts.klendProgram, isSigner: false, isWritable: false },
|
|
43
42
|
{ pubkey: accounts.tokenProgram, isSigner: false, isWritable: false },
|
|
44
43
|
{ pubkey: accounts.sharesTokenProgram, isSigner: false, isWritable: false },
|
|
45
|
-
{
|
|
46
|
-
pubkey: accounts.instructionSysvarAccount,
|
|
47
|
-
isSigner: false,
|
|
48
|
-
isWritable: false,
|
|
49
|
-
},
|
|
50
44
|
]
|
|
51
45
|
const identifier = Buffer.from([242, 35, 198, 137, 82, 225, 242, 182])
|
|
52
46
|
const buffer = Buffer.alloc(1000)
|
|
@@ -16,9 +16,9 @@ export interface UpdateReserveAllocationAccounts {
|
|
|
16
16
|
reserveCollateralMint: PublicKey
|
|
17
17
|
reserve: PublicKey
|
|
18
18
|
ctokenVault: PublicKey
|
|
19
|
+
reserveCollateralTokenProgram: PublicKey
|
|
19
20
|
systemProgram: PublicKey
|
|
20
21
|
rent: PublicKey
|
|
21
|
-
tokenProgram: PublicKey
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export const layout = borsh.struct([borsh.u64("weight"), borsh.u64("cap")])
|
|
@@ -39,9 +39,13 @@ export function updateReserveAllocation(
|
|
|
39
39
|
},
|
|
40
40
|
{ pubkey: accounts.reserve, isSigner: false, isWritable: false },
|
|
41
41
|
{ pubkey: accounts.ctokenVault, isSigner: false, isWritable: true },
|
|
42
|
+
{
|
|
43
|
+
pubkey: accounts.reserveCollateralTokenProgram,
|
|
44
|
+
isSigner: false,
|
|
45
|
+
isWritable: false,
|
|
46
|
+
},
|
|
42
47
|
{ pubkey: accounts.systemProgram, isSigner: false, isWritable: false },
|
|
43
48
|
{ pubkey: accounts.rent, isSigner: false, isWritable: false },
|
|
44
|
-
{ pubkey: accounts.tokenProgram, isSigner: false, isWritable: false },
|
|
45
49
|
]
|
|
46
50
|
const identifier = Buffer.from([5, 54, 213, 112, 75, 232, 117, 37])
|
|
47
51
|
const buffer = Buffer.alloc(1000)
|
|
@@ -60,7 +60,8 @@ export interface ReserveConfigFields {
|
|
|
60
60
|
debtWithdrawalCap: types.WithdrawalCapsFields
|
|
61
61
|
elevationGroups: Array<number>
|
|
62
62
|
disableUsageAsCollOutsideEmode: number
|
|
63
|
-
|
|
63
|
+
/** Utilization (in percentage) above which borrowing is blocked. 0 to disable. */
|
|
64
|
+
utilizationLimitBlockBorrowingAbovePct: number
|
|
64
65
|
/**
|
|
65
66
|
* Whether this reserve should be subject to auto-deleveraging after deposit or borrow limit is
|
|
66
67
|
* crossed.
|
|
@@ -147,7 +148,8 @@ export interface ReserveConfigJSON {
|
|
|
147
148
|
debtWithdrawalCap: types.WithdrawalCapsJSON
|
|
148
149
|
elevationGroups: Array<number>
|
|
149
150
|
disableUsageAsCollOutsideEmode: number
|
|
150
|
-
|
|
151
|
+
/** Utilization (in percentage) above which borrowing is blocked. 0 to disable. */
|
|
152
|
+
utilizationLimitBlockBorrowingAbovePct: number
|
|
151
153
|
/**
|
|
152
154
|
* Whether this reserve should be subject to auto-deleveraging after deposit or borrow limit is
|
|
153
155
|
* crossed.
|
|
@@ -235,7 +237,8 @@ export class ReserveConfig {
|
|
|
235
237
|
readonly debtWithdrawalCap: types.WithdrawalCaps
|
|
236
238
|
readonly elevationGroups: Array<number>
|
|
237
239
|
readonly disableUsageAsCollOutsideEmode: number
|
|
238
|
-
|
|
240
|
+
/** Utilization (in percentage) above which borrowing is blocked. 0 to disable. */
|
|
241
|
+
readonly utilizationLimitBlockBorrowingAbovePct: number
|
|
239
242
|
/**
|
|
240
243
|
* Whether this reserve should be subject to auto-deleveraging after deposit or borrow limit is
|
|
241
244
|
* crossed.
|
|
@@ -297,8 +300,8 @@ export class ReserveConfig {
|
|
|
297
300
|
})
|
|
298
301
|
this.elevationGroups = fields.elevationGroups
|
|
299
302
|
this.disableUsageAsCollOutsideEmode = fields.disableUsageAsCollOutsideEmode
|
|
300
|
-
this.
|
|
301
|
-
fields.
|
|
303
|
+
this.utilizationLimitBlockBorrowingAbovePct =
|
|
304
|
+
fields.utilizationLimitBlockBorrowingAbovePct
|
|
302
305
|
this.autodeleverageEnabled = fields.autodeleverageEnabled
|
|
303
306
|
this.reserved1 = fields.reserved1
|
|
304
307
|
this.borrowLimitOutsideElevationGroup =
|
|
@@ -336,7 +339,7 @@ export class ReserveConfig {
|
|
|
336
339
|
types.WithdrawalCaps.layout("debtWithdrawalCap"),
|
|
337
340
|
borsh.array(borsh.u8(), 20, "elevationGroups"),
|
|
338
341
|
borsh.u8("disableUsageAsCollOutsideEmode"),
|
|
339
|
-
borsh.u8("
|
|
342
|
+
borsh.u8("utilizationLimitBlockBorrowingAbovePct"),
|
|
340
343
|
borsh.u8("autodeleverageEnabled"),
|
|
341
344
|
borsh.array(borsh.u8(), 1, "reserved1"),
|
|
342
345
|
borsh.u64("borrowLimitOutsideElevationGroup"),
|
|
@@ -383,8 +386,8 @@ export class ReserveConfig {
|
|
|
383
386
|
),
|
|
384
387
|
elevationGroups: obj.elevationGroups,
|
|
385
388
|
disableUsageAsCollOutsideEmode: obj.disableUsageAsCollOutsideEmode,
|
|
386
|
-
|
|
387
|
-
obj.
|
|
389
|
+
utilizationLimitBlockBorrowingAbovePct:
|
|
390
|
+
obj.utilizationLimitBlockBorrowingAbovePct,
|
|
388
391
|
autodeleverageEnabled: obj.autodeleverageEnabled,
|
|
389
392
|
reserved1: obj.reserved1,
|
|
390
393
|
borrowLimitOutsideElevationGroup: obj.borrowLimitOutsideElevationGroup,
|
|
@@ -428,8 +431,8 @@ export class ReserveConfig {
|
|
|
428
431
|
),
|
|
429
432
|
elevationGroups: fields.elevationGroups,
|
|
430
433
|
disableUsageAsCollOutsideEmode: fields.disableUsageAsCollOutsideEmode,
|
|
431
|
-
|
|
432
|
-
fields.
|
|
434
|
+
utilizationLimitBlockBorrowingAbovePct:
|
|
435
|
+
fields.utilizationLimitBlockBorrowingAbovePct,
|
|
433
436
|
autodeleverageEnabled: fields.autodeleverageEnabled,
|
|
434
437
|
reserved1: fields.reserved1,
|
|
435
438
|
borrowLimitOutsideElevationGroup: fields.borrowLimitOutsideElevationGroup,
|
|
@@ -468,8 +471,8 @@ export class ReserveConfig {
|
|
|
468
471
|
debtWithdrawalCap: this.debtWithdrawalCap.toJSON(),
|
|
469
472
|
elevationGroups: this.elevationGroups,
|
|
470
473
|
disableUsageAsCollOutsideEmode: this.disableUsageAsCollOutsideEmode,
|
|
471
|
-
|
|
472
|
-
this.
|
|
474
|
+
utilizationLimitBlockBorrowingAbovePct:
|
|
475
|
+
this.utilizationLimitBlockBorrowingAbovePct,
|
|
473
476
|
autodeleverageEnabled: this.autodeleverageEnabled,
|
|
474
477
|
reserved1: this.reserved1,
|
|
475
478
|
borrowLimitOutsideElevationGroup:
|
|
@@ -515,8 +518,8 @@ export class ReserveConfig {
|
|
|
515
518
|
debtWithdrawalCap: types.WithdrawalCaps.fromJSON(obj.debtWithdrawalCap),
|
|
516
519
|
elevationGroups: obj.elevationGroups,
|
|
517
520
|
disableUsageAsCollOutsideEmode: obj.disableUsageAsCollOutsideEmode,
|
|
518
|
-
|
|
519
|
-
obj.
|
|
521
|
+
utilizationLimitBlockBorrowingAbovePct:
|
|
522
|
+
obj.utilizationLimitBlockBorrowingAbovePct,
|
|
520
523
|
autodeleverageEnabled: obj.autodeleverageEnabled,
|
|
521
524
|
reserved1: obj.reserved1,
|
|
522
525
|
borrowLimitOutsideElevationGroup: new BN(
|