@ignitionfi/spl-stake-pool 1.1.23 → 1.1.25

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.
@@ -22025,13 +22025,16 @@ var solanaStakePool = (function (exports, node_buffer) {
22025
22025
  continue;
22026
22026
  }
22027
22027
  const stakeAccountAddress = await findStakeProgramAddress(stakePoolProgramId, validator.voteAccountAddress, stakePoolAddress);
22028
- if (!validator.activeStakeLamports.isZero()) {
22028
+ // For active stake accounts, subtract the minimum balance that must remain
22029
+ // to allow for merges and maintain rent exemption
22030
+ const availableActiveLamports = validator.activeStakeLamports.sub(minBalance);
22031
+ if (availableActiveLamports.gt(new BN(0))) {
22029
22032
  const isPreferred = (_a = stakePool === null || stakePool === void 0 ? void 0 : stakePool.preferredWithdrawValidatorVoteAddress) === null || _a === void 0 ? void 0 : _a.equals(validator.voteAccountAddress);
22030
22033
  accounts.push({
22031
22034
  type: isPreferred ? 'preferred' : 'active',
22032
22035
  voteAddress: validator.voteAccountAddress,
22033
22036
  stakeAddress: stakeAccountAddress,
22034
- lamports: validator.activeStakeLamports,
22037
+ lamports: availableActiveLamports,
22035
22038
  });
22036
22039
  }
22037
22040
  const transientStakeLamports = validator.transientStakeLamports.sub(minBalance);
@@ -22322,6 +22325,14 @@ var solanaStakePool = (function (exports, node_buffer) {
22322
22325
  LayoutExports$1.ns64('userStakeSeed'),
22323
22326
  ]),
22324
22327
  },
22328
+ WithdrawFromStakeAccountWithSession: {
22329
+ index: 30,
22330
+ layout: LayoutExports$1.struct([
22331
+ LayoutExports$1.u8('instruction'),
22332
+ LayoutExports$1.ns64('lamports'),
22333
+ LayoutExports$1.ns64('userStakeSeed'),
22334
+ ]),
22335
+ },
22325
22336
  });
22326
22337
  /**
22327
22338
  * Stake Pool Instruction class
@@ -22755,9 +22766,9 @@ var solanaStakePool = (function (exports, node_buffer) {
22755
22766
  }
22756
22767
  /**
22757
22768
  * Creates a transaction instruction to withdraw WSOL from a stake pool using a session.
22769
+ * Rent for ATA creation (if needed) is paid from the withdrawal amount.
22758
22770
  */
22759
22771
  static withdrawWsolWithSession(params) {
22760
- var _a;
22761
22772
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawWsolWithSession;
22762
22773
  const data = encodeData(type, {
22763
22774
  poolTokensIn: params.poolTokensIn,
@@ -22778,7 +22789,6 @@ var solanaStakePool = (function (exports, node_buffer) {
22778
22789
  { pubkey: params.tokenProgramId, isSigner: false, isWritable: false },
22779
22790
  { pubkey: params.wsolMint, isSigner: false, isWritable: false },
22780
22791
  { pubkey: params.programSigner, isSigner: false, isWritable: true },
22781
- { pubkey: (_a = params.payer) !== null && _a !== void 0 ? _a : params.userTransferAuthority, isSigner: true, isWritable: true },
22782
22792
  { pubkey: params.userWallet, isSigner: false, isWritable: false },
22783
22793
  { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
22784
22794
  ];
@@ -22799,6 +22809,7 @@ var solanaStakePool = (function (exports, node_buffer) {
22799
22809
  }
22800
22810
  /**
22801
22811
  * Creates a transaction instruction to withdraw stake from a stake pool using a Fogo session.
22812
+ * The stake account is created as a PDA and rent is paid by the payer (typically paymaster).
22802
22813
  */
22803
22814
  static withdrawStakeWithSession(params) {
22804
22815
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawStakeWithSession;
@@ -22822,8 +22833,8 @@ var solanaStakePool = (function (exports, node_buffer) {
22822
22833
  { pubkey: params.tokenProgramId, isSigner: false, isWritable: false },
22823
22834
  { pubkey: StakeProgram.programId, isSigner: false, isWritable: false },
22824
22835
  { pubkey: params.programSigner, isSigner: false, isWritable: false },
22825
- { pubkey: params.payer, isSigner: true, isWritable: true },
22826
22836
  { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
22837
+ { pubkey: params.payer, isSigner: true, isWritable: true },
22827
22838
  ];
22828
22839
  return new TransactionInstruction({
22829
22840
  programId: params.programId,
@@ -22831,6 +22842,43 @@ var solanaStakePool = (function (exports, node_buffer) {
22831
22842
  data,
22832
22843
  });
22833
22844
  }
22845
+ /**
22846
+ * Creates a transaction instruction to withdraw SOL from a deactivated user stake account using a Fogo session.
22847
+ * The stake account must be fully deactivated (inactive).
22848
+ * User receives full stake balance (payer's rent contribution compensates for reduced split).
22849
+ */
22850
+ static withdrawFromStakeAccountWithSession(params) {
22851
+ // For u64::MAX (full withdrawal), we need to manually encode since buffer-layout doesn't handle bigint well
22852
+ const U64_MAX = BigInt('18446744073709551615');
22853
+ const isFullWithdrawal = params.lamports >= U64_MAX;
22854
+ // Manually create the instruction data buffer using Uint8Array for browser compatibility
22855
+ // Layout: u8 instruction (1) + u64 lamports (8) + u64 userStakeSeed (8) = 17 bytes
22856
+ const data = new Uint8Array(17);
22857
+ data[0] = 30; // instruction discriminator (WithdrawFromStakeAccountWithSession = 30)
22858
+ // Write lamports as u64 little-endian (bytes 1-8)
22859
+ const lamportsBigInt = isFullWithdrawal ? U64_MAX : params.lamports;
22860
+ for (let i = 0; i < 8; i++) {
22861
+ data[1 + i] = Number((lamportsBigInt >> BigInt(i * 8)) & BigInt(0xff));
22862
+ }
22863
+ // Write userStakeSeed as u64 little-endian (bytes 9-16)
22864
+ const seedBigInt = BigInt(params.userStakeSeed);
22865
+ for (let i = 0; i < 8; i++) {
22866
+ data[9 + i] = Number((seedBigInt >> BigInt(i * 8)) & BigInt(0xff));
22867
+ }
22868
+ // Account order matches Rust: stake_account, recipient, clock, stake_history, session_signer
22869
+ const keys = [
22870
+ { pubkey: params.userStakeAccount, isSigner: false, isWritable: true },
22871
+ { pubkey: params.userWallet, isSigner: false, isWritable: true },
22872
+ { pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
22873
+ { pubkey: SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
22874
+ { pubkey: params.sessionSigner, isSigner: true, isWritable: false },
22875
+ ];
22876
+ return new TransactionInstruction({
22877
+ programId: params.programId,
22878
+ keys,
22879
+ data: Buffer.from(data),
22880
+ });
22881
+ }
22834
22882
  /**
22835
22883
  * Creates an instruction to create metadata
22836
22884
  * using the mpl token metadata program for the pool token
@@ -23094,14 +23142,21 @@ var solanaStakePool = (function (exports, node_buffer) {
23094
23142
  /**
23095
23143
  * Creates instructions required to deposit sol to stake pool.
23096
23144
  */
23097
- async function depositWsolWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, lamports, minimumPoolTokensOut = 0, destinationTokenAccount, referrerTokenAccount, depositAuthority, payer) {
23145
+ async function depositWsolWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, lamports, minimumPoolTokensOut = 0, destinationTokenAccount, referrerTokenAccount, depositAuthority, payer,
23146
+ /**
23147
+ * Skip WSOL balance validation. Set to true when adding wrap instructions
23148
+ * in the same transaction that will fund the WSOL account before deposit.
23149
+ */
23150
+ skipBalanceCheck = false) {
23098
23151
  const wsolTokenAccount = getAssociatedTokenAddressSync(NATIVE_MINT, userPubkey);
23099
- const tokenAccountInfo = await connection.getTokenAccountBalance(wsolTokenAccount, 'confirmed');
23100
- const wsolBalance = tokenAccountInfo
23101
- ? parseInt(tokenAccountInfo.value.amount)
23102
- : 0;
23103
- if (wsolBalance < lamports) {
23104
- throw new Error(`Not enough WSOL to deposit into pool. Maximum deposit amount is ${lamportsToSol(wsolBalance)} WSOL.`);
23152
+ if (!skipBalanceCheck) {
23153
+ const tokenAccountInfo = await connection.getTokenAccountBalance(wsolTokenAccount, 'confirmed');
23154
+ const wsolBalance = tokenAccountInfo
23155
+ ? parseInt(tokenAccountInfo.value.amount)
23156
+ : 0;
23157
+ if (wsolBalance < lamports) {
23158
+ throw new Error(`Not enough WSOL to deposit into pool. Maximum deposit amount is ${lamportsToSol(wsolBalance)} WSOL.`);
23159
+ }
23105
23160
  }
23106
23161
  const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
23107
23162
  const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
@@ -23397,8 +23452,9 @@ var solanaStakePool = (function (exports, node_buffer) {
23397
23452
  }
23398
23453
  /**
23399
23454
  * Creates instructions required to withdraw wSOL from a stake pool.
23455
+ * Rent for ATA creation (if needed) is paid from the withdrawal amount.
23400
23456
  */
23401
- async function withdrawWsolWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, amount, minimumLamportsOut = 0, solWithdrawAuthority, payer) {
23457
+ async function withdrawWsolWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, amount, minimumLamportsOut = 0, solWithdrawAuthority) {
23402
23458
  const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
23403
23459
  const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
23404
23460
  const stakePool = stakePoolAccount.account.data;
@@ -23431,7 +23487,6 @@ var solanaStakePool = (function (exports, node_buffer) {
23431
23487
  wsolMint: NATIVE_MINT,
23432
23488
  programSigner,
23433
23489
  userWallet: userPubkey,
23434
- payer,
23435
23490
  poolTokensIn: poolTokens,
23436
23491
  minimumLamportsOut,
23437
23492
  }));
@@ -23462,17 +23517,83 @@ var solanaStakePool = (function (exports, node_buffer) {
23462
23517
  }
23463
23518
  throw new Error(`No available user stake seed found between ${startSeed} and ${startSeed + maxSeed - 1}`);
23464
23519
  }
23520
+ /**
23521
+ * Fetches all user stake accounts created via WithdrawStakeWithSession.
23522
+ * These are PDAs derived from [b"user_stake", user_wallet, seed].
23523
+ *
23524
+ * @param connection - Solana connection
23525
+ * @param programId - The stake pool program ID
23526
+ * @param userPubkey - User's wallet address
23527
+ * @param maxSeed - Maximum seed to check (default: 100)
23528
+ * @returns Array of user stake accounts with their details
23529
+ */
23530
+ async function getUserStakeAccounts(connection, programId, userPubkey, maxSeed = 100) {
23531
+ var _c, _d;
23532
+ const stakeAccounts = [];
23533
+ const currentEpoch = (await connection.getEpochInfo()).epoch;
23534
+ for (let seed = 0; seed < maxSeed; seed++) {
23535
+ const pda = findUserStakeProgramAddress(programId, userPubkey, seed);
23536
+ const accountInfo = await connection.getAccountInfo(pda);
23537
+ if (!accountInfo) {
23538
+ continue; // Skip empty slots, there might be gaps
23539
+ }
23540
+ // Check if owned by stake program
23541
+ if (!accountInfo.owner.equals(StakeProgram.programId)) {
23542
+ continue;
23543
+ }
23544
+ // Parse stake account data
23545
+ const stakeAccount = {
23546
+ pubkey: pda,
23547
+ seed,
23548
+ lamports: accountInfo.lamports,
23549
+ state: 'inactive',
23550
+ };
23551
+ try {
23552
+ // Parse the stake account to get delegation info
23553
+ const parsedAccount = await connection.getParsedAccountInfo(pda);
23554
+ if (parsedAccount.value && 'parsed' in parsedAccount.value.data) {
23555
+ const parsed = parsedAccount.value.data.parsed;
23556
+ if (parsed.type === 'delegated' && ((_d = (_c = parsed.info) === null || _c === void 0 ? void 0 : _c.stake) === null || _d === void 0 ? void 0 : _d.delegation)) {
23557
+ const delegation = parsed.info.stake.delegation;
23558
+ stakeAccount.voter = new PublicKey(delegation.voter);
23559
+ stakeAccount.activationEpoch = Number(delegation.activationEpoch);
23560
+ stakeAccount.deactivationEpoch = Number(delegation.deactivationEpoch);
23561
+ // Determine state based on epochs
23562
+ const activationEpoch = stakeAccount.activationEpoch;
23563
+ const deactivationEpoch = stakeAccount.deactivationEpoch;
23564
+ if (deactivationEpoch !== undefined && deactivationEpoch < Number.MAX_SAFE_INTEGER && deactivationEpoch <= currentEpoch) {
23565
+ stakeAccount.state = 'inactive';
23566
+ }
23567
+ else if (deactivationEpoch !== undefined && deactivationEpoch < Number.MAX_SAFE_INTEGER) {
23568
+ stakeAccount.state = 'deactivating';
23569
+ }
23570
+ else if (activationEpoch !== undefined && activationEpoch <= currentEpoch) {
23571
+ stakeAccount.state = 'active';
23572
+ }
23573
+ else {
23574
+ stakeAccount.state = 'activating';
23575
+ }
23576
+ }
23577
+ }
23578
+ }
23579
+ catch {
23580
+ // If parsing fails, keep default 'inactive' state
23581
+ }
23582
+ stakeAccounts.push(stakeAccount);
23583
+ }
23584
+ return stakeAccounts;
23585
+ }
23465
23586
  /**
23466
23587
  * Withdraws stake from a stake pool using a Fogo session.
23467
23588
  *
23468
- * The on-chain program creates stake account PDAs, so no pre-creation step is needed.
23469
- * The paymaster payer provides rent for the stake account PDAs.
23589
+ * The on-chain program creates stake account PDAs. The rent for these accounts
23590
+ * is paid by the payer (typically the paymaster), not deducted from the user's withdrawal.
23470
23591
  *
23471
23592
  * @param connection - Solana connection
23472
23593
  * @param stakePoolAddress - The stake pool to withdraw from
23473
23594
  * @param signerOrSession - The session signer public key
23474
23595
  * @param userPubkey - User's wallet (used for PDA derivation and token ownership)
23475
- * @param payer - Payer for stake account rent (paymaster payer in session context)
23596
+ * @param payer - Payer for stake account rent (typically paymaster)
23476
23597
  * @param amount - Amount of pool tokens to withdraw
23477
23598
  * @param userStakeSeedStart - Starting seed for user stake PDA derivation (default: 0)
23478
23599
  * @param useReserve - Whether to withdraw from reserve (default: false)
@@ -23544,7 +23665,7 @@ var solanaStakePool = (function (exports, node_buffer) {
23544
23665
  const stakeReceiverPubkey = findUserStakeProgramAddress(stakePoolProgramId, userPubkey, userStakeSeed);
23545
23666
  stakeAccountPubkeys.push(stakeReceiverPubkey);
23546
23667
  userStakeSeeds.push(userStakeSeed);
23547
- // The on-chain program will create the stake account PDA
23668
+ // The on-chain program creates the stake account PDA and rent is paid by payer.
23548
23669
  instructions.push(StakePoolInstruction.withdrawStakeWithSession({
23549
23670
  programId: stakePoolProgramId,
23550
23671
  stakePool: stakePoolAddress,
@@ -23957,6 +24078,7 @@ var solanaStakePool = (function (exports, node_buffer) {
23957
24078
  exports.getStakePoolAccount = getStakePoolAccount;
23958
24079
  exports.getStakePoolAccounts = getStakePoolAccounts;
23959
24080
  exports.getStakePoolProgramId = getStakePoolProgramId;
24081
+ exports.getUserStakeAccounts = getUserStakeAccounts;
23960
24082
  exports.increaseValidatorStake = increaseValidatorStake;
23961
24083
  exports.removeValidatorFromPool = removeValidatorFromPool;
23962
24084
  exports.stakePoolInfo = stakePoolInfo;