@kamino-finance/klend-sdk 5.10.25 → 5.10.26-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/classes/index.d.ts +3 -0
- package/dist/classes/index.d.ts.map +1 -1
- package/dist/classes/index.js +3 -0
- package/dist/classes/index.js.map +1 -1
- package/dist/classes/stakePool.d.ts +8 -0
- package/dist/classes/stakePool.d.ts.map +1 -0
- package/dist/classes/stakePool.js +18 -0
- package/dist/classes/stakePool.js.map +1 -0
- package/dist/classes/standardStakePool.d.ts +254 -0
- package/dist/classes/standardStakePool.d.ts.map +1 -0
- package/dist/classes/standardStakePool.js +275 -0
- package/dist/classes/standardStakePool.js.map +1 -0
- package/dist/classes/types.d.ts +13 -1
- package/dist/classes/types.d.ts.map +1 -1
- package/dist/classes/unstakingPool.d.ts +111 -0
- package/dist/classes/unstakingPool.d.ts.map +1 -0
- package/dist/classes/unstakingPool.js +437 -0
- package/dist/classes/unstakingPool.js.map +1 -0
- package/dist/idl_codegen_unstaking_pool/accounts/PoolState.d.ts +50 -0
- package/dist/idl_codegen_unstaking_pool/accounts/PoolState.d.ts.map +1 -0
- package/dist/idl_codegen_unstaking_pool/accounts/PoolState.js +156 -0
- package/dist/idl_codegen_unstaking_pool/accounts/PoolState.js.map +1 -0
- package/dist/idl_codegen_unstaking_pool/accounts/index.d.ts +3 -0
- package/dist/idl_codegen_unstaking_pool/accounts/index.d.ts.map +1 -0
- package/dist/idl_codegen_unstaking_pool/accounts/index.js +6 -0
- package/dist/idl_codegen_unstaking_pool/accounts/index.js.map +1 -0
- package/dist/idl_codegen_unstaking_pool/errors/anchor.d.ts +435 -0
- package/dist/idl_codegen_unstaking_pool/errors/anchor.d.ts.map +1 -0
- package/dist/idl_codegen_unstaking_pool/errors/anchor.js +767 -0
- package/dist/idl_codegen_unstaking_pool/errors/anchor.js.map +1 -0
- package/dist/idl_codegen_unstaking_pool/errors/custom.d.ts +163 -0
- package/dist/idl_codegen_unstaking_pool/errors/custom.d.ts.map +1 -0
- package/dist/idl_codegen_unstaking_pool/errors/custom.js +290 -0
- package/dist/idl_codegen_unstaking_pool/errors/custom.js.map +1 -0
- package/dist/idl_codegen_unstaking_pool/errors/index.d.ts +6 -0
- package/dist/idl_codegen_unstaking_pool/errors/index.d.ts.map +1 -0
- package/dist/idl_codegen_unstaking_pool/errors/index.js +75 -0
- package/dist/idl_codegen_unstaking_pool/errors/index.js.map +1 -0
- package/dist/idl_codegen_unstaking_pool/instructions/burn.d.ts +21 -0
- package/dist/idl_codegen_unstaking_pool/instructions/burn.d.ts.map +1 -0
- package/dist/idl_codegen_unstaking_pool/instructions/burn.js +73 -0
- package/dist/idl_codegen_unstaking_pool/instructions/burn.js.map +1 -0
- package/dist/idl_codegen_unstaking_pool/instructions/collect.d.ts +16 -0
- package/dist/idl_codegen_unstaking_pool/instructions/collect.d.ts.map +1 -0
- package/dist/idl_codegen_unstaking_pool/instructions/collect.js +29 -0
- package/dist/idl_codegen_unstaking_pool/instructions/collect.js.map +1 -0
- package/dist/idl_codegen_unstaking_pool/instructions/index.d.ts +11 -0
- package/dist/idl_codegen_unstaking_pool/instructions/index.d.ts.map +1 -0
- package/dist/idl_codegen_unstaking_pool/instructions/index.js +14 -0
- package/dist/idl_codegen_unstaking_pool/instructions/index.js.map +1 -0
- package/dist/idl_codegen_unstaking_pool/instructions/initializePool.d.ts +15 -0
- package/dist/idl_codegen_unstaking_pool/instructions/initializePool.d.ts.map +1 -0
- package/dist/idl_codegen_unstaking_pool/instructions/initializePool.js +28 -0
- package/dist/idl_codegen_unstaking_pool/instructions/initializePool.js.map +1 -0
- package/dist/idl_codegen_unstaking_pool/instructions/mint.d.ts +21 -0
- package/dist/idl_codegen_unstaking_pool/instructions/mint.d.ts.map +1 -0
- package/dist/idl_codegen_unstaking_pool/instructions/mint.js +77 -0
- package/dist/idl_codegen_unstaking_pool/instructions/mint.js.map +1 -0
- package/dist/idl_codegen_unstaking_pool/instructions/updatePoolConfig.d.ts +13 -0
- package/dist/idl_codegen_unstaking_pool/instructions/updatePoolConfig.d.ts.map +1 -0
- package/dist/idl_codegen_unstaking_pool/instructions/updatePoolConfig.js +58 -0
- package/dist/idl_codegen_unstaking_pool/instructions/updatePoolConfig.js.map +1 -0
- package/dist/idl_codegen_unstaking_pool/programId.d.ts +4 -0
- package/dist/idl_codegen_unstaking_pool/programId.d.ts.map +1 -0
- package/dist/idl_codegen_unstaking_pool/programId.js +9 -0
- package/dist/idl_codegen_unstaking_pool/programId.js.map +1 -0
- package/dist/idl_codegen_unstaking_pool/types/PoolConfigField.d.ts +32 -0
- package/dist/idl_codegen_unstaking_pool/types/PoolConfigField.d.ts.map +1 -0
- package/dist/idl_codegen_unstaking_pool/types/PoolConfigField.js +105 -0
- package/dist/idl_codegen_unstaking_pool/types/PoolConfigField.js.map +1 -0
- package/dist/idl_codegen_unstaking_pool/types/index.d.ts +5 -0
- package/dist/idl_codegen_unstaking_pool/types/index.d.ts.map +1 -0
- package/dist/idl_codegen_unstaking_pool/types/index.js +39 -0
- package/dist/idl_codegen_unstaking_pool/types/index.js.map +1 -0
- package/package.json +4 -1
- package/src/classes/index.ts +3 -0
- package/src/classes/stakePool.ts +21 -0
- package/src/classes/standardStakePool.ts +361 -0
- package/src/classes/types.ts +17 -1
- package/src/classes/unstakingPool.ts +565 -0
- package/src/idl_codegen_unstaking_pool/accounts/PoolState.ts +163 -0
- package/src/idl_codegen_unstaking_pool/accounts/index.ts +2 -0
- package/src/idl_codegen_unstaking_pool/errors/anchor.ts +764 -0
- package/src/idl_codegen_unstaking_pool/errors/custom.ts +288 -0
- package/src/idl_codegen_unstaking_pool/errors/index.ts +49 -0
- package/src/idl_codegen_unstaking_pool/instructions/burn.ts +60 -0
- package/src/idl_codegen_unstaking_pool/instructions/collect.ts +43 -0
- package/src/idl_codegen_unstaking_pool/instructions/index.ts +10 -0
- package/src/idl_codegen_unstaking_pool/instructions/initializePool.ts +41 -0
- package/src/idl_codegen_unstaking_pool/instructions/mint.ts +64 -0
- package/src/idl_codegen_unstaking_pool/instructions/updatePoolConfig.ts +40 -0
- package/src/idl_codegen_unstaking_pool/programId.ts +7 -0
- package/src/idl_codegen_unstaking_pool/types/PoolConfigField.ts +85 -0
- package/src/idl_codegen_unstaking_pool/types/index.ts +6 -0
- 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
|
+
}
|