@ignitionfi/spl-stake-pool 1.1.21 → 1.1.23

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 { findUserStakeProgramAddress, findWithdrawAuthorityProgramAddress, findStakeProgramAddress, findTransientStakeProgramAddress, findEphemeralStakeProgramAddress, findWsolTransientProgramAddress, } from './utils';
9
10
  export interface ValidatorListAccount {
10
11
  pubkey: PublicKey;
11
12
  account: AccountInfo<ValidatorList>;
@@ -91,13 +92,40 @@ export declare function withdrawWsolWithSession(connection: Connection, stakePoo
91
92
  signers: Signer[];
92
93
  }>;
93
94
  /**
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, useReserve?: boolean, voteAccountAddress?: PublicKey, minimumLamportsOut?: number, payer?: PublicKey, validatorComparator?: (_a: ValidatorAccount, _b: ValidatorAccount) => number): Promise<{
95
+ * Finds the next available seed for creating a user stake PDA.
96
+ * Scans from startSeed until an unused PDA is found.
97
+ *
98
+ * @param connection - Solana connection
99
+ * @param programId - The stake pool program ID
100
+ * @param userPubkey - User's wallet (used for PDA derivation)
101
+ * @param startSeed - Starting seed to search from (default: 0)
102
+ * @param maxSeed - Maximum seed to check before giving up (default: 1000)
103
+ * @returns The next available seed
104
+ * @throws Error if no available seed found within maxSeed
105
+ */
106
+ export declare function findNextUserStakeSeed(connection: Connection, programId: PublicKey, userPubkey: PublicKey, startSeed?: number, maxSeed?: number): Promise<number>;
107
+ /**
108
+ * Withdraws stake from a stake pool using a Fogo session.
109
+ *
110
+ * The on-chain program creates stake account PDAs, so no pre-creation step is needed.
111
+ * The paymaster payer provides rent for the stake account PDAs.
112
+ *
113
+ * @param connection - Solana connection
114
+ * @param stakePoolAddress - The stake pool to withdraw from
115
+ * @param signerOrSession - The session signer public key
116
+ * @param userPubkey - User's wallet (used for PDA derivation and token ownership)
117
+ * @param payer - Payer for stake account rent (paymaster payer in session context)
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, payer: PublicKey, amount: number, userStakeSeedStart?: number, useReserve?: boolean, voteAccountAddress?: PublicKey, minimumLamportsOut?: number, validatorComparator?: (_a: ValidatorAccount, _b: ValidatorAccount) => number): Promise<{
98
126
  instructions: TransactionInstruction[];
99
- signers: Signer[];
100
127
  stakeAccountPubkeys: PublicKey[];
128
+ userStakeSeeds: number[];
101
129
  }>;
102
130
  export declare function addValidatorToPool(connection: Connection, stakePoolAddress: PublicKey, validatorVote: PublicKey, seed?: number): Promise<{
103
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
  });
@@ -1587,6 +1603,7 @@ class StakePoolInstruction {
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,8 @@ 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: params.payer, isSigner: true, isWritable: true },
1624
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
1606
1625
  ];
1607
1626
  return new TransactionInstruction({
1608
1627
  programId: params.programId,
@@ -2220,10 +2239,46 @@ async function withdrawWsolWithSession(connection, stakePoolAddress, signerOrSes
2220
2239
  };
2221
2240
  }
2222
2241
  /**
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.
2242
+ * Finds the next available seed for creating a user stake PDA.
2243
+ * Scans from startSeed until an unused PDA is found.
2244
+ *
2245
+ * @param connection - Solana connection
2246
+ * @param programId - The stake pool program ID
2247
+ * @param userPubkey - User's wallet (used for PDA derivation)
2248
+ * @param startSeed - Starting seed to search from (default: 0)
2249
+ * @param maxSeed - Maximum seed to check before giving up (default: 1000)
2250
+ * @returns The next available seed
2251
+ * @throws Error if no available seed found within maxSeed
2252
+ */
2253
+ async function findNextUserStakeSeed(connection, programId, userPubkey, startSeed = 0, maxSeed = 1000) {
2254
+ for (let seed = startSeed; seed < startSeed + maxSeed; seed++) {
2255
+ const pda = findUserStakeProgramAddress(programId, userPubkey, seed);
2256
+ const account = await connection.getAccountInfo(pda);
2257
+ if (!account) {
2258
+ return seed;
2259
+ }
2260
+ }
2261
+ throw new Error(`No available user stake seed found between ${startSeed} and ${startSeed + maxSeed - 1}`);
2262
+ }
2263
+ /**
2264
+ * Withdraws stake from a stake pool using a Fogo session.
2265
+ *
2266
+ * The on-chain program creates stake account PDAs, so no pre-creation step is needed.
2267
+ * The paymaster payer provides rent for the stake account PDAs.
2268
+ *
2269
+ * @param connection - Solana connection
2270
+ * @param stakePoolAddress - The stake pool to withdraw from
2271
+ * @param signerOrSession - The session signer public key
2272
+ * @param userPubkey - User's wallet (used for PDA derivation and token ownership)
2273
+ * @param payer - Payer for stake account rent (paymaster payer in session context)
2274
+ * @param amount - Amount of pool tokens to withdraw
2275
+ * @param userStakeSeedStart - Starting seed for user stake PDA derivation (default: 0)
2276
+ * @param useReserve - Whether to withdraw from reserve (default: false)
2277
+ * @param voteAccountAddress - Optional specific validator to withdraw from
2278
+ * @param minimumLamportsOut - Minimum lamports to receive (slippage protection)
2279
+ * @param validatorComparator - Optional comparator for validator selection
2225
2280
  */
2226
- async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, amount, useReserve = false, voteAccountAddress, minimumLamportsOut = 0, payer, validatorComparator) {
2281
+ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, payer, amount, userStakeSeedStart = 0, useReserve = false, voteAccountAddress, minimumLamportsOut = 0, validatorComparator) {
2227
2282
  const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
2228
2283
  const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
2229
2284
  const stakePool = stakePoolAccount.account.data;
@@ -2273,8 +2328,8 @@ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSe
2273
2328
  withdrawAccounts.push(...(await prepareWithdrawAccounts(connection, stakePool, stakePoolAddress, poolAmount, validatorComparator, poolTokenAccount.equals(stakePool.managerFeeAccount))));
2274
2329
  }
2275
2330
  const instructions = [];
2276
- const signers = [];
2277
2331
  const stakeAccountPubkeys = [];
2332
+ const userStakeSeeds = [];
2278
2333
  // Max 5 accounts to prevent an error: "Transaction too large"
2279
2334
  const maxWithdrawAccounts = 5;
2280
2335
  let i = 0;
@@ -2282,41 +2337,36 @@ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSe
2282
2337
  if (i >= maxWithdrawAccounts) {
2283
2338
  break;
2284
2339
  }
2285
- // Create a new stake account to receive the withdrawal
2286
- const stakeReceiver = Keypair.generate();
2287
- signers.push(stakeReceiver);
2288
- stakeAccountPubkeys.push(stakeReceiver.publicKey);
2289
- // Create the stake account that will receive the split stake
2290
- instructions.push(SystemProgram.createAccount({
2291
- fromPubkey: payer !== null && payer !== void 0 ? payer : userPubkey,
2292
- newAccountPubkey: stakeReceiver.publicKey,
2293
- lamports: stakeAccountRentExemption,
2294
- space: StakeProgram.space,
2295
- programId: StakeProgram.programId,
2296
- }));
2297
- // Add the withdraw stake with session instruction
2340
+ // Derive the stake account PDA for this withdrawal
2341
+ const userStakeSeed = userStakeSeedStart + i;
2342
+ const stakeReceiverPubkey = findUserStakeProgramAddress(stakePoolProgramId, userPubkey, userStakeSeed);
2343
+ stakeAccountPubkeys.push(stakeReceiverPubkey);
2344
+ userStakeSeeds.push(userStakeSeed);
2345
+ // The on-chain program will create the stake account PDA
2298
2346
  instructions.push(StakePoolInstruction.withdrawStakeWithSession({
2299
2347
  programId: stakePoolProgramId,
2300
2348
  stakePool: stakePoolAddress,
2301
2349
  validatorList: stakePool.validatorList,
2302
2350
  withdrawAuthority,
2303
2351
  stakeToSplit: withdrawAccount.stakeAddress,
2304
- stakeToReceive: stakeReceiver.publicKey,
2352
+ stakeToReceive: stakeReceiverPubkey,
2305
2353
  sessionSigner: signerOrSession,
2306
2354
  burnFromPool: poolTokenAccount,
2307
2355
  managerFeeAccount: stakePool.managerFeeAccount,
2308
2356
  poolMint: stakePool.poolMint,
2309
2357
  tokenProgramId: stakePool.tokenProgramId,
2310
2358
  programSigner,
2359
+ payer,
2311
2360
  poolTokensIn: withdrawAccount.poolAmount.toNumber(),
2312
- minimumLamportsOut: solToLamports(minimumLamportsOut),
2361
+ minimumLamportsOut,
2362
+ userStakeSeed,
2313
2363
  }));
2314
2364
  i++;
2315
2365
  }
2316
2366
  return {
2317
2367
  instructions,
2318
- signers,
2319
2368
  stakeAccountPubkeys,
2369
+ userStakeSeeds,
2320
2370
  };
2321
2371
  }
2322
2372
  async function addValidatorToPool(connection, stakePoolAddress, validatorVote, seed) {
@@ -2681,5 +2731,5 @@ async function updatePoolTokenMetadata(connection, stakePoolAddress, name, symbo
2681
2731
  };
2682
2732
  }
2683
2733
 
2684
- 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 };
2734
+ 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 };
2685
2735
  //# sourceMappingURL=index.esm.js.map