@kamino-finance/klend-sdk 5.10.10 → 5.10.12
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 -28
- 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/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 -31
- package/src/client.ts +11 -0
- package/src/client_kamino_manager.ts +12 -5
- package/src/utils/accountListing.ts +32 -0
- package/src/utils/index.ts +1 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getAllObligationAccounts = getAllObligationAccounts;
|
|
7
|
+
const accounts_1 = require("../idl_codegen/accounts");
|
|
8
|
+
const programId_1 = require("../idl_codegen/programId");
|
|
9
|
+
const bs58_1 = __importDefault(require("bs58"));
|
|
10
|
+
async function* getAllObligationAccounts(connection) {
|
|
11
|
+
// Poor-man's paging...
|
|
12
|
+
for (let i = 0; i < 256; i++) {
|
|
13
|
+
const obligations = await connection.getProgramAccounts(programId_1.PROGRAM_ID, {
|
|
14
|
+
filters: [
|
|
15
|
+
{
|
|
16
|
+
dataSize: accounts_1.Obligation.layout.span + 8,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
memcmp: {
|
|
20
|
+
offset: 0,
|
|
21
|
+
bytes: bs58_1.default.encode(accounts_1.Obligation.discriminator),
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
memcmp: {
|
|
26
|
+
offset: 64,
|
|
27
|
+
bytes: bs58_1.default.encode([i]), // ...via sharding by userId's first byte (just as a source of randomness)
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
});
|
|
32
|
+
for (const obligation of obligations) {
|
|
33
|
+
yield accounts_1.Obligation.decode(obligation.account.data);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=accountListing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accountListing.js","sourceRoot":"","sources":["../../src/utils/accountListing.ts"],"names":[],"mappings":";;;;;AAKA,4DA0BC;AA9BD,sDAAqD;AACrD,wDAAsD;AACtD,gDAAwB;AAEjB,KAAK,SAAS,CAAC,CAAC,wBAAwB,CAAC,UAAsB;IACpE,uBAAuB;IACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC,kBAAkB,CAAC,sBAAU,EAAE;YAClE,OAAO,EAAE;gBACP;oBACE,QAAQ,EAAE,qBAAU,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC;iBACrC;gBACD;oBACE,MAAM,EAAE;wBACN,MAAM,EAAE,CAAC;wBACT,KAAK,EAAE,cAAI,CAAC,MAAM,CAAC,qBAAU,CAAC,aAAa,CAAC;qBAC7C;iBACF;gBACD;oBACE,MAAM,EAAE;wBACN,MAAM,EAAE,EAAE;wBACV,KAAK,EAAE,cAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,0EAA0E;qBACpG;iBACF;aACF;SACF,CAAC,CAAC;QACH,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;YACrC,MAAM,qBAAU,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAC;AACtB,cAAc,OAAO,CAAC;AACtB,cAAc,aAAa,CAAC;AAC5B,cAAc,OAAO,CAAC;AACtB,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,OAAO,CAAC;AACtB,cAAc,OAAO,CAAC;AACtB,cAAc,aAAa,CAAC;AAC5B,cAAc,OAAO,CAAC;AACtB,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC"}
|
package/dist/utils/index.js
CHANGED
|
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./accountListing"), exports);
|
|
17
18
|
__exportStar(require("./api"), exports);
|
|
18
19
|
__exportStar(require("./ata"), exports);
|
|
19
20
|
__exportStar(require("./constants"), exports);
|
package/dist/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,wCAAsB;AACtB,wCAAsB;AACtB,8CAA4B;AAC5B,wCAAsB;AACtB,gDAA8B;AAC9B,mDAAiC;AACjC,0CAAwB;AACxB,0CAAwB;AACxB,iDAA+B;AAC/B,2CAAyB;AACzB,2CAAyB;AACzB,gDAA8B;AAC9B,0DAAwC;AACxC,iDAA+B;AAC/B,yCAAuB;AACvB,wCAAsB"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,mDAAiC;AACjC,wCAAsB;AACtB,wCAAsB;AACtB,8CAA4B;AAC5B,wCAAsB;AACtB,gDAA8B;AAC9B,mDAAiC;AACjC,0CAAwB;AACxB,0CAAwB;AACxB,iDAA+B;AAC/B,2CAAyB;AACzB,2CAAyB;AACzB,gDAA8B;AAC9B,0DAAwC;AACxC,iDAA+B;AAC/B,yCAAuB;AACvB,wCAAsB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kamino-finance/klend-sdk",
|
|
3
|
-
"version": "5.10.
|
|
3
|
+
"version": "5.10.12",
|
|
4
4
|
"description": "Typescript SDK for interacting with the Kamino Lending (klend) protocol",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"start-validator": "solana-test-validator $(./deps/test-validator-params.sh)",
|
|
46
46
|
"start-validator-and-test": "yarn start-server-and-test 'yarn start-validator' http://127.0.0.1:8899/health test",
|
|
47
47
|
"dump-programs": "./deps/dump-from-mainnet.sh",
|
|
48
|
+
"cli": "tsx --no-deprecation src/client.ts",
|
|
48
49
|
"kamino-manager": "tsx src/client_kamino_manager.ts",
|
|
49
50
|
"dev": "concurrently \"yarn build:watch\" \"nodemon --watch dist -e js,ts --exec 'yarn dev:push-and-update'\"",
|
|
50
51
|
"dev:push-and-update": "yalc publish && cd examples && yalc update"
|
package/src/classes/manager.ts
CHANGED
|
@@ -71,6 +71,7 @@ import {
|
|
|
71
71
|
AcceptVaultOwnershipIxs,
|
|
72
72
|
DepositIxs,
|
|
73
73
|
InitVaultIxs,
|
|
74
|
+
ReserveAllocationOverview,
|
|
74
75
|
SyncVaultLUTIxs,
|
|
75
76
|
UpdateReserveAllocationIxs,
|
|
76
77
|
UpdateVaultConfigIxs,
|
|
@@ -416,7 +417,7 @@ export class KaminoManager {
|
|
|
416
417
|
* This function will return 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
|
|
417
418
|
* @param user - user to withdraw
|
|
418
419
|
* @param vault - vault to withdraw from
|
|
419
|
-
* @param shareAmount - share amount to withdraw, in order to withdraw everything, any value > user share amount
|
|
420
|
+
* @param shareAmount - share amount to withdraw (in tokens, not lamports), in order to withdraw everything, any value > user share amount
|
|
420
421
|
* @param slot - current slot, used to estimate the interest earned in the different reserves with allocation from the vault
|
|
421
422
|
* @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
|
|
422
423
|
* @returns an array of instructions to create missing ATAs if needed and the withdraw instructions
|
|
@@ -853,12 +854,21 @@ export class KaminoManager {
|
|
|
853
854
|
/**
|
|
854
855
|
* This will return the a map between reserve pubkey and the pct of the vault invested amount in each reserve
|
|
855
856
|
* @param vaultState - the kamino vault to get reserves distribution for
|
|
856
|
-
* @returns a
|
|
857
|
+
* @returns a map between reserve pubkey and the allocation pct for the reserve
|
|
857
858
|
*/
|
|
858
859
|
getAllocationsDistribuionPct(vaultState: VaultState): PubkeyHashMap<PublicKey, Decimal> {
|
|
859
860
|
return this._vaultClient.getAllocationsDistribuionPct(vaultState);
|
|
860
861
|
}
|
|
861
862
|
|
|
863
|
+
/**
|
|
864
|
+
* This will return the a map between reserve pubkey and the allocation overview for the reserve
|
|
865
|
+
* @param vaultState - the kamino vault to get reserves allocation overview for
|
|
866
|
+
* @returns a map between reserve pubkey and the allocation overview for the reserve
|
|
867
|
+
*/
|
|
868
|
+
getVaultAllocations(vaultState: VaultState): PubkeyHashMap<PublicKey, ReserveAllocationOverview> {
|
|
869
|
+
return this._vaultClient.getVaultAllocations(vaultState);
|
|
870
|
+
}
|
|
871
|
+
|
|
862
872
|
/**
|
|
863
873
|
* This will return the amount of token invested from the vault into the given reserve
|
|
864
874
|
* @param vault - the kamino vault to get invested amount in reserve for
|
package/src/classes/market.ts
CHANGED
|
@@ -265,7 +265,13 @@ export class KaminoMarket {
|
|
|
265
265
|
|
|
266
266
|
const groupsColl = new Set(collReserve.state.config.elevationGroups);
|
|
267
267
|
const groupsDebt = new Set(debtReserve.state.config.elevationGroups);
|
|
268
|
-
const commonElevationGroups = [...groupsColl].filter(
|
|
268
|
+
const commonElevationGroups = [...groupsColl].filter(
|
|
269
|
+
(item) =>
|
|
270
|
+
groupsDebt.has(item) &&
|
|
271
|
+
item !== 0 &&
|
|
272
|
+
this.state.elevationGroups[item - 1].allowNewLoans !== 0 &&
|
|
273
|
+
collReserve.state.config.borrowLimitAgainstThisCollateralInElevationGroup[item - 1].gt(new BN(0))
|
|
274
|
+
);
|
|
269
275
|
|
|
270
276
|
// Ltv factor for coll token
|
|
271
277
|
const maxCollateralLtv =
|
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
|
-
reserveCollateralTokenProgram: vaultState.tokenProgram
|
|
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
|
|
|
@@ -801,7 +809,7 @@ export class KaminoVaultClient {
|
|
|
801
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
|
|
802
810
|
* @param user - user to withdraw
|
|
803
811
|
* @param vault - vault to withdraw from
|
|
804
|
-
* @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
|
|
805
813
|
* @param slot - current slot, used to estimate the interest earned in the different reserves with allocation from the vault
|
|
806
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
|
|
807
815
|
* @param [farmState] - the state of the vault farm, if the vault has a farm. Optional. If not provided, it will be fetched
|
|
@@ -838,8 +846,8 @@ export class KaminoVaultClient {
|
|
|
838
846
|
}
|
|
839
847
|
|
|
840
848
|
// if the vault has allocations withdraw otherwise wtihdraw from available ix
|
|
841
|
-
const vaultAllocation = vaultState.vaultAllocationStrategy.find(
|
|
842
|
-
allocation.reserve.equals(PublicKey.default)
|
|
849
|
+
const vaultAllocation = vaultState.vaultAllocationStrategy.find(
|
|
850
|
+
(allocation) => !allocation.reserve.equals(PublicKey.default)
|
|
843
851
|
);
|
|
844
852
|
|
|
845
853
|
if (vaultAllocation) {
|
|
@@ -989,7 +997,6 @@ export class KaminoVaultClient {
|
|
|
989
997
|
return withdrawIxns;
|
|
990
998
|
}
|
|
991
999
|
|
|
992
|
-
// todo: make sure we also check the ata of the investor for the vault token exists
|
|
993
1000
|
/**
|
|
994
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
|
|
995
1002
|
* @param payer wallet that pays the tx
|
|
@@ -997,20 +1004,87 @@ export class KaminoVaultClient {
|
|
|
997
1004
|
* @returns - an array of invest instructions for each invest action required for the vault reserves
|
|
998
1005
|
*/
|
|
999
1006
|
async investAllReservesIxs(payer: PublicKey, vault: KaminoVault): Promise<TransactionInstruction[]> {
|
|
1000
|
-
//TODO: Order invest ixns by - invest that removes first, then invest that adds
|
|
1001
1007
|
const vaultState = await vault.getState(this.getConnection());
|
|
1002
|
-
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
|
+
|
|
1003
1045
|
const investIxnsPromises: Promise<TransactionInstruction[]>[] = [];
|
|
1004
|
-
|
|
1005
|
-
|
|
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);
|
|
1006
1049
|
if (reserveState === null) {
|
|
1007
1050
|
throw new Error(`Reserve ${reserve.toBase58()} not found`);
|
|
1008
1051
|
}
|
|
1009
|
-
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
|
+
);
|
|
1010
1062
|
investIxnsPromises.push(investIxsPromise);
|
|
1011
1063
|
}
|
|
1012
1064
|
|
|
1013
|
-
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());
|
|
1014
1088
|
|
|
1015
1089
|
return investIxns;
|
|
1016
1090
|
}
|
|
@@ -1028,16 +1102,23 @@ export class KaminoVaultClient {
|
|
|
1028
1102
|
payer: PublicKey,
|
|
1029
1103
|
vault: KaminoVault,
|
|
1030
1104
|
reserve: ReserveWithAddress,
|
|
1031
|
-
vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve
|
|
1105
|
+
vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve>,
|
|
1106
|
+
createAtaIfNeeded: boolean = true
|
|
1032
1107
|
): Promise<TransactionInstruction[]> {
|
|
1108
|
+
console.log('create invest ix for reserve', reserve.address.toBase58());
|
|
1033
1109
|
const vaultState = await vault.getState(this.getConnection());
|
|
1034
1110
|
const cTokenVault = getCTokenVaultPda(vault.address, reserve.address, this._kaminoVaultProgramId);
|
|
1035
1111
|
const lendingMarketAuth = lendingMarketAuthPda(reserve.state.lendingMarket, this._kaminoLendProgramId)[0];
|
|
1036
1112
|
|
|
1113
|
+
const ixs: TransactionInstruction[] = [];
|
|
1114
|
+
|
|
1037
1115
|
const tokenProgram = await getAccountOwner(this.getConnection(), vaultState.tokenMint);
|
|
1038
1116
|
const [{ ata: payerTokenAta, createAtaIx }] = createAtasIdempotent(payer, [
|
|
1039
1117
|
{ mint: vaultState.tokenMint, tokenProgram },
|
|
1040
1118
|
]);
|
|
1119
|
+
if (createAtaIfNeeded) {
|
|
1120
|
+
ixs.push(createAtaIx);
|
|
1121
|
+
}
|
|
1041
1122
|
|
|
1042
1123
|
const investAccounts: InvestAccounts = {
|
|
1043
1124
|
payer,
|
|
@@ -1399,6 +1480,49 @@ export class KaminoVaultClient {
|
|
|
1399
1480
|
return ixns;
|
|
1400
1481
|
}
|
|
1401
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
|
+
|
|
1402
1526
|
/**
|
|
1403
1527
|
* This method returns the user shares balance for a given vault
|
|
1404
1528
|
* @param user - user to calculate the shares balance for
|
|
@@ -1651,7 +1775,7 @@ export class KaminoVaultClient {
|
|
|
1651
1775
|
/**
|
|
1652
1776
|
* This will return the a map between reserve pubkey and the pct of the vault invested amount in each reserve
|
|
1653
1777
|
* @param vaultState - the kamino vault to get reserves distribution for
|
|
1654
|
-
* @returns a
|
|
1778
|
+
* @returns a map between reserve pubkey and the allocation pct for the reserve
|
|
1655
1779
|
*/
|
|
1656
1780
|
getAllocationsDistribuionPct(vaultState: VaultState): PubkeyHashMap<PublicKey, Decimal> {
|
|
1657
1781
|
const allocationsDistributionPct = new PubkeyHashMap<PublicKey, Decimal>();
|
|
@@ -1660,7 +1784,6 @@ export class KaminoVaultClient {
|
|
|
1660
1784
|
const filteredAllocations = vaultState.vaultAllocationStrategy.filter(
|
|
1661
1785
|
(allocation) => !allocation.reserve.equals(PublicKey.default)
|
|
1662
1786
|
);
|
|
1663
|
-
console.log('filteredAllocations length', filteredAllocations.length);
|
|
1664
1787
|
filteredAllocations.forEach((allocation) => {
|
|
1665
1788
|
totalAllocation = totalAllocation.add(new Decimal(allocation.targetAllocationWeight.toString()));
|
|
1666
1789
|
});
|
|
@@ -1675,6 +1798,30 @@ export class KaminoVaultClient {
|
|
|
1675
1798
|
return allocationsDistributionPct;
|
|
1676
1799
|
}
|
|
1677
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
|
+
|
|
1678
1825
|
/**
|
|
1679
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
|
|
1680
1827
|
* @param vault - the kamino vault to get available liquidity to withdraw for
|
|
@@ -1727,7 +1874,11 @@ export class KaminoVaultClient {
|
|
|
1727
1874
|
* @returns a hashmap from each reserve pubkey to the reserve state
|
|
1728
1875
|
*/
|
|
1729
1876
|
getAllVaultReserves(vault: VaultState): PublicKey[] {
|
|
1730
|
-
return vault.vaultAllocationStrategy
|
|
1877
|
+
return vault.vaultAllocationStrategy
|
|
1878
|
+
.map((vaultAllocation) => vaultAllocation.reserve)
|
|
1879
|
+
.filter((reserve) => {
|
|
1880
|
+
return !reserve.equals(PublicKey.default);
|
|
1881
|
+
});
|
|
1731
1882
|
}
|
|
1732
1883
|
|
|
1733
1884
|
/**
|
|
@@ -2282,10 +2433,10 @@ export class KaminoVaultConfig {
|
|
|
2282
2433
|
readonly tokenMint: PublicKey;
|
|
2283
2434
|
/** The token mint program id */
|
|
2284
2435
|
readonly tokenMintProgramId: PublicKey;
|
|
2285
|
-
/** The performance fee rate of the vault, expressed as a decimal */
|
|
2286
|
-
readonly
|
|
2287
|
-
/** The management fee rate of the vault, expressed as a decimal */
|
|
2288
|
-
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;
|
|
2289
2440
|
/** The name to be stored on cain for the vault (max 40 characters). */
|
|
2290
2441
|
readonly name: string;
|
|
2291
2442
|
|
|
@@ -2293,24 +2444,24 @@ export class KaminoVaultConfig {
|
|
|
2293
2444
|
admin: PublicKey;
|
|
2294
2445
|
tokenMint: PublicKey;
|
|
2295
2446
|
tokenMintProgramId: PublicKey;
|
|
2296
|
-
|
|
2297
|
-
|
|
2447
|
+
performanceFeeRatePercentage: Decimal;
|
|
2448
|
+
managementFeeRatePercentage: Decimal;
|
|
2298
2449
|
name: string;
|
|
2299
2450
|
}) {
|
|
2300
2451
|
this.admin = args.admin;
|
|
2301
2452
|
this.tokenMint = args.tokenMint;
|
|
2302
|
-
this.
|
|
2303
|
-
this.
|
|
2453
|
+
this.performanceFeeRatePercentage = args.performanceFeeRatePercentage;
|
|
2454
|
+
this.managementFeeRatePercentage = args.managementFeeRatePercentage;
|
|
2304
2455
|
this.tokenMintProgramId = args.tokenMintProgramId;
|
|
2305
2456
|
this.name = args.name;
|
|
2306
2457
|
}
|
|
2307
2458
|
|
|
2308
2459
|
getPerformanceFeeBps(): number {
|
|
2309
|
-
return this.
|
|
2460
|
+
return this.performanceFeeRatePercentage.mul(100).toNumber();
|
|
2310
2461
|
}
|
|
2311
2462
|
|
|
2312
2463
|
getManagementFeeBps(): number {
|
|
2313
|
-
return this.
|
|
2464
|
+
return this.managementFeeRatePercentage.mul(100).toNumber();
|
|
2314
2465
|
}
|
|
2315
2466
|
}
|
|
2316
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
|
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Connection } from '@solana/web3.js';
|
|
2
|
+
import { Obligation } from '../idl_codegen/accounts';
|
|
3
|
+
import { PROGRAM_ID } from '../idl_codegen/programId';
|
|
4
|
+
import bs58 from 'bs58';
|
|
5
|
+
|
|
6
|
+
export async function* getAllObligationAccounts(connection: Connection): AsyncGenerator<Obligation, void, unknown> {
|
|
7
|
+
// Poor-man's paging...
|
|
8
|
+
for (let i = 0; i < 256; i++) {
|
|
9
|
+
const obligations = await connection.getProgramAccounts(PROGRAM_ID, {
|
|
10
|
+
filters: [
|
|
11
|
+
{
|
|
12
|
+
dataSize: Obligation.layout.span + 8,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
memcmp: {
|
|
16
|
+
offset: 0,
|
|
17
|
+
bytes: bs58.encode(Obligation.discriminator),
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
memcmp: {
|
|
22
|
+
offset: 64,
|
|
23
|
+
bytes: bs58.encode([i]), // ...via sharding by userId's first byte (just as a source of randomness)
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
});
|
|
28
|
+
for (const obligation of obligations) {
|
|
29
|
+
yield Obligation.decode(obligation.account.data);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/utils/index.ts
CHANGED