@kamino-finance/klend-sdk 5.10.27 → 5.10.28-beta.0

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.
Files changed (95) hide show
  1. package/dist/classes/index.d.ts +3 -0
  2. package/dist/classes/index.d.ts.map +1 -1
  3. package/dist/classes/index.js +3 -0
  4. package/dist/classes/index.js.map +1 -1
  5. package/dist/classes/stakePool.d.ts +8 -0
  6. package/dist/classes/stakePool.d.ts.map +1 -0
  7. package/dist/classes/stakePool.js +18 -0
  8. package/dist/classes/stakePool.js.map +1 -0
  9. package/dist/classes/standardStakePool.d.ts +255 -0
  10. package/dist/classes/standardStakePool.d.ts.map +1 -0
  11. package/dist/classes/standardStakePool.js +275 -0
  12. package/dist/classes/standardStakePool.js.map +1 -0
  13. package/dist/classes/types.d.ts +13 -1
  14. package/dist/classes/types.d.ts.map +1 -1
  15. package/dist/classes/unstakingPool.d.ts +111 -0
  16. package/dist/classes/unstakingPool.d.ts.map +1 -0
  17. package/dist/classes/unstakingPool.js +437 -0
  18. package/dist/classes/unstakingPool.js.map +1 -0
  19. package/dist/idl_codegen_unstaking_pool/accounts/PoolState.d.ts +50 -0
  20. package/dist/idl_codegen_unstaking_pool/accounts/PoolState.d.ts.map +1 -0
  21. package/dist/idl_codegen_unstaking_pool/accounts/PoolState.js +156 -0
  22. package/dist/idl_codegen_unstaking_pool/accounts/PoolState.js.map +1 -0
  23. package/dist/idl_codegen_unstaking_pool/accounts/index.d.ts +3 -0
  24. package/dist/idl_codegen_unstaking_pool/accounts/index.d.ts.map +1 -0
  25. package/dist/idl_codegen_unstaking_pool/accounts/index.js +6 -0
  26. package/dist/idl_codegen_unstaking_pool/accounts/index.js.map +1 -0
  27. package/dist/idl_codegen_unstaking_pool/errors/anchor.d.ts +435 -0
  28. package/dist/idl_codegen_unstaking_pool/errors/anchor.d.ts.map +1 -0
  29. package/dist/idl_codegen_unstaking_pool/errors/anchor.js +767 -0
  30. package/dist/idl_codegen_unstaking_pool/errors/anchor.js.map +1 -0
  31. package/dist/idl_codegen_unstaking_pool/errors/custom.d.ts +163 -0
  32. package/dist/idl_codegen_unstaking_pool/errors/custom.d.ts.map +1 -0
  33. package/dist/idl_codegen_unstaking_pool/errors/custom.js +290 -0
  34. package/dist/idl_codegen_unstaking_pool/errors/custom.js.map +1 -0
  35. package/dist/idl_codegen_unstaking_pool/errors/index.d.ts +6 -0
  36. package/dist/idl_codegen_unstaking_pool/errors/index.d.ts.map +1 -0
  37. package/dist/idl_codegen_unstaking_pool/errors/index.js +75 -0
  38. package/dist/idl_codegen_unstaking_pool/errors/index.js.map +1 -0
  39. package/dist/idl_codegen_unstaking_pool/instructions/burn.d.ts +21 -0
  40. package/dist/idl_codegen_unstaking_pool/instructions/burn.d.ts.map +1 -0
  41. package/dist/idl_codegen_unstaking_pool/instructions/burn.js +73 -0
  42. package/dist/idl_codegen_unstaking_pool/instructions/burn.js.map +1 -0
  43. package/dist/idl_codegen_unstaking_pool/instructions/collect.d.ts +16 -0
  44. package/dist/idl_codegen_unstaking_pool/instructions/collect.d.ts.map +1 -0
  45. package/dist/idl_codegen_unstaking_pool/instructions/collect.js +29 -0
  46. package/dist/idl_codegen_unstaking_pool/instructions/collect.js.map +1 -0
  47. package/dist/idl_codegen_unstaking_pool/instructions/index.d.ts +11 -0
  48. package/dist/idl_codegen_unstaking_pool/instructions/index.d.ts.map +1 -0
  49. package/dist/idl_codegen_unstaking_pool/instructions/index.js +14 -0
  50. package/dist/idl_codegen_unstaking_pool/instructions/index.js.map +1 -0
  51. package/dist/idl_codegen_unstaking_pool/instructions/initializePool.d.ts +15 -0
  52. package/dist/idl_codegen_unstaking_pool/instructions/initializePool.d.ts.map +1 -0
  53. package/dist/idl_codegen_unstaking_pool/instructions/initializePool.js +28 -0
  54. package/dist/idl_codegen_unstaking_pool/instructions/initializePool.js.map +1 -0
  55. package/dist/idl_codegen_unstaking_pool/instructions/mint.d.ts +21 -0
  56. package/dist/idl_codegen_unstaking_pool/instructions/mint.d.ts.map +1 -0
  57. package/dist/idl_codegen_unstaking_pool/instructions/mint.js +77 -0
  58. package/dist/idl_codegen_unstaking_pool/instructions/mint.js.map +1 -0
  59. package/dist/idl_codegen_unstaking_pool/instructions/updatePoolConfig.d.ts +13 -0
  60. package/dist/idl_codegen_unstaking_pool/instructions/updatePoolConfig.d.ts.map +1 -0
  61. package/dist/idl_codegen_unstaking_pool/instructions/updatePoolConfig.js +58 -0
  62. package/dist/idl_codegen_unstaking_pool/instructions/updatePoolConfig.js.map +1 -0
  63. package/dist/idl_codegen_unstaking_pool/programId.d.ts +4 -0
  64. package/dist/idl_codegen_unstaking_pool/programId.d.ts.map +1 -0
  65. package/dist/idl_codegen_unstaking_pool/programId.js +9 -0
  66. package/dist/idl_codegen_unstaking_pool/programId.js.map +1 -0
  67. package/dist/idl_codegen_unstaking_pool/types/PoolConfigField.d.ts +32 -0
  68. package/dist/idl_codegen_unstaking_pool/types/PoolConfigField.d.ts.map +1 -0
  69. package/dist/idl_codegen_unstaking_pool/types/PoolConfigField.js +105 -0
  70. package/dist/idl_codegen_unstaking_pool/types/PoolConfigField.js.map +1 -0
  71. package/dist/idl_codegen_unstaking_pool/types/index.d.ts +5 -0
  72. package/dist/idl_codegen_unstaking_pool/types/index.d.ts.map +1 -0
  73. package/dist/idl_codegen_unstaking_pool/types/index.js +39 -0
  74. package/dist/idl_codegen_unstaking_pool/types/index.js.map +1 -0
  75. package/package.json +4 -1
  76. package/src/classes/index.ts +3 -0
  77. package/src/classes/stakePool.ts +21 -0
  78. package/src/classes/standardStakePool.ts +361 -0
  79. package/src/classes/types.ts +17 -1
  80. package/src/classes/unstakingPool.ts +565 -0
  81. package/src/idl_codegen_unstaking_pool/accounts/PoolState.ts +163 -0
  82. package/src/idl_codegen_unstaking_pool/accounts/index.ts +2 -0
  83. package/src/idl_codegen_unstaking_pool/errors/anchor.ts +764 -0
  84. package/src/idl_codegen_unstaking_pool/errors/custom.ts +288 -0
  85. package/src/idl_codegen_unstaking_pool/errors/index.ts +49 -0
  86. package/src/idl_codegen_unstaking_pool/instructions/burn.ts +60 -0
  87. package/src/idl_codegen_unstaking_pool/instructions/collect.ts +43 -0
  88. package/src/idl_codegen_unstaking_pool/instructions/index.ts +10 -0
  89. package/src/idl_codegen_unstaking_pool/instructions/initializePool.ts +41 -0
  90. package/src/idl_codegen_unstaking_pool/instructions/mint.ts +64 -0
  91. package/src/idl_codegen_unstaking_pool/instructions/updatePoolConfig.ts +40 -0
  92. package/src/idl_codegen_unstaking_pool/programId.ts +7 -0
  93. package/src/idl_codegen_unstaking_pool/types/PoolConfigField.ts +85 -0
  94. package/src/idl_codegen_unstaking_pool/types/index.ts +6 -0
  95. package/src/idl_unstaking_pool.json +456 -0
@@ -0,0 +1,565 @@
1
+ import {
2
+ AccountMeta,
3
+ AddressLookupTableProgram,
4
+ Connection,
5
+ GetProgramAccountsResponse,
6
+ Keypair,
7
+ PublicKey,
8
+ SystemProgram,
9
+ SYSVAR_CLOCK_PUBKEY,
10
+ SYSVAR_INSTRUCTIONS_PUBKEY,
11
+ SYSVAR_RENT_PUBKEY,
12
+ TransactionInstruction,
13
+ } from '@solana/web3.js';
14
+ import {
15
+ initializePool,
16
+ InitializePoolAccounts,
17
+ updatePoolConfig,
18
+ UpdatePoolConfigAccounts,
19
+ UpdatePoolConfigArgs,
20
+ collect,
21
+ CollectAccounts,
22
+ burn,
23
+ BurnAccounts,
24
+ BurnArgs,
25
+ mint,
26
+ MintAccounts,
27
+ MintArgs,
28
+ } from '../idl_codegen_unstaking_pool/instructions';
29
+ import { ASSOCIATED_TOKEN_PROGRAM_ID, NATIVE_MINT, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@solana/spl-token';
30
+ import { getAssociatedTokenAddress, WRAPPED_SOL_MINT } from '../lib';
31
+ import { PoolState } from '../idl_codegen_unstaking_pool/accounts';
32
+ import { PublicKeySet } from '../utils';
33
+ import bs58 from 'bs58';
34
+ import { getProgramAccounts } from '../utils/rpc';
35
+ import { InitPoolIxs, MintIxs, SyncVaultLUTIxs, UpdatePoolConfigIxs } from './types';
36
+ import { PoolConfigField, PoolConfigFieldKind } from '../idl_codegen_unstaking_pool/types';
37
+ import BN from 'bn.js';
38
+ import { STAKE_POOL_PROGRAM_ID } from '@solana/spl-stake-pool';
39
+ import { mapStakedSolMintToPool, StakePoolType } from './stakePool';
40
+ import { getStandardPoolMintRemainingAccounts, StakeAccount } from './standardStakePool';
41
+ export const unstakingPoolId = new PublicKey('USo1uB8RsRuM8y8e8vbL3mwR22EzSTLyZqaJPoZvn3a');
42
+ export const unstakingPoolStagingId = new PublicKey('STuRAYCqtoJ15dASjaypTtgqDB7h7fLgiNBuWt17ZG5');
43
+ export const STAKE_PROGRAM_ID = new PublicKey('Stake11111111111111111111111111111111111111');
44
+ export const CLOCK_PROGRAM_ID = new PublicKey('SysvarC1ock11111111111111111111111111111111');
45
+ const STAKE_HISTORY_PROGRAM_ID = new PublicKey('SysvarStakeHistory1111111111111111111111111');
46
+
47
+ const BASE_POOL_AUTHORITY_SEED = 'authority';
48
+ const UNSTAKING_SOL_MINT_SEED = 'unstaking_sol_mint';
49
+
50
+ /**
51
+ * KaminoPoolClient is a class that provides a high-level interface to interact with the Kamino Pool program.
52
+ */
53
+ export class UnstakingPoolClient {
54
+ private readonly _connection: Connection;
55
+ private readonly _unstakingPoolProgramId: PublicKey;
56
+
57
+ constructor(connection: Connection, unstakingPoolprogramId?: PublicKey) {
58
+ this._connection = connection;
59
+ this._unstakingPoolProgramId = unstakingPoolprogramId ? unstakingPoolprogramId : unstakingPoolId;
60
+ }
61
+
62
+ getConnection() {
63
+ return this._connection;
64
+ }
65
+
66
+ getProgramID() {
67
+ return this._unstakingPoolProgramId;
68
+ }
69
+
70
+ /**
71
+ * This method will create a pool with a given config. The config can be changed later on, but it is recommended to set it up correctly from the start
72
+ * @param poolConfig - the config object used to create a pool
73
+ * @returns pool - keypair, should be used to sign the transaction which creates the pool account
74
+ * @returns pool: the keypair of the pool, used to sign the initialization transaction; initPoolIxs: a struct with ixs to initialize the pool and its lookup table + populateLUTIxs, a list to populate the lookup table which has to be executed in a separate transaction
75
+ */
76
+ async createPoolIxs(poolConfig: UnstakingPoolConfig): Promise<{ pool: Keypair; initPoolIxs: InitPoolIxs }> {
77
+ const poolState = Keypair.generate();
78
+ const size = PoolState.layout.span + 8;
79
+
80
+ const createPoolIx = SystemProgram.createAccount({
81
+ fromPubkey: poolConfig.admin,
82
+ newAccountPubkey: poolState.publicKey,
83
+ lamports: await this._connection.getMinimumBalanceForRentExemption(size),
84
+ space: size,
85
+ programId: this._unstakingPoolProgramId,
86
+ });
87
+
88
+ const unstakingSolMint = PublicKey.findProgramAddressSync(
89
+ [Buffer.from(UNSTAKING_SOL_MINT_SEED), poolState.publicKey.toBytes()],
90
+ this._unstakingPoolProgramId
91
+ )[0];
92
+
93
+ const basePoolAuthority = PublicKey.findProgramAddressSync(
94
+ [Buffer.from(BASE_POOL_AUTHORITY_SEED), poolState.publicKey.toBytes()],
95
+ this._unstakingPoolProgramId
96
+ )[0];
97
+
98
+ const wsolVault = getAssociatedTokenAddress(WRAPPED_SOL_MINT, basePoolAuthority);
99
+
100
+ const initPoolAccounts: InitializePoolAccounts = {
101
+ admin: poolConfig.admin,
102
+ poolState: poolState.publicKey,
103
+ basePoolAuthority,
104
+ systemProgram: SystemProgram.programId,
105
+ rent: SYSVAR_RENT_PUBKEY,
106
+ tokenProgram: TOKEN_PROGRAM_ID,
107
+ associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
108
+ unstakingSolMint,
109
+ wsolMint: WRAPPED_SOL_MINT,
110
+ wsolVault,
111
+ };
112
+ const initPoolIx = initializePool(initPoolAccounts, this._unstakingPoolProgramId);
113
+
114
+ // create and set up the pool lookup table
115
+ const slot = await this._connection.getSlot();
116
+ const [createLUTIx, lut] = this.getInitLookupTableIx(poolConfig.admin, slot);
117
+
118
+ const allAccountsToBeInserted = [
119
+ poolState.publicKey,
120
+ basePoolAuthority,
121
+ wsolVault,
122
+ unstakingSolMint,
123
+ poolConfig.admin,
124
+ NATIVE_MINT,
125
+ this._unstakingPoolProgramId,
126
+ SystemProgram.programId,
127
+ SYSVAR_RENT_PUBKEY,
128
+ TOKEN_PROGRAM_ID,
129
+ TOKEN_2022_PROGRAM_ID,
130
+ SYSVAR_INSTRUCTIONS_PUBKEY,
131
+ SYSVAR_CLOCK_PUBKEY,
132
+ STAKE_PROGRAM_ID,
133
+ STAKE_POOL_PROGRAM_ID,
134
+ ];
135
+ const insertIntoLUTIxs = await this.insertIntoLookupTableIxs(poolConfig.admin, lut, allAccountsToBeInserted, []);
136
+ const { updatePoolConfigIx: updateLUTIx } = await this.updatePoolConfigIxs(
137
+ poolState.publicKey,
138
+ poolConfig.admin,
139
+ new PoolConfigField.LookupTable(),
140
+ lut.toString()
141
+ );
142
+ const ixns = [createPoolIx, initPoolIx, createLUTIx, ...insertIntoLUTIxs, updateLUTIx];
143
+
144
+ if (poolConfig.actionAuthority) {
145
+ const { updatePoolConfigIx: updateActionAuthorityIx } = await this.updatePoolConfigIxs(
146
+ poolState.publicKey,
147
+ poolConfig.admin,
148
+ new PoolConfigField.ActionAuthority(),
149
+ poolConfig.actionAuthority.toString()
150
+ );
151
+ ixns.push(updateActionAuthorityIx);
152
+ }
153
+
154
+ return { pool: poolState, initPoolIxs: { initPoolIxs: ixns, populateLUTIxs: [] } };
155
+ }
156
+
157
+ /**
158
+ * Update pool configuration such as admin authority (or fees/minimum depositable in the future)
159
+ * @param poolState - the pool to update and set the LUT for if needed or only the pool pubkey if updating LUT is not needed
160
+ * @param admin - admin of the specified pool
161
+ * @param mode - what field to update for pool
162
+ * @param value - new value that is converted .toString()
163
+ * @returns a struct that contains a list of ix to update the pool config + a list of ixs to insert all new accounts in the LUT
164
+ */
165
+ async updatePoolConfigIxs(
166
+ poolState: UnstakingPool | PublicKey,
167
+ admin: PublicKey,
168
+ mode: PoolConfigFieldKind,
169
+ value: string
170
+ ): Promise<UpdatePoolConfigIxs> {
171
+ const updatePoolConfigAccounts: UpdatePoolConfigAccounts = {
172
+ admin,
173
+ poolState: poolState instanceof UnstakingPool ? poolState.address : poolState,
174
+ };
175
+ const args: UpdatePoolConfigArgs = {
176
+ entry: mode,
177
+ data: Buffer.from([0]),
178
+ };
179
+
180
+ if (isNaN(+value)) {
181
+ const data = new PublicKey(value);
182
+ args.data = data.toBuffer();
183
+ } else {
184
+ const buffer = Buffer.alloc(8);
185
+ buffer.writeBigUInt64LE(BigInt(value.toString()));
186
+ args.data = buffer;
187
+ }
188
+
189
+ const updatePoolConfigIx = updatePoolConfig(args, updatePoolConfigAccounts, this._unstakingPoolProgramId);
190
+
191
+ const updateLUTIxs: TransactionInstruction[] = [];
192
+
193
+ if (poolState instanceof UnstakingPool && poolState.state) {
194
+ if (mode.kind === new PoolConfigField.ActionAuthority().kind) {
195
+ const newPubkey = new PublicKey(value);
196
+ const insertIntoLutIxs = await this.insertIntoLookupTableIxs(
197
+ poolState.state.actionAuthority,
198
+ poolState.state.poolLookupTable,
199
+ [newPubkey]
200
+ );
201
+ updateLUTIxs.push(...insertIntoLutIxs);
202
+ }
203
+ }
204
+
205
+ return { updatePoolConfigIx, updateLUTIxs };
206
+ }
207
+
208
+ /**
209
+ * Collect a stake account SOL if the needed epoch was reached
210
+ * @param poolState - the pool to collect SOL into
211
+ * @param payer - payer for the operation (ix is permissionless)
212
+ * @param stakeAccount - stake account that was deactivated this epoch and has base pool authority as owner
213
+ * @returns collect instruction
214
+ */
215
+ async collectIx(
216
+ poolState: UnstakingPool,
217
+ payer: PublicKey,
218
+ stakeAccount: PublicKey
219
+ ): Promise<TransactionInstruction> {
220
+ const pool = await poolState.getState(this.getConnection());
221
+ const accounts: CollectAccounts = {
222
+ poolState: poolState.address,
223
+ payer,
224
+ stakeAccount,
225
+ basePoolAuthority: pool.basePoolAuthority,
226
+ wsolVault: pool.wsolVault,
227
+ wsolMint: NATIVE_MINT,
228
+ tokenProgram: TOKEN_PROGRAM_ID,
229
+ systemProgram: SystemProgram.programId,
230
+ clockProgramId: SYSVAR_CLOCK_PUBKEY,
231
+ stakeProgramId: STAKE_PROGRAM_ID,
232
+ stakeHistoryProgramId: STAKE_HISTORY_PROGRAM_ID,
233
+ };
234
+ return collect(accounts, this._unstakingPoolProgramId);
235
+ }
236
+
237
+ /**
238
+ * Burn a number of shares (USOL) in exchange for SOL
239
+ * @param poolState - the pool to burn USOL from
240
+ * @param user - user that burns (ix is not gated by action authority)
241
+ * @param sharesToBurn - number of shares that are equivalent 1:1 with SOL
242
+ * @returns burn instruction
243
+ */
244
+ async burnIx(poolState: UnstakingPool, user: PublicKey, sharesToBurn: BN): Promise<TransactionInstruction> {
245
+ const pool = await poolState.getState(this.getConnection());
246
+ const accounts: BurnAccounts = {
247
+ poolState: poolState.address,
248
+ basePoolAuthority: pool.basePoolAuthority,
249
+ wsolVault: pool.wsolVault,
250
+ wsolMint: NATIVE_MINT,
251
+ systemProgram: SystemProgram.programId,
252
+ user,
253
+ userWsolToken: getAssociatedTokenAddress(NATIVE_MINT, user),
254
+ userUnstakingSolToken: getAssociatedTokenAddress(pool.unstakingSolMint, user),
255
+ unstakingSolMint: pool.unstakingSolMint,
256
+ unstakingSolTokenProgram: TOKEN_PROGRAM_ID,
257
+ wsolTokenProgram: TOKEN_PROGRAM_ID,
258
+ };
259
+ const args: BurnArgs = {
260
+ sharesToBurn,
261
+ };
262
+ return burn(args, accounts, this._unstakingPoolProgramId);
263
+ }
264
+
265
+ /**
266
+ * Mints a number of unstaking sol (USOL) in exchange for staked SOL
267
+ * NOTE: this ix is permissioned by action authority
268
+ * @param poolState - the pool to mint USOL from
269
+ * @param user - user that mints
270
+ * @param stakedSolMint - staked sol mint
271
+ * @param stakedSolToDeposit - staked sol to convert to USOL (at the pool ratio)
272
+ * @returns burn instruction
273
+ */
274
+ async mintIx(
275
+ poolState: UnstakingPool,
276
+ user: PublicKey,
277
+ stakedSolMint: PublicKey,
278
+ stakedSolToDeposit: BN
279
+ ): Promise<MintIxs> {
280
+ const pool = await poolState.getState(this.getConnection());
281
+ const [stakedSolPool, stakedSolPoolPk, stakePoolType] = await mapStakedSolMintToPool(
282
+ this.getConnection(),
283
+ stakedSolMint
284
+ );
285
+ const accounts: MintAccounts = {
286
+ poolState: poolState.address,
287
+ basePoolAuthority: pool.basePoolAuthority,
288
+ systemProgram: SystemProgram.programId,
289
+ unstakingSolMint: pool.unstakingSolMint,
290
+ unstakingSolTokenProgram: TOKEN_PROGRAM_ID,
291
+ user,
292
+ actionAuthority: pool.actionAuthority,
293
+ userStakedSolToken: getAssociatedTokenAddress(stakedSolMint, user),
294
+ userUnstakingSolToken: getAssociatedTokenAddress(pool.unstakingSolMint, user),
295
+ stakedSolMint,
296
+ stakedSolTokenProgram: stakedSolPool.tokenProgramId,
297
+ };
298
+ const args: MintArgs = {
299
+ stakedSolToDeposit,
300
+ };
301
+ const txn = mint(args, accounts, this._unstakingPoolProgramId);
302
+ let remainingAccounts: AccountMeta[] = [];
303
+ let remainingSigners: Keypair[] = [];
304
+ switch (stakePoolType) {
305
+ case StakePoolType.Standard:
306
+ [remainingAccounts, remainingSigners] = await getStandardPoolMintRemainingAccounts(
307
+ this.getConnection(),
308
+ stakedSolPool,
309
+ stakedSolPoolPk,
310
+ stakedSolToDeposit
311
+ );
312
+ }
313
+ txn.keys = txn.keys.concat(remainingAccounts);
314
+ return { mintIx: txn, additionalSigners: remainingSigners };
315
+ }
316
+
317
+ /**
318
+ * Sync a pool for lookup table; create and set the LUT for the pool if needed and fill it with all the needed accounts
319
+ * @param pool the pool to sync and set the LUT for if needed
320
+ * @param poolReserves optional; the state of the reserves in the pool allocation
321
+ * @returns a struct that contains a list of ix to create the LUT and assign it to the pool if needed + a list of ixs to insert all the accounts in the LUT
322
+ */
323
+ async syncPoolLookupTable(pool: UnstakingPool): Promise<SyncVaultLUTIxs> {
324
+ const poolState = await pool.getState(this._connection);
325
+ const allAccountsToBeInserted = [
326
+ pool.address,
327
+ poolState.basePoolAuthority,
328
+ poolState.wsolVault,
329
+ poolState.unstakingSolMint,
330
+ poolState.actionAuthority,
331
+ poolState.admin,
332
+ this._unstakingPoolProgramId,
333
+ SystemProgram.programId,
334
+ SYSVAR_RENT_PUBKEY,
335
+ TOKEN_PROGRAM_ID,
336
+ TOKEN_2022_PROGRAM_ID,
337
+ SYSVAR_INSTRUCTIONS_PUBKEY,
338
+ SYSVAR_CLOCK_PUBKEY,
339
+ STAKE_PROGRAM_ID,
340
+ STAKE_POOL_PROGRAM_ID,
341
+ ];
342
+
343
+ const setupLUTIfNeededIxs: TransactionInstruction[] = [];
344
+ let lut = poolState.poolLookupTable;
345
+ if (lut.equals(PublicKey.default)) {
346
+ const recentSlot = await this._connection.getSlot();
347
+ const [ixn, address] = this.getInitLookupTableIx(poolState.admin, recentSlot);
348
+ setupLUTIfNeededIxs.push(ixn);
349
+ lut = address;
350
+
351
+ // set the new LUT for the pool
352
+ const updatePoolConfigIxs = await this.updatePoolConfigIxs(
353
+ pool,
354
+ pool.address,
355
+ new PoolConfigField.LookupTable(),
356
+ lut.toString()
357
+ );
358
+ setupLUTIfNeededIxs.push(updatePoolConfigIxs.updatePoolConfigIx);
359
+ }
360
+
361
+ const ixns: TransactionInstruction[] = [];
362
+ let overridenExistentAccounts: PublicKey[] | undefined = undefined;
363
+ if (poolState.poolLookupTable.equals(PublicKey.default)) {
364
+ overridenExistentAccounts = [];
365
+ }
366
+ ixns.push(
367
+ ...(await this.insertIntoLookupTableIxs(poolState.admin, lut, allAccountsToBeInserted, overridenExistentAccounts))
368
+ );
369
+
370
+ return {
371
+ setupLUTIfNeededIxs,
372
+ syncLUTIxs: ixns,
373
+ };
374
+ }
375
+
376
+ getInitLookupTableIx(payer: PublicKey, slot: number): [TransactionInstruction, PublicKey] {
377
+ const [ixn, address] = AddressLookupTableProgram.createLookupTable({
378
+ authority: payer,
379
+ payer,
380
+ recentSlot: slot,
381
+ });
382
+
383
+ return [ixn, address];
384
+ }
385
+
386
+ async insertIntoLookupTableIxs(
387
+ payer: PublicKey,
388
+ lookupTable: PublicKey,
389
+ keys: PublicKey[],
390
+ accountsInLUT?: PublicKey[]
391
+ ): Promise<TransactionInstruction[]> {
392
+ let lutContents = accountsInLUT;
393
+ if (!accountsInLUT) {
394
+ lutContents = await this.getAccountsInLUT(lookupTable);
395
+ } else {
396
+ lutContents = accountsInLUT;
397
+ }
398
+
399
+ const missingAccounts = keys.filter((key) => !lutContents!.includes(key) && !key.equals(PublicKey.default));
400
+ // deduplicate missing accounts and remove default accounts and convert it back to an array
401
+ const missingAccountsList = new PublicKeySet(missingAccounts).toArray();
402
+
403
+ const chunkSize = 20;
404
+ const ixns: TransactionInstruction[] = [];
405
+
406
+ for (let i = 0; i < missingAccountsList.length; i += chunkSize) {
407
+ const chunk = missingAccountsList.slice(i, i + chunkSize);
408
+ const ixn = AddressLookupTableProgram.extendLookupTable({
409
+ lookupTable,
410
+ authority: payer,
411
+ payer,
412
+ addresses: chunk,
413
+ });
414
+ ixns.push(ixn);
415
+ }
416
+
417
+ return ixns;
418
+ }
419
+
420
+ /**
421
+ *
422
+ * @param connection
423
+ * @param lookupTable
424
+ * @returns
425
+ */
426
+ async getAccountsInLUT(lookupTable: PublicKey): Promise<PublicKey[]> {
427
+ const lutState = await this._connection.getAddressLookupTable(lookupTable);
428
+ if (!lutState || !lutState.value) {
429
+ throw new Error(`Lookup table ${lookupTable.toString()} not found`);
430
+ }
431
+
432
+ return lutState.value.state.addresses;
433
+ }
434
+
435
+ deactivateLookupTableIx(payer: PublicKey, lookupTable: PublicKey): TransactionInstruction {
436
+ const ixn = AddressLookupTableProgram.deactivateLookupTable({
437
+ authority: payer,
438
+ lookupTable: lookupTable,
439
+ });
440
+
441
+ return ixn;
442
+ }
443
+
444
+ /// this require the LUT to be deactivated at least 500 blocks before
445
+ closeLookupTableIx(payer: PublicKey, lookupTable: PublicKey): TransactionInstruction {
446
+ const ixn = AddressLookupTableProgram.closeLookupTable({
447
+ authority: payer,
448
+ recipient: payer,
449
+ lookupTable: lookupTable,
450
+ });
451
+
452
+ return ixn;
453
+ }
454
+
455
+ /**
456
+ * Get all pools
457
+ * @returns an array of all pools
458
+ */
459
+ async getAllPools(): Promise<UnstakingPool[]> {
460
+ const filters = [
461
+ {
462
+ dataSize: PoolState.layout.span + 8,
463
+ },
464
+ {
465
+ memcmp: {
466
+ offset: 0,
467
+ bytes: bs58.encode(PoolState.discriminator),
468
+ },
469
+ },
470
+ ];
471
+
472
+ const unstakingPools: GetProgramAccountsResponse = await getProgramAccounts(
473
+ this._connection,
474
+ this._unstakingPoolProgramId,
475
+ PoolState.layout.span + 8,
476
+ {
477
+ commitment: this._connection.commitment ?? 'processed',
478
+ filters,
479
+ }
480
+ );
481
+
482
+ return unstakingPools.map((unstakingPool) => {
483
+ if (unstakingPool.account === null) {
484
+ throw new Error(`unstakingPool with pubkey ${unstakingPool.pubkey.toString()} does not exist`);
485
+ }
486
+
487
+ const unstakingPoolAccount = PoolState.decode(unstakingPool.account.data);
488
+ if (!unstakingPoolAccount) {
489
+ throw Error(`unstakingPool with pubkey ${unstakingPool.pubkey.toString()} could not be decoded`);
490
+ }
491
+
492
+ return new UnstakingPool(unstakingPool.pubkey, unstakingPoolAccount, this._unstakingPoolProgramId);
493
+ });
494
+ }
495
+ } // UnstakingPoolClient
496
+
497
+ export class UnstakingPool {
498
+ readonly address: PublicKey;
499
+ state: PoolState | undefined | null;
500
+ programId: PublicKey;
501
+
502
+ constructor(poolAddress: PublicKey, state?: PoolState, programId: PublicKey = unstakingPoolId) {
503
+ this.address = poolAddress;
504
+ this.state = state;
505
+ this.programId = programId;
506
+ }
507
+
508
+ async getState(connection: Connection): Promise<PoolState> {
509
+ if (!this.state) {
510
+ const res = await PoolState.fetch(connection, this.address, this.programId);
511
+ if (!res) {
512
+ throw new Error(`Invalid pool ${this.address.toString()}`);
513
+ }
514
+ this.state = res;
515
+ return res;
516
+ } else {
517
+ return this.state;
518
+ }
519
+ }
520
+
521
+ async reloadState(connection: Connection): Promise<PoolState> {
522
+ this.state = await PoolState.fetch(connection, this.address, this.programId);
523
+ if (!this.state) {
524
+ throw new Error(`Could not fetch pool ${this.address.toString()}`);
525
+ }
526
+ return this.state;
527
+ }
528
+
529
+ async getStakeAccountsForPool(connection: Connection): Promise<Array<[StakeAccount, PublicKey]>> {
530
+ if (!this.state) {
531
+ throw new Error('Need to have pool state to fetch stake accounts');
532
+ }
533
+ // Filter only accounts that have withdraw authority the base pool authority
534
+ // and are delegating
535
+ const results = await connection.getParsedProgramAccounts(STAKE_PROGRAM_ID, {
536
+ filters: [
537
+ { memcmp: { offset: 0, bytes: bs58.encode([2]) } },
538
+ { memcmp: { offset: 44, bytes: this.state.basePoolAuthority.toBase58() } },
539
+ ],
540
+ });
541
+ return results
542
+ .filter(result => 'parsed' in result.account.data)
543
+ .map(result => {
544
+ if(!('parsed' in result.account.data)) {
545
+ throw new Error('Already filtered above for these, this error should not happen!');
546
+ }
547
+ return [result.account.data.parsed, result.pubkey];
548
+ })
549
+ }
550
+ }
551
+
552
+ /**
553
+ * Used to initialize a Kamino Pool
554
+ */
555
+ export class UnstakingPoolConfig {
556
+ /** The admin of the pool */
557
+ readonly admin: PublicKey;
558
+ /** Pubkey that can mint new tokens */
559
+ readonly actionAuthority: PublicKey | null;
560
+
561
+ constructor(args: { admin: PublicKey; actionAuthority: PublicKey | null }) {
562
+ this.admin = args.admin;
563
+ this.actionAuthority = args.actionAuthority;
564
+ }
565
+ }