@ignitionfi/fogo-stake-pool 1.0.2 → 1.1.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.
@@ -19026,124 +19026,6 @@ var solanaStakePool = (function (exports, node_buffer) {
19026
19026
  // for merges without a mismatch on credits observed
19027
19027
  const MINIMUM_ACTIVE_STAKE = 1000000;
19028
19028
 
19029
- /**
19030
- * Populate a buffer of instruction data using an InstructionType
19031
- * @internal
19032
- */
19033
- function encodeData(type, fields) {
19034
- const allocLength = type.layout.span;
19035
- const data = node_buffer.Buffer.alloc(allocLength);
19036
- const layoutFields = Object.assign({ instruction: type.index }, fields);
19037
- type.layout.encode(layoutFields, data);
19038
- return data;
19039
- }
19040
- /**
19041
- * Decode instruction data buffer using an InstructionType
19042
- * @internal
19043
- */
19044
- function decodeData(type, buffer) {
19045
- let data;
19046
- try {
19047
- data = type.layout.decode(buffer);
19048
- }
19049
- catch (err) {
19050
- throw new Error(`invalid instruction; ${err}`);
19051
- }
19052
- if (data.instruction !== type.index) {
19053
- throw new Error(`invalid instruction; instruction index mismatch ${data.instruction} != ${type.index}`);
19054
- }
19055
- return data;
19056
- }
19057
-
19058
- function solToLamports(amount) {
19059
- if (isNaN(amount)) {
19060
- return Number(0);
19061
- }
19062
- return Number(amount * LAMPORTS_PER_SOL);
19063
- }
19064
- function lamportsToSol(lamports) {
19065
- if (typeof lamports === 'number') {
19066
- return Math.abs(lamports) / LAMPORTS_PER_SOL;
19067
- }
19068
- if (typeof lamports === 'bigint') {
19069
- return Math.abs(Number(lamports)) / LAMPORTS_PER_SOL;
19070
- }
19071
- let signMultiplier = 1;
19072
- if (lamports.isNeg()) {
19073
- signMultiplier = -1;
19074
- }
19075
- const absLamports = lamports.abs();
19076
- const lamportsString = absLamports.toString(10).padStart(10, '0');
19077
- const splitIndex = lamportsString.length - 9;
19078
- const solString = `${lamportsString.slice(0, splitIndex)}.${lamportsString.slice(splitIndex)}`;
19079
- return signMultiplier * Number.parseFloat(solString);
19080
- }
19081
-
19082
- /**
19083
- * Generates the wSOL transient program address for the stake pool
19084
- */
19085
- function findWsolTransientProgramAddress(programId, userPubkey) {
19086
- const [publicKey] = PublicKey.findProgramAddressSync([node_buffer.Buffer.from('transient_wsol'), userPubkey.toBuffer()], programId);
19087
- return publicKey;
19088
- }
19089
- /**
19090
- * Generates the withdraw authority program address for the stake pool
19091
- */
19092
- async function findWithdrawAuthorityProgramAddress(programId, stakePoolAddress) {
19093
- const [publicKey] = PublicKey.findProgramAddressSync([stakePoolAddress.toBuffer(), node_buffer.Buffer.from('withdraw')], programId);
19094
- return publicKey;
19095
- }
19096
- /**
19097
- * Generates the stake program address for a validator's vote account
19098
- */
19099
- async function findStakeProgramAddress(programId, voteAccountAddress, stakePoolAddress, seed) {
19100
- const [publicKey] = PublicKey.findProgramAddressSync([
19101
- voteAccountAddress.toBuffer(),
19102
- stakePoolAddress.toBuffer(),
19103
- seed ? new BN(seed).toArrayLike(node_buffer.Buffer, 'le', 4) : node_buffer.Buffer.alloc(0),
19104
- ], programId);
19105
- return publicKey;
19106
- }
19107
- /**
19108
- * Generates the stake program address for a validator's vote account
19109
- */
19110
- async function findTransientStakeProgramAddress(programId, voteAccountAddress, stakePoolAddress, seed) {
19111
- const [publicKey] = PublicKey.findProgramAddressSync([
19112
- TRANSIENT_STAKE_SEED_PREFIX,
19113
- voteAccountAddress.toBuffer(),
19114
- stakePoolAddress.toBuffer(),
19115
- seed.toArrayLike(node_buffer.Buffer, 'le', 8),
19116
- ], programId);
19117
- return publicKey;
19118
- }
19119
- /**
19120
- * Generates the ephemeral program address for stake pool redelegation
19121
- */
19122
- async function findEphemeralStakeProgramAddress(programId, stakePoolAddress, seed) {
19123
- const [publicKey] = PublicKey.findProgramAddressSync([EPHEMERAL_STAKE_SEED_PREFIX, stakePoolAddress.toBuffer(), seed.toArrayLike(node_buffer.Buffer, 'le', 8)], programId);
19124
- return publicKey;
19125
- }
19126
- /**
19127
- * Generates the metadata program address for the stake pool
19128
- */
19129
- function findMetadataAddress(stakePoolMintAddress) {
19130
- const [publicKey] = PublicKey.findProgramAddressSync([node_buffer.Buffer.from('metadata'), METADATA_PROGRAM_ID.toBuffer(), stakePoolMintAddress.toBuffer()], METADATA_PROGRAM_ID);
19131
- return publicKey;
19132
- }
19133
- /**
19134
- * Generates the user stake account PDA for session-based withdrawals.
19135
- * The PDA is derived from the user's wallet and a unique seed.
19136
- */
19137
- function findUserStakeProgramAddress(programId, userWallet, seed) {
19138
- const seedBN = typeof seed === 'number' ? new BN(seed) : seed;
19139
- const [publicKey] = PublicKey.findProgramAddressSync([
19140
- USER_STAKE_SEED_PREFIX,
19141
- userWallet.toBuffer(),
19142
- seedBN.toArrayLike(node_buffer.Buffer, 'le', 8),
19143
- ], programId);
19144
- return publicKey;
19145
- }
19146
-
19147
19029
  var Layout = {};
19148
19030
 
19149
19031
  /* The MIT License (MIT)
@@ -21758,14 +21640,18 @@ var solanaStakePool = (function (exports, node_buffer) {
21758
21640
 
21759
21641
  var LayoutExports = /*@__PURE__*/ requireLayout();
21760
21642
 
21761
- class BNLayout extends LayoutExports.Layout {
21643
+ /**
21644
+ * BN-based layout for data decoding using buffer-layout.
21645
+ * Used for decoding on-chain account data (StakePool, ValidatorList, etc.)
21646
+ */
21647
+ class BNDataLayout extends LayoutExports.Layout {
21762
21648
  constructor(span, signed, property) {
21763
21649
  super(span, property);
21764
- this.blob = LayoutExports.blob(span);
21650
+ this.blobLayout = LayoutExports.blob(span);
21765
21651
  this.signed = signed;
21766
21652
  }
21767
21653
  decode(b, offset = 0) {
21768
- const num = new BN(this.blob.decode(b, offset), 10, 'le');
21654
+ const num = new BN(this.blobLayout.decode(b, offset), 10, 'le');
21769
21655
  if (this.signed) {
21770
21656
  return num.fromTwos(this.span * 8).clone();
21771
21657
  }
@@ -21775,11 +21661,51 @@ var solanaStakePool = (function (exports, node_buffer) {
21775
21661
  if (this.signed) {
21776
21662
  src = src.toTwos(this.span * 8);
21777
21663
  }
21778
- return this.blob.encode(src.toArrayLike(Buffer, 'le', this.span), b, offset);
21664
+ return this.blobLayout.encode(src.toArrayLike(Buffer, 'le', this.span), b, offset);
21779
21665
  }
21780
21666
  }
21667
+ /**
21668
+ * Creates a u64 layout for data decoding (account layouts).
21669
+ * Used in StakePoolLayout, ValidatorListLayout, etc.
21670
+ */
21781
21671
  function u64(property) {
21782
- return new BNLayout(8, false, property);
21672
+ return new BNDataLayout(8, false, property);
21673
+ }
21674
+ /**
21675
+ * BN-based layout for 64-bit unsigned integers using @solana/buffer-layout.
21676
+ * Used for encoding instruction data with support for values > MAX_SAFE_INTEGER.
21677
+ */
21678
+ class BNInstructionLayout extends LayoutExports$1.Layout {
21679
+ constructor(span, signed, property) {
21680
+ super(span, property);
21681
+ this.blobLayout = LayoutExports$1.blob(span);
21682
+ this.signed = signed;
21683
+ }
21684
+ decode(b, offset = 0) {
21685
+ const num = new BN(this.blobLayout.decode(b, offset), 10, 'le');
21686
+ if (this.signed) {
21687
+ return num.fromTwos(this.span * 8).clone();
21688
+ }
21689
+ return num;
21690
+ }
21691
+ encode(src, b, offset = 0) {
21692
+ if (this.signed) {
21693
+ src = src.toTwos(this.span * 8);
21694
+ }
21695
+ return this.blobLayout.encode(src.toArrayLike(Buffer, 'le', this.span), b, offset);
21696
+ }
21697
+ getSpan(_b, _offset) {
21698
+ return this.span;
21699
+ }
21700
+ }
21701
+ /**
21702
+ * Creates a u64 layout for instruction encoding.
21703
+ * Properly handles BN values larger than Number.MAX_SAFE_INTEGER.
21704
+ * Compatible with @solana/buffer-layout.struct().
21705
+ */
21706
+ // eslint-disable-next-line ts/no-explicit-any
21707
+ function u64Instruction(property) {
21708
+ return new BNInstructionLayout(8, false, property);
21783
21709
  }
21784
21710
  class WrappedLayout extends LayoutExports.Layout {
21785
21711
  constructor(layout, decoder, encoder, property) {
@@ -21847,6 +21773,124 @@ var solanaStakePool = (function (exports, node_buffer) {
21847
21773
  return new WrappedLayout(layout, ({ values }) => values, values => ({ values }), property);
21848
21774
  }
21849
21775
 
21776
+ /**
21777
+ * Populate a buffer of instruction data using an InstructionType
21778
+ * @internal
21779
+ */
21780
+ function encodeData(type, fields) {
21781
+ const allocLength = type.layout.span;
21782
+ const data = node_buffer.Buffer.alloc(allocLength);
21783
+ const layoutFields = Object.assign({ instruction: type.index }, fields);
21784
+ type.layout.encode(layoutFields, data);
21785
+ return data;
21786
+ }
21787
+ /**
21788
+ * Decode instruction data buffer using an InstructionType
21789
+ * @internal
21790
+ */
21791
+ function decodeData(type, buffer) {
21792
+ let data;
21793
+ try {
21794
+ data = type.layout.decode(buffer);
21795
+ }
21796
+ catch (err) {
21797
+ throw new Error(`invalid instruction; ${err}`);
21798
+ }
21799
+ if (data.instruction !== type.index) {
21800
+ throw new Error(`invalid instruction; instruction index mismatch ${data.instruction} != ${type.index}`);
21801
+ }
21802
+ return data;
21803
+ }
21804
+
21805
+ function solToLamports(amount) {
21806
+ if (isNaN(amount)) {
21807
+ return Number(0);
21808
+ }
21809
+ return Number(amount * LAMPORTS_PER_SOL);
21810
+ }
21811
+ function lamportsToSol(lamports) {
21812
+ if (typeof lamports === 'number') {
21813
+ return Math.abs(lamports) / LAMPORTS_PER_SOL;
21814
+ }
21815
+ if (typeof lamports === 'bigint') {
21816
+ return Math.abs(Number(lamports)) / LAMPORTS_PER_SOL;
21817
+ }
21818
+ let signMultiplier = 1;
21819
+ if (lamports.isNeg()) {
21820
+ signMultiplier = -1;
21821
+ }
21822
+ const absLamports = lamports.abs();
21823
+ const lamportsString = absLamports.toString(10).padStart(10, '0');
21824
+ const splitIndex = lamportsString.length - 9;
21825
+ const solString = `${lamportsString.slice(0, splitIndex)}.${lamportsString.slice(splitIndex)}`;
21826
+ return signMultiplier * Number.parseFloat(solString);
21827
+ }
21828
+
21829
+ /**
21830
+ * Generates the wSOL transient program address for the stake pool
21831
+ */
21832
+ function findWsolTransientProgramAddress(programId, userPubkey) {
21833
+ const [publicKey] = PublicKey.findProgramAddressSync([node_buffer.Buffer.from('transient_wsol'), userPubkey.toBuffer()], programId);
21834
+ return publicKey;
21835
+ }
21836
+ /**
21837
+ * Generates the withdraw authority program address for the stake pool
21838
+ */
21839
+ async function findWithdrawAuthorityProgramAddress(programId, stakePoolAddress) {
21840
+ const [publicKey] = PublicKey.findProgramAddressSync([stakePoolAddress.toBuffer(), node_buffer.Buffer.from('withdraw')], programId);
21841
+ return publicKey;
21842
+ }
21843
+ /**
21844
+ * Generates the stake program address for a validator's vote account
21845
+ */
21846
+ async function findStakeProgramAddress(programId, voteAccountAddress, stakePoolAddress, seed) {
21847
+ const [publicKey] = PublicKey.findProgramAddressSync([
21848
+ voteAccountAddress.toBuffer(),
21849
+ stakePoolAddress.toBuffer(),
21850
+ seed ? new BN(seed).toArrayLike(node_buffer.Buffer, 'le', 4) : node_buffer.Buffer.alloc(0),
21851
+ ], programId);
21852
+ return publicKey;
21853
+ }
21854
+ /**
21855
+ * Generates the stake program address for a validator's vote account
21856
+ */
21857
+ async function findTransientStakeProgramAddress(programId, voteAccountAddress, stakePoolAddress, seed) {
21858
+ const [publicKey] = PublicKey.findProgramAddressSync([
21859
+ TRANSIENT_STAKE_SEED_PREFIX,
21860
+ voteAccountAddress.toBuffer(),
21861
+ stakePoolAddress.toBuffer(),
21862
+ seed.toArrayLike(node_buffer.Buffer, 'le', 8),
21863
+ ], programId);
21864
+ return publicKey;
21865
+ }
21866
+ /**
21867
+ * Generates the ephemeral program address for stake pool redelegation
21868
+ */
21869
+ async function findEphemeralStakeProgramAddress(programId, stakePoolAddress, seed) {
21870
+ const [publicKey] = PublicKey.findProgramAddressSync([EPHEMERAL_STAKE_SEED_PREFIX, stakePoolAddress.toBuffer(), seed.toArrayLike(node_buffer.Buffer, 'le', 8)], programId);
21871
+ return publicKey;
21872
+ }
21873
+ /**
21874
+ * Generates the metadata program address for the stake pool
21875
+ */
21876
+ function findMetadataAddress(stakePoolMintAddress) {
21877
+ const [publicKey] = PublicKey.findProgramAddressSync([node_buffer.Buffer.from('metadata'), METADATA_PROGRAM_ID.toBuffer(), stakePoolMintAddress.toBuffer()], METADATA_PROGRAM_ID);
21878
+ return publicKey;
21879
+ }
21880
+ /**
21881
+ * Generates the user stake account PDA for session-based withdrawals.
21882
+ * The PDA is derived from the user's wallet and a unique seed.
21883
+ */
21884
+ function findUserStakeProgramAddress(programId, userWallet, seed) {
21885
+ const seedBN = typeof seed === 'number' ? new BN(seed) : seed;
21886
+ const [publicKey] = PublicKey.findProgramAddressSync([
21887
+ USER_STAKE_SEED_PREFIX,
21888
+ userWallet.toBuffer(),
21889
+ seedBN.toArrayLike(node_buffer.Buffer, 'le', 8),
21890
+ ], programId);
21891
+ return publicKey;
21892
+ }
21893
+
21850
21894
  const feeFields = [u64('denominator'), u64('numerator')];
21851
21895
  var AccountType;
21852
21896
  (function (AccountType) {
@@ -22008,80 +22052,84 @@ var solanaStakePool = (function (exports, node_buffer) {
22008
22052
  },
22009
22053
  };
22010
22054
  }
22011
- async function prepareWithdrawAccounts(connection, stakePool, stakePoolAddress, amount, compareFn, skipFee) {
22055
+ async function prepareWithdrawAccounts(connection, stakePool, stakePoolAddress, amount, compareFn, skipFee, allowPartial, prefetchedData) {
22012
22056
  var _a, _b;
22013
22057
  const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
22014
- const validatorListAcc = await connection.getAccountInfo(stakePool.validatorList);
22015
- const validatorList = ValidatorListLayout.decode(validatorListAcc === null || validatorListAcc === void 0 ? void 0 : validatorListAcc.data);
22016
- if (!(validatorList === null || validatorList === void 0 ? void 0 : validatorList.validators) || (validatorList === null || validatorList === void 0 ? void 0 : validatorList.validators.length) === 0) {
22017
- throw new Error('No accounts found');
22058
+ // Use prefetched data if available, otherwise fetch from RPC
22059
+ let validatorListData;
22060
+ let minBalanceForRentExemption;
22061
+ let stakeMinimumDelegation;
22062
+ if (prefetchedData) {
22063
+ validatorListData = prefetchedData.validatorListData;
22064
+ minBalanceForRentExemption = prefetchedData.minBalanceForRentExemption;
22065
+ stakeMinimumDelegation = prefetchedData.stakeMinimumDelegation;
22018
22066
  }
22019
- const minBalanceForRentExemption = await connection.getMinimumBalanceForRentExemption(StakeProgram.space);
22020
- const minBalance = new BN(minBalanceForRentExemption + MINIMUM_ACTIVE_STAKE);
22021
- // First, collect all stake account addresses we need to check
22022
- const accountsToFetch = [];
22067
+ else {
22068
+ const [validatorListAcc, rentExemption, stakeMinimumDelegationResponse] = await Promise.all([
22069
+ connection.getAccountInfo(stakePool.validatorList),
22070
+ connection.getMinimumBalanceForRentExemption(StakeProgram.space),
22071
+ connection.getStakeMinimumDelegation(),
22072
+ ]);
22073
+ validatorListData = (_a = validatorListAcc === null || validatorListAcc === void 0 ? void 0 : validatorListAcc.data) !== null && _a !== void 0 ? _a : null;
22074
+ minBalanceForRentExemption = rentExemption;
22075
+ stakeMinimumDelegation = Number(stakeMinimumDelegationResponse.value);
22076
+ }
22077
+ if (!validatorListData) {
22078
+ throw new Error('No staked funds available for delayed unstake. Use instant unstake instead.');
22079
+ }
22080
+ const validatorList = ValidatorListLayout.decode(validatorListData);
22081
+ if (!(validatorList === null || validatorList === void 0 ? void 0 : validatorList.validators) || (validatorList === null || validatorList === void 0 ? void 0 : validatorList.validators.length) === 0) {
22082
+ throw new Error('No staked funds available for delayed unstake. Use instant unstake instead.');
22083
+ }
22084
+ // minBalance = rent + max(stake_minimum_delegation, MINIMUM_ACTIVE_STAKE)
22085
+ const minimumDelegation = Math.max(stakeMinimumDelegation, MINIMUM_ACTIVE_STAKE);
22086
+ const minBalance = new BN(minBalanceForRentExemption + minimumDelegation);
22087
+ // Threshold for has_active_stake check (ceiling division for lamports_per_pool_token)
22088
+ const lamportsPerPoolToken = stakePool.totalLamports
22089
+ .add(stakePool.poolTokenSupply)
22090
+ .sub(new BN(1))
22091
+ .div(stakePool.poolTokenSupply);
22092
+ const minimumLamportsWithTolerance = minBalance.add(lamportsPerPoolToken);
22093
+ const hasActiveStake = validatorList.validators.some(v => v.status === ValidatorStakeInfoStatus.Active
22094
+ && v.activeStakeLamports.gt(minimumLamportsWithTolerance));
22095
+ const hasTransientStake = validatorList.validators.some(v => v.status === ValidatorStakeInfoStatus.Active
22096
+ && v.transientStakeLamports.gt(minimumLamportsWithTolerance));
22097
+ // ValidatorRemoval mode: no validator above threshold
22098
+ const isValidatorRemovalMode = !hasActiveStake && !hasTransientStake;
22099
+ let accounts = [];
22023
22100
  for (const validator of validatorList.validators) {
22024
22101
  if (validator.status !== ValidatorStakeInfoStatus.Active) {
22025
22102
  continue;
22026
22103
  }
22027
22104
  const stakeAccountAddress = await findStakeProgramAddress(stakePoolProgramId, validator.voteAccountAddress, stakePoolAddress);
22028
- const isPreferred = (_a = stakePool === null || stakePool === void 0 ? void 0 : stakePool.preferredWithdrawValidatorVoteAddress) === null || _a === void 0 ? void 0 : _a.equals(validator.voteAccountAddress);
22029
- // Add active stake account if validator list indicates it has stake
22030
- if (validator.activeStakeLamports.gt(new BN(0))) {
22031
- accountsToFetch.push({
22105
+ // ValidatorRemoval: full balance available; Normal: leave minBalance
22106
+ const availableActiveLamports = isValidatorRemovalMode
22107
+ ? validator.activeStakeLamports
22108
+ : validator.activeStakeLamports.sub(minBalance);
22109
+ if (availableActiveLamports.gt(new BN(0))) {
22110
+ const isPreferred = (_b = stakePool === null || stakePool === void 0 ? void 0 : stakePool.preferredWithdrawValidatorVoteAddress) === null || _b === void 0 ? void 0 : _b.equals(validator.voteAccountAddress);
22111
+ accounts.push({
22032
22112
  type: isPreferred ? 'preferred' : 'active',
22033
22113
  voteAddress: validator.voteAccountAddress,
22034
22114
  stakeAddress: stakeAccountAddress,
22115
+ lamports: availableActiveLamports,
22035
22116
  });
22036
22117
  }
22037
- // Add transient stake account if validator list indicates it has stake
22038
- if (validator.transientStakeLamports.gt(new BN(0))) {
22118
+ const availableTransientLamports = isValidatorRemovalMode
22119
+ ? validator.transientStakeLamports
22120
+ : validator.transientStakeLamports.sub(minBalance);
22121
+ if (availableTransientLamports.gt(new BN(0))) {
22039
22122
  const transientStakeAccountAddress = await findTransientStakeProgramAddress(stakePoolProgramId, validator.voteAccountAddress, stakePoolAddress, validator.transientSeedSuffixStart);
22040
- accountsToFetch.push({
22123
+ accounts.push({
22041
22124
  type: 'transient',
22042
22125
  voteAddress: validator.voteAccountAddress,
22043
22126
  stakeAddress: transientStakeAccountAddress,
22044
- });
22045
- }
22046
- }
22047
- // Fetch all stake accounts + reserve in one batch call
22048
- const addressesToFetch = [
22049
- ...accountsToFetch.map(a => a.stakeAddress),
22050
- stakePool.reserveStake,
22051
- ];
22052
- const accountInfos = await connection.getMultipleAccountsInfo(addressesToFetch);
22053
- // Build accounts list using actual on-chain balances
22054
- let accounts = [];
22055
- for (let i = 0; i < accountsToFetch.length; i++) {
22056
- const { type, voteAddress, stakeAddress } = accountsToFetch[i];
22057
- const accountInfo = accountInfos[i];
22058
- if (!accountInfo) {
22059
- continue;
22060
- }
22061
- // Use actual on-chain balance instead of validator list value
22062
- const actualLamports = new BN(accountInfo.lamports);
22063
- const availableLamports = actualLamports.sub(minBalance);
22064
- if (availableLamports.gt(new BN(0))) {
22065
- accounts.push({
22066
- type,
22067
- voteAddress,
22068
- stakeAddress,
22069
- lamports: availableLamports,
22127
+ lamports: availableTransientLamports,
22070
22128
  });
22071
22129
  }
22072
22130
  }
22073
22131
  // Sort from highest to lowest balance
22074
22132
  accounts = accounts.sort(compareFn || ((a, b) => b.lamports.sub(a.lamports).toNumber()));
22075
- // Add reserve stake using actual balance (last item in batch fetch)
22076
- const reserveAccountInfo = accountInfos[accountInfos.length - 1];
22077
- const reserveStakeBalance = new BN(((_b = reserveAccountInfo === null || reserveAccountInfo === void 0 ? void 0 : reserveAccountInfo.lamports) !== null && _b !== void 0 ? _b : 0) - minBalanceForRentExemption);
22078
- if (reserveStakeBalance.gt(new BN(0))) {
22079
- accounts.push({
22080
- type: 'reserve',
22081
- stakeAddress: stakePool.reserveStake,
22082
- lamports: reserveStakeBalance,
22083
- });
22084
- }
22085
22133
  // Prepare the list of accounts to withdraw from
22086
22134
  const withdrawFrom = [];
22087
22135
  let remainingAmount = new BN(amount);
@@ -22090,23 +22138,24 @@ var solanaStakePool = (function (exports, node_buffer) {
22090
22138
  numerator: fee.denominator.sub(fee.numerator),
22091
22139
  denominator: fee.denominator,
22092
22140
  };
22093
- for (const type of ['preferred', 'active', 'transient', 'reserve']) {
22141
+ for (const type of ['preferred', 'active', 'transient']) {
22094
22142
  const filteredAccounts = accounts.filter(a => a.type === type);
22095
22143
  for (const { stakeAddress, voteAddress, lamports } of filteredAccounts) {
22096
- if (lamports.lte(minBalance) && type === 'transient') {
22097
- continue;
22098
- }
22099
22144
  let availableForWithdrawal = calcPoolTokensForDeposit(stakePool, lamports);
22100
22145
  if (!skipFee && !inverseFee.numerator.isZero()) {
22101
22146
  availableForWithdrawal = availableForWithdrawal
22102
22147
  .mul(inverseFee.denominator)
22103
22148
  .div(inverseFee.numerator);
22104
22149
  }
22150
+ // In ValidatorRemoval mode, must withdraw full validator balance (no partial)
22151
+ // Skip if remaining amount is less than full validator balance
22152
+ if (isValidatorRemovalMode && remainingAmount.lt(availableForWithdrawal)) {
22153
+ continue;
22154
+ }
22105
22155
  const poolAmount = BN.min(availableForWithdrawal, remainingAmount);
22106
22156
  if (poolAmount.lte(new BN(0))) {
22107
22157
  continue;
22108
22158
  }
22109
- // Those accounts will be withdrawn completely with `claim` instruction
22110
22159
  withdrawFrom.push({ stakeAddress, voteAddress, poolAmount });
22111
22160
  remainingAmount = remainingAmount.sub(poolAmount);
22112
22161
  if (remainingAmount.isZero()) {
@@ -22119,7 +22168,23 @@ var solanaStakePool = (function (exports, node_buffer) {
22119
22168
  }
22120
22169
  // Not enough stake to withdraw the specified amount
22121
22170
  if (remainingAmount.gt(new BN(0))) {
22122
- throw new Error(`No stake accounts found in this pool with enough balance to withdraw ${lamportsToSol(amount)} pool tokens.`);
22171
+ if (allowPartial) {
22172
+ const delayedAmount = amount.sub(remainingAmount);
22173
+ return {
22174
+ withdrawAccounts: withdrawFrom,
22175
+ delayedAmount,
22176
+ remainingAmount,
22177
+ };
22178
+ }
22179
+ const availableAmount = amount.sub(remainingAmount);
22180
+ throw new Error(`Not enough staked funds for delayed unstake. Requested ${lamportsToSol(amount)} iFOGO, but only ${lamportsToSol(availableAmount)} available. Use instant unstake for the remaining amount.`);
22181
+ }
22182
+ if (allowPartial) {
22183
+ return {
22184
+ withdrawAccounts: withdrawFrom,
22185
+ delayedAmount: amount,
22186
+ remainingAmount: new BN(0),
22187
+ };
22123
22188
  }
22124
22189
  return withdrawFrom;
22125
22190
  }
@@ -22168,11 +22233,50 @@ var solanaStakePool = (function (exports, node_buffer) {
22168
22233
  return result;
22169
22234
  }
22170
22235
 
22236
+ /**
22237
+ * Converts various numeric types to BN for safe large number handling.
22238
+ * @internal
22239
+ */
22240
+ function toBN(value) {
22241
+ if (BN.isBN(value)) {
22242
+ return value;
22243
+ }
22244
+ if (typeof value === 'bigint') {
22245
+ return new BN(value.toString());
22246
+ }
22247
+ if (typeof value === 'string') {
22248
+ // Validate string is a valid non-negative integer
22249
+ const trimmed = value.trim();
22250
+ if (!/^\d+$/.test(trimmed)) {
22251
+ throw new Error(`Invalid amount string: "${value}". Must be a non-negative integer.`);
22252
+ }
22253
+ return new BN(trimmed);
22254
+ }
22255
+ if (typeof value === 'number') {
22256
+ if (!Number.isFinite(value)) {
22257
+ throw new Error('Invalid amount: must be a finite number');
22258
+ }
22259
+ if (value < 0) {
22260
+ throw new Error('Invalid amount: must be non-negative');
22261
+ }
22262
+ if (!Number.isInteger(value)) {
22263
+ throw new Error('Invalid amount: must be an integer (lamports)');
22264
+ }
22265
+ // CRITICAL: Numbers > MAX_SAFE_INTEGER have already lost precision
22266
+ // We throw an error instead of silently corrupting data
22267
+ if (value > Number.MAX_SAFE_INTEGER) {
22268
+ throw new Error(`Amount ${value} exceeds Number.MAX_SAFE_INTEGER (9,007,199,254,740,991). ` +
22269
+ `Use BigInt or BN for large values to avoid precision loss.`);
22270
+ }
22271
+ return new BN(value);
22272
+ }
22273
+ throw new Error(`Invalid amount type: ${typeof value}`);
22274
+ }
22171
22275
  // 'UpdateTokenMetadata' and 'CreateTokenMetadata' have dynamic layouts
22172
22276
  const MOVE_STAKE_LAYOUT = LayoutExports$1.struct([
22173
22277
  LayoutExports$1.u8('instruction'),
22174
- LayoutExports$1.ns64('lamports'),
22175
- LayoutExports$1.ns64('transientStakeSeed'),
22278
+ u64Instruction('lamports'),
22279
+ u64Instruction('transientStakeSeed'),
22176
22280
  ]);
22177
22281
  const UPDATE_VALIDATOR_LIST_BALANCE_LAYOUT = LayoutExports$1.struct([
22178
22282
  LayoutExports$1.u8('instruction'),
@@ -22247,7 +22351,7 @@ var solanaStakePool = (function (exports, node_buffer) {
22247
22351
  index: 10,
22248
22352
  layout: LayoutExports$1.struct([
22249
22353
  LayoutExports$1.u8('instruction'),
22250
- LayoutExports$1.ns64('poolTokens'),
22354
+ u64Instruction('poolTokens'),
22251
22355
  ]),
22252
22356
  },
22253
22357
  /// Deposit SOL directly into the pool's reserve account. The output is a "pool" token
@@ -22256,7 +22360,7 @@ var solanaStakePool = (function (exports, node_buffer) {
22256
22360
  index: 14,
22257
22361
  layout: LayoutExports$1.struct([
22258
22362
  LayoutExports$1.u8('instruction'),
22259
- LayoutExports$1.ns64('lamports'),
22363
+ u64Instruction('lamports'),
22260
22364
  ]),
22261
22365
  },
22262
22366
  /// Withdraw SOL directly from the pool's reserve account. Fails if the
@@ -22265,25 +22369,25 @@ var solanaStakePool = (function (exports, node_buffer) {
22265
22369
  index: 16,
22266
22370
  layout: LayoutExports$1.struct([
22267
22371
  LayoutExports$1.u8('instruction'),
22268
- LayoutExports$1.ns64('poolTokens'),
22372
+ u64Instruction('poolTokens'),
22269
22373
  ]),
22270
22374
  },
22271
22375
  IncreaseAdditionalValidatorStake: {
22272
22376
  index: 19,
22273
22377
  layout: LayoutExports$1.struct([
22274
22378
  LayoutExports$1.u8('instruction'),
22275
- LayoutExports$1.ns64('lamports'),
22276
- LayoutExports$1.ns64('transientStakeSeed'),
22277
- LayoutExports$1.ns64('ephemeralStakeSeed'),
22379
+ u64Instruction('lamports'),
22380
+ u64Instruction('transientStakeSeed'),
22381
+ u64Instruction('ephemeralStakeSeed'),
22278
22382
  ]),
22279
22383
  },
22280
22384
  DecreaseAdditionalValidatorStake: {
22281
22385
  index: 20,
22282
22386
  layout: LayoutExports$1.struct([
22283
22387
  LayoutExports$1.u8('instruction'),
22284
- LayoutExports$1.ns64('lamports'),
22285
- LayoutExports$1.ns64('transientStakeSeed'),
22286
- LayoutExports$1.ns64('ephemeralStakeSeed'),
22388
+ u64Instruction('lamports'),
22389
+ u64Instruction('transientStakeSeed'),
22390
+ u64Instruction('ephemeralStakeSeed'),
22287
22391
  ]),
22288
22392
  },
22289
22393
  DecreaseValidatorStakeWithReserve: {
@@ -22298,62 +22402,62 @@ var solanaStakePool = (function (exports, node_buffer) {
22298
22402
  index: 23,
22299
22403
  layout: LayoutExports$1.struct([
22300
22404
  LayoutExports$1.u8('instruction'),
22301
- LayoutExports$1.ns64('lamports'),
22405
+ u64Instruction('lamports'),
22302
22406
  ]),
22303
22407
  },
22304
22408
  WithdrawStakeWithSlippage: {
22305
22409
  index: 24,
22306
22410
  layout: LayoutExports$1.struct([
22307
22411
  LayoutExports$1.u8('instruction'),
22308
- LayoutExports$1.ns64('poolTokensIn'),
22309
- LayoutExports$1.ns64('minimumLamportsOut'),
22412
+ u64Instruction('poolTokensIn'),
22413
+ u64Instruction('minimumLamportsOut'),
22310
22414
  ]),
22311
22415
  },
22312
22416
  DepositSolWithSlippage: {
22313
22417
  index: 25,
22314
22418
  layout: LayoutExports$1.struct([
22315
22419
  LayoutExports$1.u8('instruction'),
22316
- LayoutExports$1.ns64('lamports'),
22420
+ u64Instruction('lamports'),
22317
22421
  ]),
22318
22422
  },
22319
22423
  WithdrawSolWithSlippage: {
22320
22424
  index: 26,
22321
22425
  layout: LayoutExports$1.struct([
22322
22426
  LayoutExports$1.u8('instruction'),
22323
- LayoutExports$1.ns64('lamports'),
22427
+ u64Instruction('lamports'),
22324
22428
  ]),
22325
22429
  },
22326
22430
  DepositWsolWithSession: {
22327
22431
  index: 27,
22328
22432
  layout: LayoutExports$1.struct([
22329
22433
  LayoutExports$1.u8('instruction'),
22330
- LayoutExports$1.ns64('lamportsIn'),
22331
- LayoutExports$1.ns64('minimumPoolTokensOut'),
22434
+ u64Instruction('lamportsIn'),
22435
+ u64Instruction('minimumPoolTokensOut'),
22332
22436
  ]),
22333
22437
  },
22334
22438
  WithdrawWsolWithSession: {
22335
22439
  index: 28,
22336
22440
  layout: LayoutExports$1.struct([
22337
22441
  LayoutExports$1.u8('instruction'),
22338
- LayoutExports$1.ns64('poolTokensIn'),
22339
- LayoutExports$1.ns64('minimumLamportsOut'),
22442
+ u64Instruction('poolTokensIn'),
22443
+ u64Instruction('minimumLamportsOut'),
22340
22444
  ]),
22341
22445
  },
22342
22446
  WithdrawStakeWithSession: {
22343
22447
  index: 29,
22344
22448
  layout: LayoutExports$1.struct([
22345
22449
  LayoutExports$1.u8('instruction'),
22346
- LayoutExports$1.ns64('poolTokensIn'),
22347
- LayoutExports$1.ns64('minimumLamportsOut'),
22348
- LayoutExports$1.ns64('userStakeSeed'),
22450
+ u64Instruction('poolTokensIn'),
22451
+ u64Instruction('minimumLamportsOut'),
22452
+ u64Instruction('userStakeSeed'),
22349
22453
  ]),
22350
22454
  },
22351
22455
  WithdrawFromStakeAccountWithSession: {
22352
22456
  index: 30,
22353
22457
  layout: LayoutExports$1.struct([
22354
22458
  LayoutExports$1.u8('instruction'),
22355
- LayoutExports$1.ns64('lamports'),
22356
- LayoutExports$1.ns64('userStakeSeed'),
22459
+ u64Instruction('lamports'),
22460
+ u64Instruction('userStakeSeed'),
22357
22461
  ]),
22358
22462
  },
22359
22463
  });
@@ -22485,7 +22589,10 @@ var solanaStakePool = (function (exports, node_buffer) {
22485
22589
  static increaseValidatorStake(params) {
22486
22590
  const { programId, stakePool, staker, withdrawAuthority, validatorList, reserveStake, transientStake, validatorStake, validatorVote, lamports, transientStakeSeed, } = params;
22487
22591
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.IncreaseValidatorStake;
22488
- const data = encodeData(type, { lamports, transientStakeSeed });
22592
+ const data = encodeData(type, {
22593
+ lamports: toBN(lamports),
22594
+ transientStakeSeed: toBN(transientStakeSeed),
22595
+ });
22489
22596
  const keys = [
22490
22597
  { pubkey: stakePool, isSigner: false, isWritable: false },
22491
22598
  { pubkey: staker, isSigner: true, isWritable: false },
@@ -22515,7 +22622,11 @@ var solanaStakePool = (function (exports, node_buffer) {
22515
22622
  static increaseAdditionalValidatorStake(params) {
22516
22623
  const { programId, stakePool, staker, withdrawAuthority, validatorList, reserveStake, transientStake, validatorStake, validatorVote, lamports, transientStakeSeed, ephemeralStake, ephemeralStakeSeed, } = params;
22517
22624
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.IncreaseAdditionalValidatorStake;
22518
- const data = encodeData(type, { lamports, transientStakeSeed, ephemeralStakeSeed });
22625
+ const data = encodeData(type, {
22626
+ lamports: toBN(lamports),
22627
+ transientStakeSeed: toBN(transientStakeSeed),
22628
+ ephemeralStakeSeed: toBN(ephemeralStakeSeed),
22629
+ });
22519
22630
  const keys = [
22520
22631
  { pubkey: stakePool, isSigner: false, isWritable: false },
22521
22632
  { pubkey: staker, isSigner: true, isWritable: false },
@@ -22545,7 +22656,10 @@ var solanaStakePool = (function (exports, node_buffer) {
22545
22656
  static decreaseValidatorStake(params) {
22546
22657
  const { programId, stakePool, staker, withdrawAuthority, validatorList, validatorStake, transientStake, lamports, transientStakeSeed, } = params;
22547
22658
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DecreaseValidatorStake;
22548
- const data = encodeData(type, { lamports, transientStakeSeed });
22659
+ const data = encodeData(type, {
22660
+ lamports: toBN(lamports),
22661
+ transientStakeSeed: toBN(transientStakeSeed),
22662
+ });
22549
22663
  const keys = [
22550
22664
  { pubkey: stakePool, isSigner: false, isWritable: false },
22551
22665
  { pubkey: staker, isSigner: true, isWritable: false },
@@ -22571,7 +22685,10 @@ var solanaStakePool = (function (exports, node_buffer) {
22571
22685
  static decreaseValidatorStakeWithReserve(params) {
22572
22686
  const { programId, stakePool, staker, withdrawAuthority, validatorList, reserveStake, validatorStake, transientStake, lamports, transientStakeSeed, } = params;
22573
22687
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DecreaseValidatorStakeWithReserve;
22574
- const data = encodeData(type, { lamports, transientStakeSeed });
22688
+ const data = encodeData(type, {
22689
+ lamports: toBN(lamports),
22690
+ transientStakeSeed: toBN(transientStakeSeed),
22691
+ });
22575
22692
  const keys = [
22576
22693
  { pubkey: stakePool, isSigner: false, isWritable: false },
22577
22694
  { pubkey: staker, isSigner: true, isWritable: false },
@@ -22598,7 +22715,11 @@ var solanaStakePool = (function (exports, node_buffer) {
22598
22715
  static decreaseAdditionalValidatorStake(params) {
22599
22716
  const { programId, stakePool, staker, withdrawAuthority, validatorList, reserveStake, validatorStake, transientStake, lamports, transientStakeSeed, ephemeralStakeSeed, ephemeralStake, } = params;
22600
22717
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DecreaseAdditionalValidatorStake;
22601
- const data = encodeData(type, { lamports, transientStakeSeed, ephemeralStakeSeed });
22718
+ const data = encodeData(type, {
22719
+ lamports: toBN(lamports),
22720
+ transientStakeSeed: toBN(transientStakeSeed),
22721
+ ephemeralStakeSeed: toBN(ephemeralStakeSeed),
22722
+ });
22602
22723
  const keys = [
22603
22724
  { pubkey: stakePool, isSigner: false, isWritable: false },
22604
22725
  { pubkey: staker, isSigner: true, isWritable: false },
@@ -22655,7 +22776,7 @@ var solanaStakePool = (function (exports, node_buffer) {
22655
22776
  static depositSol(params) {
22656
22777
  const { programId, stakePool, withdrawAuthority, depositAuthority, reserveStake, fundingAccount, destinationPoolAccount, managerFeeAccount, referralPoolAccount, poolMint, lamports, } = params;
22657
22778
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DepositSol;
22658
- const data = encodeData(type, { lamports });
22779
+ const data = encodeData(type, { lamports: toBN(lamports) });
22659
22780
  const keys = [
22660
22781
  { pubkey: stakePool, isSigner: false, isWritable: true },
22661
22782
  { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
@@ -22688,8 +22809,8 @@ var solanaStakePool = (function (exports, node_buffer) {
22688
22809
  var _a;
22689
22810
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DepositWsolWithSession;
22690
22811
  const data = encodeData(type, {
22691
- lamportsIn: params.lamportsIn,
22692
- minimumPoolTokensOut: params.minimumPoolTokensOut,
22812
+ lamportsIn: toBN(params.lamportsIn),
22813
+ minimumPoolTokensOut: toBN(params.minimumPoolTokensOut),
22693
22814
  });
22694
22815
  const keys = [
22695
22816
  { pubkey: params.stakePool, isSigner: false, isWritable: true },
@@ -22731,7 +22852,7 @@ var solanaStakePool = (function (exports, node_buffer) {
22731
22852
  static withdrawStake(params) {
22732
22853
  const { programId, stakePool, validatorList, withdrawAuthority, validatorStake, destinationStake, destinationStakeAuthority, sourceTransferAuthority, sourcePoolAccount, managerFeeAccount, poolMint, poolTokens, } = params;
22733
22854
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawStake;
22734
- const data = encodeData(type, { poolTokens });
22855
+ const data = encodeData(type, { poolTokens: toBN(poolTokens) });
22735
22856
  const keys = [
22736
22857
  { pubkey: stakePool, isSigner: false, isWritable: true },
22737
22858
  { pubkey: validatorList, isSigner: false, isWritable: true },
@@ -22759,7 +22880,7 @@ var solanaStakePool = (function (exports, node_buffer) {
22759
22880
  static withdrawSol(params) {
22760
22881
  const { programId, stakePool, withdrawAuthority, sourceTransferAuthority, sourcePoolAccount, reserveStake, destinationSystemAccount, managerFeeAccount, solWithdrawAuthority, poolMint, poolTokens, } = params;
22761
22882
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawSol;
22762
- const data = encodeData(type, { poolTokens });
22883
+ const data = encodeData(type, { poolTokens: toBN(poolTokens) });
22763
22884
  const keys = [
22764
22885
  { pubkey: stakePool, isSigner: false, isWritable: true },
22765
22886
  { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
@@ -22794,8 +22915,8 @@ var solanaStakePool = (function (exports, node_buffer) {
22794
22915
  static withdrawWsolWithSession(params) {
22795
22916
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawWsolWithSession;
22796
22917
  const data = encodeData(type, {
22797
- poolTokensIn: params.poolTokensIn,
22798
- minimumLamportsOut: params.minimumLamportsOut,
22918
+ poolTokensIn: toBN(params.poolTokensIn),
22919
+ minimumLamportsOut: toBN(params.minimumLamportsOut),
22799
22920
  });
22800
22921
  const keys = [
22801
22922
  { pubkey: params.stakePool, isSigner: false, isWritable: true },
@@ -22832,14 +22953,14 @@ var solanaStakePool = (function (exports, node_buffer) {
22832
22953
  }
22833
22954
  /**
22834
22955
  * Creates a transaction instruction to withdraw stake from a stake pool using a Fogo session.
22835
- * The stake account is created as a PDA and rent is paid by the payer (typically paymaster).
22956
+ * The stake account is created as a PDA and rent is funded from the reserve.
22836
22957
  */
22837
22958
  static withdrawStakeWithSession(params) {
22838
22959
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawStakeWithSession;
22839
22960
  const data = encodeData(type, {
22840
- poolTokensIn: params.poolTokensIn,
22841
- minimumLamportsOut: params.minimumLamportsOut,
22842
- userStakeSeed: params.userStakeSeed,
22961
+ poolTokensIn: toBN(params.poolTokensIn),
22962
+ minimumLamportsOut: toBN(params.minimumLamportsOut),
22963
+ userStakeSeed: toBN(params.userStakeSeed),
22843
22964
  });
22844
22965
  const keys = [
22845
22966
  { pubkey: params.stakePool, isSigner: false, isWritable: true },
@@ -22847,17 +22968,19 @@ var solanaStakePool = (function (exports, node_buffer) {
22847
22968
  { pubkey: params.withdrawAuthority, isSigner: false, isWritable: false },
22848
22969
  { pubkey: params.stakeToSplit, isSigner: false, isWritable: true },
22849
22970
  { pubkey: params.stakeToReceive, isSigner: false, isWritable: true },
22850
- { pubkey: params.sessionSigner, isSigner: true, isWritable: false }, // user_stake_authority_info (signer_or_session)
22851
- { pubkey: params.sessionSigner, isSigner: false, isWritable: false }, // user_transfer_authority_info (not used in session path)
22971
+ { pubkey: params.sessionSigner, isSigner: true, isWritable: false }, // user_stake_authority_info
22972
+ { pubkey: params.sessionSigner, isSigner: false, isWritable: false }, // user_transfer_authority_info (unused in session path)
22852
22973
  { pubkey: params.burnFromPool, isSigner: false, isWritable: true },
22853
22974
  { pubkey: params.managerFeeAccount, isSigner: false, isWritable: true },
22854
22975
  { pubkey: params.poolMint, isSigner: false, isWritable: true },
22855
22976
  { pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
22856
22977
  { pubkey: params.tokenProgramId, isSigner: false, isWritable: false },
22857
22978
  { pubkey: StakeProgram.programId, isSigner: false, isWritable: false },
22979
+ // Session-specific accounts
22858
22980
  { pubkey: params.programSigner, isSigner: false, isWritable: false },
22859
22981
  { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
22860
- { pubkey: params.payer, isSigner: true, isWritable: true },
22982
+ { pubkey: params.reserveStake, isSigner: false, isWritable: true },
22983
+ { pubkey: SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
22861
22984
  ];
22862
22985
  return new TransactionInstruction({
22863
22986
  programId: params.programId,
@@ -22872,8 +22995,8 @@ var solanaStakePool = (function (exports, node_buffer) {
22872
22995
  static withdrawFromStakeAccountWithSession(params) {
22873
22996
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawFromStakeAccountWithSession;
22874
22997
  const data = encodeData(type, {
22875
- lamports: params.lamports,
22876
- userStakeSeed: params.userStakeSeed,
22998
+ lamports: toBN(params.lamports),
22999
+ userStakeSeed: toBN(params.userStakeSeed),
22877
23000
  });
22878
23001
  const keys = [
22879
23002
  { pubkey: params.userStakeAccount, isSigner: false, isWritable: true },
@@ -23162,10 +23285,18 @@ var solanaStakePool = (function (exports, node_buffer) {
23162
23285
  if (!skipBalanceCheck) {
23163
23286
  const tokenAccountInfo = await connection.getTokenAccountBalance(wsolTokenAccount, 'confirmed');
23164
23287
  const wsolBalance = tokenAccountInfo
23165
- ? parseInt(tokenAccountInfo.value.amount)
23166
- : 0;
23167
- if (wsolBalance < lamports) {
23168
- throw new Error(`Not enough WSOL to deposit into pool. Maximum deposit amount is ${lamportsToSol(wsolBalance)} WSOL.`);
23288
+ ? BigInt(tokenAccountInfo.value.amount)
23289
+ : BigInt(0);
23290
+ // Convert lamports to BigInt for comparison
23291
+ const lamportsBigInt = typeof lamports === 'bigint'
23292
+ ? lamports
23293
+ : typeof lamports === 'string'
23294
+ ? BigInt(lamports)
23295
+ : BN.isBN(lamports)
23296
+ ? BigInt(lamports.toString())
23297
+ : BigInt(lamports);
23298
+ if (wsolBalance < lamportsBigInt) {
23299
+ throw new Error(`Not enough WSOL to deposit into pool. Maximum deposit amount is ${lamportsToSol(Number(wsolBalance))} WSOL.`);
23169
23300
  }
23170
23301
  }
23171
23302
  const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
@@ -23211,7 +23342,15 @@ var solanaStakePool = (function (exports, node_buffer) {
23211
23342
  */
23212
23343
  async function depositSol(connection, stakePoolAddress, from, lamports, destinationTokenAccount, referrerTokenAccount, depositAuthority) {
23213
23344
  const fromBalance = await connection.getBalance(from, 'confirmed');
23214
- if (fromBalance < lamports) {
23345
+ // Convert lamports to BigInt for comparison
23346
+ const lamportsBigInt = typeof lamports === 'bigint'
23347
+ ? lamports
23348
+ : typeof lamports === 'string'
23349
+ ? BigInt(lamports)
23350
+ : BN.isBN(lamports)
23351
+ ? BigInt(lamports.toString())
23352
+ : BigInt(lamports);
23353
+ if (BigInt(fromBalance) < lamportsBigInt) {
23215
23354
  throw new Error(`Not enough SOL to deposit into pool. Maximum deposit amount is ${lamportsToSol(fromBalance)} SOL.`);
23216
23355
  }
23217
23356
  const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
@@ -23225,7 +23364,7 @@ var solanaStakePool = (function (exports, node_buffer) {
23225
23364
  instructions.push(SystemProgram.transfer({
23226
23365
  fromPubkey: from,
23227
23366
  toPubkey: userSolTransfer.publicKey,
23228
- lamports,
23367
+ lamports: lamportsBigInt,
23229
23368
  }));
23230
23369
  // Create token account if not specified
23231
23370
  if (!destinationTokenAccount) {
@@ -23597,37 +23736,51 @@ var solanaStakePool = (function (exports, node_buffer) {
23597
23736
  * Withdraws stake from a stake pool using a Fogo session.
23598
23737
  *
23599
23738
  * The on-chain program creates stake account PDAs. The rent for these accounts
23600
- * is paid by the payer (typically the paymaster), not deducted from the user's withdrawal.
23739
+ * is funded from the reserve stake.
23601
23740
  *
23602
23741
  * @param connection - Solana connection
23603
23742
  * @param stakePoolAddress - The stake pool to withdraw from
23604
23743
  * @param signerOrSession - The session signer public key
23605
23744
  * @param userPubkey - User's wallet (used for PDA derivation and token ownership)
23606
- * @param payer - Payer for stake account rent (typically paymaster)
23607
23745
  * @param amount - Amount of pool tokens to withdraw
23608
23746
  * @param userStakeSeedStart - Starting seed for user stake PDA derivation (default: 0)
23609
23747
  * @param useReserve - Whether to withdraw from reserve (default: false)
23610
23748
  * @param voteAccountAddress - Optional specific validator to withdraw from
23611
23749
  * @param minimumLamportsOut - Minimum lamports to receive (slippage protection)
23612
23750
  * @param validatorComparator - Optional comparator for validator selection
23751
+ * @param allowPartial - If true, returns partial results instead of throwing when not enough stake available
23613
23752
  */
23614
- async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, payer, amount, userStakeSeedStart = 0, useReserve = false, voteAccountAddress, minimumLamportsOut = 0, validatorComparator) {
23615
- const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
23753
+ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, amount, userStakeSeedStart = 0, useReserve = false, voteAccountAddress, minimumLamportsOut = 0, validatorComparator, allowPartial = false) {
23754
+ var _c;
23616
23755
  const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
23756
+ // First fetch: get stake pool to know other account addresses
23757
+ const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
23617
23758
  const stakePool = stakePoolAccount.account.data;
23618
23759
  const poolTokens = solToLamports(amount);
23619
23760
  const poolAmount = new BN(poolTokens);
23620
23761
  const poolTokenAccount = getAssociatedTokenAddressSync(stakePool.poolMint, userPubkey);
23621
- const tokenAccount = await getAccount(connection, poolTokenAccount);
23762
+ // Second fetch: get ALL remaining data in parallel
23763
+ const [tokenAccount, stakeAccountRentExemption, validatorListAcc, stakeMinimumDelegationResponse] = await Promise.all([
23764
+ getAccount(connection, poolTokenAccount),
23765
+ connection.getMinimumBalanceForRentExemption(StakeProgram.space),
23766
+ connection.getAccountInfo(stakePool.validatorList),
23767
+ connection.getStakeMinimumDelegation(),
23768
+ ]);
23769
+ // Pre-fetch data to avoid duplicate RPC calls in prepareWithdrawAccounts
23770
+ const prefetchedData = {
23771
+ validatorListData: (_c = validatorListAcc === null || validatorListAcc === void 0 ? void 0 : validatorListAcc.data) !== null && _c !== void 0 ? _c : null,
23772
+ minBalanceForRentExemption: stakeAccountRentExemption,
23773
+ stakeMinimumDelegation: Number(stakeMinimumDelegationResponse.value),
23774
+ };
23622
23775
  if (tokenAccount.amount < poolTokens) {
23623
23776
  throw new Error(`Not enough token balance to withdraw ${amount} pool tokens.
23624
23777
  Maximum withdraw amount is ${lamportsToSol(tokenAccount.amount)} pool tokens.`);
23625
23778
  }
23626
23779
  const [programSigner] = PublicKey.findProgramAddressSync([Buffer.from('fogo_session_program_signer')], stakePoolProgramId);
23627
23780
  const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
23628
- const stakeAccountRentExemption = await connection.getMinimumBalanceForRentExemption(StakeProgram.space);
23629
23781
  // Determine which stake accounts to withdraw from
23630
23782
  const withdrawAccounts = [];
23783
+ let partialRemainingAmount;
23631
23784
  if (useReserve) {
23632
23785
  withdrawAccounts.push({
23633
23786
  stakeAddress: stakePool.reserveStake,
@@ -23658,7 +23811,14 @@ var solanaStakePool = (function (exports, node_buffer) {
23658
23811
  }
23659
23812
  else {
23660
23813
  // Get the list of accounts to withdraw from automatically
23661
- withdrawAccounts.push(...(await prepareWithdrawAccounts(connection, stakePool, stakePoolAddress, poolAmount, validatorComparator, poolTokenAccount.equals(stakePool.managerFeeAccount))));
23814
+ if (allowPartial) {
23815
+ const result = await prepareWithdrawAccounts(connection, stakePool, stakePoolAddress, poolAmount, validatorComparator, poolTokenAccount.equals(stakePool.managerFeeAccount), true, prefetchedData);
23816
+ withdrawAccounts.push(...result.withdrawAccounts);
23817
+ partialRemainingAmount = result.remainingAmount;
23818
+ }
23819
+ else {
23820
+ withdrawAccounts.push(...(await prepareWithdrawAccounts(connection, stakePool, stakePoolAddress, poolAmount, validatorComparator, poolTokenAccount.equals(stakePool.managerFeeAccount), undefined, prefetchedData)));
23821
+ }
23662
23822
  }
23663
23823
  const instructions = [];
23664
23824
  const stakeAccountPubkeys = [];
@@ -23675,7 +23835,7 @@ var solanaStakePool = (function (exports, node_buffer) {
23675
23835
  const stakeReceiverPubkey = findUserStakeProgramAddress(stakePoolProgramId, userPubkey, userStakeSeed);
23676
23836
  stakeAccountPubkeys.push(stakeReceiverPubkey);
23677
23837
  userStakeSeeds.push(userStakeSeed);
23678
- // The on-chain program creates the stake account PDA and rent is paid by payer.
23838
+ // The on-chain program creates the stake account PDA and rent is funded from reserve.
23679
23839
  instructions.push(StakePoolInstruction.withdrawStakeWithSession({
23680
23840
  programId: stakePoolProgramId,
23681
23841
  stakePool: stakePoolAddress,
@@ -23689,7 +23849,7 @@ var solanaStakePool = (function (exports, node_buffer) {
23689
23849
  poolMint: stakePool.poolMint,
23690
23850
  tokenProgramId: stakePool.tokenProgramId,
23691
23851
  programSigner,
23692
- payer,
23852
+ reserveStake: stakePool.reserveStake,
23693
23853
  poolTokensIn: withdrawAccount.poolAmount.toNumber(),
23694
23854
  minimumLamportsOut,
23695
23855
  userStakeSeed,
@@ -23700,6 +23860,7 @@ var solanaStakePool = (function (exports, node_buffer) {
23700
23860
  instructions,
23701
23861
  stakeAccountPubkeys,
23702
23862
  userStakeSeeds,
23863
+ remainingPoolTokens: partialRemainingAmount ? lamportsToSol(partialRemainingAmount) : 0,
23703
23864
  };
23704
23865
  }
23705
23866
  async function addValidatorToPool(connection, stakePoolAddress, validatorVote, seed) {