@ignitionfi/spl-stake-pool 1.1.22 → 1.1.24

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/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export { DEVNET_STAKE_POOL_PROGRAM_ID, STAKE_POOL_PROGRAM_ID, } from './constant
6
6
  export * from './instructions';
7
7
  export type { AccountType, StakePool, ValidatorList, ValidatorStakeInfo, } from './layouts';
8
8
  export { StakePoolLayout, ValidatorListLayout, ValidatorStakeInfoLayout, } from './layouts';
9
+ export { findEphemeralStakeProgramAddress, findStakeProgramAddress, findTransientStakeProgramAddress, findUserStakeProgramAddress, findWithdrawAuthorityProgramAddress, findWsolTransientProgramAddress, } from './utils';
9
10
  export interface ValidatorListAccount {
10
11
  pubkey: PublicKey;
11
12
  account: AccountInfo<ValidatorList>;
@@ -85,18 +86,46 @@ export declare function withdrawSol(connection: Connection, stakePoolAddress: Pu
85
86
  }>;
86
87
  /**
87
88
  * Creates instructions required to withdraw wSOL from a stake pool.
89
+ * Rent for ATA creation (if needed) is paid from the withdrawal amount.
88
90
  */
89
- export declare function withdrawWsolWithSession(connection: Connection, stakePoolAddress: PublicKey, signerOrSession: PublicKey, userPubkey: PublicKey, amount: number, minimumLamportsOut?: number, solWithdrawAuthority?: PublicKey, payer?: PublicKey): Promise<{
91
+ export declare function withdrawWsolWithSession(connection: Connection, stakePoolAddress: PublicKey, signerOrSession: PublicKey, userPubkey: PublicKey, amount: number, minimumLamportsOut?: number, solWithdrawAuthority?: PublicKey): Promise<{
90
92
  instructions: TransactionInstruction[];
91
93
  signers: Signer[];
92
94
  }>;
93
95
  /**
94
- * Creates instructions required to withdraw stake from a stake pool using a Fogo session.
95
- * The withdrawn stake account will be authorized to the user's wallet.
96
- */
97
- export declare function withdrawStakeWithSession(connection: Connection, stakePoolAddress: PublicKey, signerOrSession: PublicKey, userPubkey: PublicKey, amount: number, payer: PublicKey, useReserve?: boolean, voteAccountAddress?: PublicKey, minimumLamportsOut?: number, validatorComparator?: (_a: ValidatorAccount, _b: ValidatorAccount) => number): Promise<{
96
+ * Finds the next available seed for creating a user stake PDA.
97
+ * Scans from startSeed until an unused PDA is found.
98
+ *
99
+ * @param connection - Solana connection
100
+ * @param programId - The stake pool program ID
101
+ * @param userPubkey - User's wallet (used for PDA derivation)
102
+ * @param startSeed - Starting seed to search from (default: 0)
103
+ * @param maxSeed - Maximum seed to check before giving up (default: 1000)
104
+ * @returns The next available seed
105
+ * @throws Error if no available seed found within maxSeed
106
+ */
107
+ export declare function findNextUserStakeSeed(connection: Connection, programId: PublicKey, userPubkey: PublicKey, startSeed?: number, maxSeed?: number): Promise<number>;
108
+ /**
109
+ * Withdraws stake from a stake pool using a Fogo session.
110
+ *
111
+ * The on-chain program creates stake account PDAs, so no pre-creation step is needed.
112
+ * Rent for the stake account PDAs is paid from the withdrawal amount.
113
+ *
114
+ * @param connection - Solana connection
115
+ * @param stakePoolAddress - The stake pool to withdraw from
116
+ * @param signerOrSession - The session signer public key
117
+ * @param userPubkey - User's wallet (used for PDA derivation and token ownership)
118
+ * @param amount - Amount of pool tokens to withdraw
119
+ * @param userStakeSeedStart - Starting seed for user stake PDA derivation (default: 0)
120
+ * @param useReserve - Whether to withdraw from reserve (default: false)
121
+ * @param voteAccountAddress - Optional specific validator to withdraw from
122
+ * @param minimumLamportsOut - Minimum lamports to receive (slippage protection)
123
+ * @param validatorComparator - Optional comparator for validator selection
124
+ */
125
+ export declare function withdrawStakeWithSession(connection: Connection, stakePoolAddress: PublicKey, signerOrSession: PublicKey, userPubkey: PublicKey, amount: number, userStakeSeedStart?: number, useReserve?: boolean, voteAccountAddress?: PublicKey, minimumLamportsOut?: number, validatorComparator?: (_a: ValidatorAccount, _b: ValidatorAccount) => number): Promise<{
98
126
  instructions: TransactionInstruction[];
99
127
  stakeAccountPubkeys: PublicKey[];
128
+ userStakeSeeds: number[];
100
129
  }>;
101
130
  export declare function addValidatorToPool(connection: Connection, stakePoolAddress: PublicKey, validatorVote: PublicKey, seed?: number): Promise<{
102
131
  instructions: TransactionInstruction[];
package/dist/index.esm.js CHANGED
@@ -432,6 +432,8 @@ const MAX_VALIDATORS_TO_UPDATE = 4;
432
432
  const EPHEMERAL_STAKE_SEED_PREFIX = Buffer$1.from('ephemeral');
433
433
  // Seed used to derive transient stake accounts.
434
434
  const TRANSIENT_STAKE_SEED_PREFIX = Buffer$1.from('transient');
435
+ // Seed for user stake account created during session withdrawal
436
+ const USER_STAKE_SEED_PREFIX = Buffer$1.from('user_stake');
435
437
  // Minimum amount of staked SOL required in a validator stake account to allow
436
438
  // for merges without a mismatch on credits observed
437
439
  const MINIMUM_ACTIVE_STAKE = LAMPORTS_PER_SOL;
@@ -540,6 +542,19 @@ function findMetadataAddress(stakePoolMintAddress) {
540
542
  const [publicKey] = PublicKey.findProgramAddressSync([Buffer$1.from('metadata'), METADATA_PROGRAM_ID.toBuffer(), stakePoolMintAddress.toBuffer()], METADATA_PROGRAM_ID);
541
543
  return publicKey;
542
544
  }
545
+ /**
546
+ * Generates the user stake account PDA for session-based withdrawals.
547
+ * The PDA is derived from the user's wallet and a unique seed.
548
+ */
549
+ function findUserStakeProgramAddress(programId, userWallet, seed) {
550
+ const seedBN = typeof seed === 'number' ? new BN(seed) : seed;
551
+ const [publicKey] = PublicKey.findProgramAddressSync([
552
+ USER_STAKE_SEED_PREFIX,
553
+ userWallet.toBuffer(),
554
+ seedBN.toArrayLike(Buffer$1, 'le', 8),
555
+ ], programId);
556
+ return publicKey;
557
+ }
543
558
 
544
559
  class BNLayout extends Layout {
545
560
  constructor(span, signed, property) {
@@ -1102,6 +1117,7 @@ const STAKE_POOL_INSTRUCTION_LAYOUTS = Object.freeze({
1102
1117
  BufferLayout.u8('instruction'),
1103
1118
  BufferLayout.ns64('poolTokensIn'),
1104
1119
  BufferLayout.ns64('minimumLamportsOut'),
1120
+ BufferLayout.ns64('userStakeSeed'),
1105
1121
  ]),
1106
1122
  },
1107
1123
  });
@@ -1537,9 +1553,9 @@ class StakePoolInstruction {
1537
1553
  }
1538
1554
  /**
1539
1555
  * Creates a transaction instruction to withdraw WSOL from a stake pool using a session.
1556
+ * Rent for ATA creation (if needed) is paid from the withdrawal amount.
1540
1557
  */
1541
1558
  static withdrawWsolWithSession(params) {
1542
- var _a;
1543
1559
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawWsolWithSession;
1544
1560
  const data = encodeData(type, {
1545
1561
  poolTokensIn: params.poolTokensIn,
@@ -1560,7 +1576,6 @@ class StakePoolInstruction {
1560
1576
  { pubkey: params.tokenProgramId, isSigner: false, isWritable: false },
1561
1577
  { pubkey: params.wsolMint, isSigner: false, isWritable: false },
1562
1578
  { pubkey: params.programSigner, isSigner: false, isWritable: true },
1563
- { pubkey: (_a = params.payer) !== null && _a !== void 0 ? _a : params.userTransferAuthority, isSigner: true, isWritable: true },
1564
1579
  { pubkey: params.userWallet, isSigner: false, isWritable: false },
1565
1580
  { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
1566
1581
  ];
@@ -1581,12 +1596,14 @@ class StakePoolInstruction {
1581
1596
  }
1582
1597
  /**
1583
1598
  * Creates a transaction instruction to withdraw stake from a stake pool using a Fogo session.
1599
+ * The stake account is created as a PDA and rent is paid from the withdrawal amount.
1584
1600
  */
1585
1601
  static withdrawStakeWithSession(params) {
1586
1602
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawStakeWithSession;
1587
1603
  const data = encodeData(type, {
1588
1604
  poolTokensIn: params.poolTokensIn,
1589
1605
  minimumLamportsOut: params.minimumLamportsOut,
1606
+ userStakeSeed: params.userStakeSeed,
1590
1607
  });
1591
1608
  const keys = [
1592
1609
  { pubkey: params.stakePool, isSigner: false, isWritable: true },
@@ -1603,6 +1620,7 @@ class StakePoolInstruction {
1603
1620
  { pubkey: params.tokenProgramId, isSigner: false, isWritable: false },
1604
1621
  { pubkey: StakeProgram.programId, isSigner: false, isWritable: false },
1605
1622
  { pubkey: params.programSigner, isSigner: false, isWritable: false },
1623
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
1606
1624
  ];
1607
1625
  return new TransactionInstruction({
1608
1626
  programId: params.programId,
@@ -2176,8 +2194,9 @@ async function withdrawSol(connection, stakePoolAddress, tokenOwner, solReceiver
2176
2194
  }
2177
2195
  /**
2178
2196
  * Creates instructions required to withdraw wSOL from a stake pool.
2197
+ * Rent for ATA creation (if needed) is paid from the withdrawal amount.
2179
2198
  */
2180
- async function withdrawWsolWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, amount, minimumLamportsOut = 0, solWithdrawAuthority, payer) {
2199
+ async function withdrawWsolWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, amount, minimumLamportsOut = 0, solWithdrawAuthority) {
2181
2200
  const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
2182
2201
  const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
2183
2202
  const stakePool = stakePoolAccount.account.data;
@@ -2210,7 +2229,6 @@ async function withdrawWsolWithSession(connection, stakePoolAddress, signerOrSes
2210
2229
  wsolMint: NATIVE_MINT,
2211
2230
  programSigner,
2212
2231
  userWallet: userPubkey,
2213
- payer,
2214
2232
  poolTokensIn: poolTokens,
2215
2233
  minimumLamportsOut,
2216
2234
  }));
@@ -2220,10 +2238,45 @@ async function withdrawWsolWithSession(connection, stakePoolAddress, signerOrSes
2220
2238
  };
2221
2239
  }
2222
2240
  /**
2223
- * Creates instructions required to withdraw stake from a stake pool using a Fogo session.
2224
- * The withdrawn stake account will be authorized to the user's wallet.
2241
+ * Finds the next available seed for creating a user stake PDA.
2242
+ * Scans from startSeed until an unused PDA is found.
2243
+ *
2244
+ * @param connection - Solana connection
2245
+ * @param programId - The stake pool program ID
2246
+ * @param userPubkey - User's wallet (used for PDA derivation)
2247
+ * @param startSeed - Starting seed to search from (default: 0)
2248
+ * @param maxSeed - Maximum seed to check before giving up (default: 1000)
2249
+ * @returns The next available seed
2250
+ * @throws Error if no available seed found within maxSeed
2225
2251
  */
2226
- async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, amount, payer, useReserve = false, voteAccountAddress, minimumLamportsOut = 0, validatorComparator) {
2252
+ async function findNextUserStakeSeed(connection, programId, userPubkey, startSeed = 0, maxSeed = 1000) {
2253
+ for (let seed = startSeed; seed < startSeed + maxSeed; seed++) {
2254
+ const pda = findUserStakeProgramAddress(programId, userPubkey, seed);
2255
+ const account = await connection.getAccountInfo(pda);
2256
+ if (!account) {
2257
+ return seed;
2258
+ }
2259
+ }
2260
+ throw new Error(`No available user stake seed found between ${startSeed} and ${startSeed + maxSeed - 1}`);
2261
+ }
2262
+ /**
2263
+ * Withdraws stake from a stake pool using a Fogo session.
2264
+ *
2265
+ * The on-chain program creates stake account PDAs, so no pre-creation step is needed.
2266
+ * Rent for the stake account PDAs is paid from the withdrawal amount.
2267
+ *
2268
+ * @param connection - Solana connection
2269
+ * @param stakePoolAddress - The stake pool to withdraw from
2270
+ * @param signerOrSession - The session signer public key
2271
+ * @param userPubkey - User's wallet (used for PDA derivation and token ownership)
2272
+ * @param amount - Amount of pool tokens to withdraw
2273
+ * @param userStakeSeedStart - Starting seed for user stake PDA derivation (default: 0)
2274
+ * @param useReserve - Whether to withdraw from reserve (default: false)
2275
+ * @param voteAccountAddress - Optional specific validator to withdraw from
2276
+ * @param minimumLamportsOut - Minimum lamports to receive (slippage protection)
2277
+ * @param validatorComparator - Optional comparator for validator selection
2278
+ */
2279
+ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, amount, userStakeSeedStart = 0, useReserve = false, voteAccountAddress, minimumLamportsOut = 0, validatorComparator) {
2227
2280
  const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
2228
2281
  const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
2229
2282
  const stakePool = stakePoolAccount.account.data;
@@ -2274,6 +2327,7 @@ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSe
2274
2327
  }
2275
2328
  const instructions = [];
2276
2329
  const stakeAccountPubkeys = [];
2330
+ const userStakeSeeds = [];
2277
2331
  // Max 5 accounts to prevent an error: "Transaction too large"
2278
2332
  const maxWithdrawAccounts = 5;
2279
2333
  let i = 0;
@@ -2281,26 +2335,12 @@ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSe
2281
2335
  if (i >= maxWithdrawAccounts) {
2282
2336
  break;
2283
2337
  }
2284
- // Create a deterministic stake account address using a seed
2285
- // This avoids needing the new account to sign (required for sessions)
2286
- // We use payer as base so only payer needs to sign (payer = basePubkey)
2287
- // The seed includes timestamp + random component to ensure uniqueness
2288
- const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
2289
- const seed = `ws:${uniqueId}:${i}`.slice(0, 32); // Seed max 32 chars
2290
- const stakeReceiverPubkey = await PublicKey.createWithSeed(payer, seed, StakeProgram.programId);
2338
+ // Derive the stake account PDA for this withdrawal
2339
+ const userStakeSeed = userStakeSeedStart + i;
2340
+ const stakeReceiverPubkey = findUserStakeProgramAddress(stakePoolProgramId, userPubkey, userStakeSeed);
2291
2341
  stakeAccountPubkeys.push(stakeReceiverPubkey);
2292
- // Create the stake account using createAccountWithSeed
2293
- // Since basePubkey === fromPubkey (both are payer), only payer needs to sign
2294
- instructions.push(SystemProgram.createAccountWithSeed({
2295
- fromPubkey: payer,
2296
- newAccountPubkey: stakeReceiverPubkey,
2297
- basePubkey: payer,
2298
- seed,
2299
- lamports: stakeAccountRentExemption,
2300
- space: StakeProgram.space,
2301
- programId: StakeProgram.programId,
2302
- }));
2303
- // Add the withdraw stake with session instruction
2342
+ userStakeSeeds.push(userStakeSeed);
2343
+ // The on-chain program will create the stake account PDA
2304
2344
  instructions.push(StakePoolInstruction.withdrawStakeWithSession({
2305
2345
  programId: stakePoolProgramId,
2306
2346
  stakePool: stakePoolAddress,
@@ -2316,12 +2356,14 @@ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSe
2316
2356
  programSigner,
2317
2357
  poolTokensIn: withdrawAccount.poolAmount.toNumber(),
2318
2358
  minimumLamportsOut,
2359
+ userStakeSeed,
2319
2360
  }));
2320
2361
  i++;
2321
2362
  }
2322
2363
  return {
2323
2364
  instructions,
2324
2365
  stakeAccountPubkeys,
2366
+ userStakeSeeds,
2325
2367
  };
2326
2368
  }
2327
2369
  async function addValidatorToPool(connection, stakePoolAddress, validatorVote, seed) {
@@ -2686,5 +2728,5 @@ async function updatePoolTokenMetadata(connection, stakePoolAddress, name, symbo
2686
2728
  };
2687
2729
  }
2688
2730
 
2689
- export { DEVNET_STAKE_POOL_PROGRAM_ID, STAKE_POOL_INSTRUCTION_LAYOUTS, STAKE_POOL_PROGRAM_ID, StakePoolInstruction, StakePoolLayout, ValidatorListLayout, ValidatorStakeInfoLayout, addValidatorToPool, createPoolTokenMetadata, decreaseValidatorStake, depositSol, depositStake, depositWsolWithSession, getStakeAccount, getStakePoolAccount, getStakePoolAccounts, getStakePoolProgramId, increaseValidatorStake, removeValidatorFromPool, stakePoolInfo, tokenMetadataLayout, updatePoolTokenMetadata, updateStakePool, withdrawSol, withdrawStake, withdrawStakeWithSession, withdrawWsolWithSession };
2731
+ export { DEVNET_STAKE_POOL_PROGRAM_ID, STAKE_POOL_INSTRUCTION_LAYOUTS, STAKE_POOL_PROGRAM_ID, StakePoolInstruction, StakePoolLayout, ValidatorListLayout, ValidatorStakeInfoLayout, addValidatorToPool, createPoolTokenMetadata, decreaseValidatorStake, depositSol, depositStake, depositWsolWithSession, findEphemeralStakeProgramAddress, findNextUserStakeSeed, findStakeProgramAddress, findTransientStakeProgramAddress, findUserStakeProgramAddress, findWithdrawAuthorityProgramAddress, findWsolTransientProgramAddress, getStakeAccount, getStakePoolAccount, getStakePoolAccounts, getStakePoolProgramId, increaseValidatorStake, removeValidatorFromPool, stakePoolInfo, tokenMetadataLayout, updatePoolTokenMetadata, updateStakePool, withdrawSol, withdrawStake, withdrawStakeWithSession, withdrawWsolWithSession };
2690
2732
  //# sourceMappingURL=index.esm.js.map