@ignitionfi/fogo-stake-pool 1.0.2 → 1.0.3
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.browser.cjs.js +116 -71
- package/dist/index.browser.cjs.js.map +1 -1
- package/dist/index.browser.esm.js +116 -71
- package/dist/index.browser.esm.js.map +1 -1
- package/dist/index.cjs.js +116 -71
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.esm.js +116 -71
- package/dist/index.esm.js.map +1 -1
- package/dist/index.iife.js +116 -71
- package/dist/index.iife.js.map +1 -1
- package/dist/index.iife.min.js +1 -1
- package/dist/index.iife.min.js.map +1 -1
- package/dist/instructions.d.ts +3 -3
- package/dist/utils/stake.d.ts +19 -2
- package/package.json +1 -1
- package/src/constants.ts +1 -1
- package/src/index.ts +47 -13
- package/src/instructions.ts +8 -6
- package/src/utils/stake.ts +133 -75
|
@@ -827,80 +827,84 @@ async function getValidatorListAccount(connection, pubkey) {
|
|
|
827
827
|
},
|
|
828
828
|
};
|
|
829
829
|
}
|
|
830
|
-
async function prepareWithdrawAccounts(connection, stakePool, stakePoolAddress, amount, compareFn, skipFee) {
|
|
830
|
+
async function prepareWithdrawAccounts(connection, stakePool, stakePoolAddress, amount, compareFn, skipFee, allowPartial, prefetchedData) {
|
|
831
831
|
var _a, _b;
|
|
832
832
|
const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
833
|
+
// Use prefetched data if available, otherwise fetch from RPC
|
|
834
|
+
let validatorListData;
|
|
835
|
+
let minBalanceForRentExemption;
|
|
836
|
+
let stakeMinimumDelegation;
|
|
837
|
+
if (prefetchedData) {
|
|
838
|
+
validatorListData = prefetchedData.validatorListData;
|
|
839
|
+
minBalanceForRentExemption = prefetchedData.minBalanceForRentExemption;
|
|
840
|
+
stakeMinimumDelegation = prefetchedData.stakeMinimumDelegation;
|
|
837
841
|
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
+
else {
|
|
843
|
+
const [validatorListAcc, rentExemption, stakeMinimumDelegationResponse] = await Promise.all([
|
|
844
|
+
connection.getAccountInfo(stakePool.validatorList),
|
|
845
|
+
connection.getMinimumBalanceForRentExemption(web3_js.StakeProgram.space),
|
|
846
|
+
connection.getStakeMinimumDelegation(),
|
|
847
|
+
]);
|
|
848
|
+
validatorListData = (_a = validatorListAcc === null || validatorListAcc === void 0 ? void 0 : validatorListAcc.data) !== null && _a !== void 0 ? _a : null;
|
|
849
|
+
minBalanceForRentExemption = rentExemption;
|
|
850
|
+
stakeMinimumDelegation = Number(stakeMinimumDelegationResponse.value);
|
|
851
|
+
}
|
|
852
|
+
if (!validatorListData) {
|
|
853
|
+
throw new Error('No staked funds available for delayed unstake. Use instant unstake instead.');
|
|
854
|
+
}
|
|
855
|
+
const validatorList = ValidatorListLayout.decode(validatorListData);
|
|
856
|
+
if (!(validatorList === null || validatorList === void 0 ? void 0 : validatorList.validators) || (validatorList === null || validatorList === void 0 ? void 0 : validatorList.validators.length) === 0) {
|
|
857
|
+
throw new Error('No staked funds available for delayed unstake. Use instant unstake instead.');
|
|
858
|
+
}
|
|
859
|
+
// minBalance = rent + max(stake_minimum_delegation, MINIMUM_ACTIVE_STAKE)
|
|
860
|
+
const minimumDelegation = Math.max(stakeMinimumDelegation, MINIMUM_ACTIVE_STAKE);
|
|
861
|
+
const minBalance = new BN(minBalanceForRentExemption + minimumDelegation);
|
|
862
|
+
// Threshold for has_active_stake check (ceiling division for lamports_per_pool_token)
|
|
863
|
+
const lamportsPerPoolToken = stakePool.totalLamports
|
|
864
|
+
.add(stakePool.poolTokenSupply)
|
|
865
|
+
.sub(new BN(1))
|
|
866
|
+
.div(stakePool.poolTokenSupply);
|
|
867
|
+
const minimumLamportsWithTolerance = minBalance.add(lamportsPerPoolToken);
|
|
868
|
+
const hasActiveStake = validatorList.validators.some(v => v.status === ValidatorStakeInfoStatus.Active
|
|
869
|
+
&& v.activeStakeLamports.gt(minimumLamportsWithTolerance));
|
|
870
|
+
const hasTransientStake = validatorList.validators.some(v => v.status === ValidatorStakeInfoStatus.Active
|
|
871
|
+
&& v.transientStakeLamports.gt(minimumLamportsWithTolerance));
|
|
872
|
+
// ValidatorRemoval mode: no validator above threshold
|
|
873
|
+
const isValidatorRemovalMode = !hasActiveStake && !hasTransientStake;
|
|
874
|
+
let accounts = [];
|
|
842
875
|
for (const validator of validatorList.validators) {
|
|
843
876
|
if (validator.status !== ValidatorStakeInfoStatus.Active) {
|
|
844
877
|
continue;
|
|
845
878
|
}
|
|
846
879
|
const stakeAccountAddress = await findStakeProgramAddress(stakePoolProgramId, validator.voteAccountAddress, stakePoolAddress);
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
880
|
+
// ValidatorRemoval: full balance available; Normal: leave minBalance
|
|
881
|
+
const availableActiveLamports = isValidatorRemovalMode
|
|
882
|
+
? validator.activeStakeLamports
|
|
883
|
+
: validator.activeStakeLamports.sub(minBalance);
|
|
884
|
+
if (availableActiveLamports.gt(new BN(0))) {
|
|
885
|
+
const isPreferred = (_b = stakePool === null || stakePool === void 0 ? void 0 : stakePool.preferredWithdrawValidatorVoteAddress) === null || _b === void 0 ? void 0 : _b.equals(validator.voteAccountAddress);
|
|
886
|
+
accounts.push({
|
|
851
887
|
type: isPreferred ? 'preferred' : 'active',
|
|
852
888
|
voteAddress: validator.voteAccountAddress,
|
|
853
889
|
stakeAddress: stakeAccountAddress,
|
|
890
|
+
lamports: availableActiveLamports,
|
|
854
891
|
});
|
|
855
892
|
}
|
|
856
|
-
|
|
857
|
-
|
|
893
|
+
const availableTransientLamports = isValidatorRemovalMode
|
|
894
|
+
? validator.transientStakeLamports
|
|
895
|
+
: validator.transientStakeLamports.sub(minBalance);
|
|
896
|
+
if (availableTransientLamports.gt(new BN(0))) {
|
|
858
897
|
const transientStakeAccountAddress = await findTransientStakeProgramAddress(stakePoolProgramId, validator.voteAccountAddress, stakePoolAddress, validator.transientSeedSuffixStart);
|
|
859
|
-
|
|
898
|
+
accounts.push({
|
|
860
899
|
type: 'transient',
|
|
861
900
|
voteAddress: validator.voteAccountAddress,
|
|
862
901
|
stakeAddress: transientStakeAccountAddress,
|
|
863
|
-
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
// Fetch all stake accounts + reserve in one batch call
|
|
867
|
-
const addressesToFetch = [
|
|
868
|
-
...accountsToFetch.map(a => a.stakeAddress),
|
|
869
|
-
stakePool.reserveStake,
|
|
870
|
-
];
|
|
871
|
-
const accountInfos = await connection.getMultipleAccountsInfo(addressesToFetch);
|
|
872
|
-
// Build accounts list using actual on-chain balances
|
|
873
|
-
let accounts = [];
|
|
874
|
-
for (let i = 0; i < accountsToFetch.length; i++) {
|
|
875
|
-
const { type, voteAddress, stakeAddress } = accountsToFetch[i];
|
|
876
|
-
const accountInfo = accountInfos[i];
|
|
877
|
-
if (!accountInfo) {
|
|
878
|
-
continue;
|
|
879
|
-
}
|
|
880
|
-
// Use actual on-chain balance instead of validator list value
|
|
881
|
-
const actualLamports = new BN(accountInfo.lamports);
|
|
882
|
-
const availableLamports = actualLamports.sub(minBalance);
|
|
883
|
-
if (availableLamports.gt(new BN(0))) {
|
|
884
|
-
accounts.push({
|
|
885
|
-
type,
|
|
886
|
-
voteAddress,
|
|
887
|
-
stakeAddress,
|
|
888
|
-
lamports: availableLamports,
|
|
902
|
+
lamports: availableTransientLamports,
|
|
889
903
|
});
|
|
890
904
|
}
|
|
891
905
|
}
|
|
892
906
|
// Sort from highest to lowest balance
|
|
893
907
|
accounts = accounts.sort(compareFn || ((a, b) => b.lamports.sub(a.lamports).toNumber()));
|
|
894
|
-
// Add reserve stake using actual balance (last item in batch fetch)
|
|
895
|
-
const reserveAccountInfo = accountInfos[accountInfos.length - 1];
|
|
896
|
-
const reserveStakeBalance = new BN(((_b = reserveAccountInfo === null || reserveAccountInfo === void 0 ? void 0 : reserveAccountInfo.lamports) !== null && _b !== void 0 ? _b : 0) - minBalanceForRentExemption);
|
|
897
|
-
if (reserveStakeBalance.gt(new BN(0))) {
|
|
898
|
-
accounts.push({
|
|
899
|
-
type: 'reserve',
|
|
900
|
-
stakeAddress: stakePool.reserveStake,
|
|
901
|
-
lamports: reserveStakeBalance,
|
|
902
|
-
});
|
|
903
|
-
}
|
|
904
908
|
// Prepare the list of accounts to withdraw from
|
|
905
909
|
const withdrawFrom = [];
|
|
906
910
|
let remainingAmount = new BN(amount);
|
|
@@ -909,23 +913,24 @@ async function prepareWithdrawAccounts(connection, stakePool, stakePoolAddress,
|
|
|
909
913
|
numerator: fee.denominator.sub(fee.numerator),
|
|
910
914
|
denominator: fee.denominator,
|
|
911
915
|
};
|
|
912
|
-
for (const type of ['preferred', 'active', 'transient'
|
|
916
|
+
for (const type of ['preferred', 'active', 'transient']) {
|
|
913
917
|
const filteredAccounts = accounts.filter(a => a.type === type);
|
|
914
918
|
for (const { stakeAddress, voteAddress, lamports } of filteredAccounts) {
|
|
915
|
-
if (lamports.lte(minBalance) && type === 'transient') {
|
|
916
|
-
continue;
|
|
917
|
-
}
|
|
918
919
|
let availableForWithdrawal = calcPoolTokensForDeposit(stakePool, lamports);
|
|
919
920
|
if (!skipFee && !inverseFee.numerator.isZero()) {
|
|
920
921
|
availableForWithdrawal = availableForWithdrawal
|
|
921
922
|
.mul(inverseFee.denominator)
|
|
922
923
|
.div(inverseFee.numerator);
|
|
923
924
|
}
|
|
925
|
+
// In ValidatorRemoval mode, must withdraw full validator balance (no partial)
|
|
926
|
+
// Skip if remaining amount is less than full validator balance
|
|
927
|
+
if (isValidatorRemovalMode && remainingAmount.lt(availableForWithdrawal)) {
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
924
930
|
const poolAmount = BN.min(availableForWithdrawal, remainingAmount);
|
|
925
931
|
if (poolAmount.lte(new BN(0))) {
|
|
926
932
|
continue;
|
|
927
933
|
}
|
|
928
|
-
// Those accounts will be withdrawn completely with `claim` instruction
|
|
929
934
|
withdrawFrom.push({ stakeAddress, voteAddress, poolAmount });
|
|
930
935
|
remainingAmount = remainingAmount.sub(poolAmount);
|
|
931
936
|
if (remainingAmount.isZero()) {
|
|
@@ -938,7 +943,23 @@ async function prepareWithdrawAccounts(connection, stakePool, stakePoolAddress,
|
|
|
938
943
|
}
|
|
939
944
|
// Not enough stake to withdraw the specified amount
|
|
940
945
|
if (remainingAmount.gt(new BN(0))) {
|
|
941
|
-
|
|
946
|
+
if (allowPartial) {
|
|
947
|
+
const delayedAmount = amount.sub(remainingAmount);
|
|
948
|
+
return {
|
|
949
|
+
withdrawAccounts: withdrawFrom,
|
|
950
|
+
delayedAmount,
|
|
951
|
+
remainingAmount,
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
const availableAmount = amount.sub(remainingAmount);
|
|
955
|
+
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.`);
|
|
956
|
+
}
|
|
957
|
+
if (allowPartial) {
|
|
958
|
+
return {
|
|
959
|
+
withdrawAccounts: withdrawFrom,
|
|
960
|
+
delayedAmount: amount,
|
|
961
|
+
remainingAmount: new BN(0),
|
|
962
|
+
};
|
|
942
963
|
}
|
|
943
964
|
return withdrawFrom;
|
|
944
965
|
}
|
|
@@ -1651,7 +1672,7 @@ class StakePoolInstruction {
|
|
|
1651
1672
|
}
|
|
1652
1673
|
/**
|
|
1653
1674
|
* Creates a transaction instruction to withdraw stake from a stake pool using a Fogo session.
|
|
1654
|
-
* The stake account is created as a PDA and rent is
|
|
1675
|
+
* The stake account is created as a PDA and rent is funded from the reserve.
|
|
1655
1676
|
*/
|
|
1656
1677
|
static withdrawStakeWithSession(params) {
|
|
1657
1678
|
const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawStakeWithSession;
|
|
@@ -1666,17 +1687,19 @@ class StakePoolInstruction {
|
|
|
1666
1687
|
{ pubkey: params.withdrawAuthority, isSigner: false, isWritable: false },
|
|
1667
1688
|
{ pubkey: params.stakeToSplit, isSigner: false, isWritable: true },
|
|
1668
1689
|
{ pubkey: params.stakeToReceive, isSigner: false, isWritable: true },
|
|
1669
|
-
{ pubkey: params.sessionSigner, isSigner: true, isWritable: false }, // user_stake_authority_info
|
|
1670
|
-
{ pubkey: params.sessionSigner, isSigner: false, isWritable: false }, // user_transfer_authority_info (
|
|
1690
|
+
{ pubkey: params.sessionSigner, isSigner: true, isWritable: false }, // user_stake_authority_info
|
|
1691
|
+
{ pubkey: params.sessionSigner, isSigner: false, isWritable: false }, // user_transfer_authority_info (unused in session path)
|
|
1671
1692
|
{ pubkey: params.burnFromPool, isSigner: false, isWritable: true },
|
|
1672
1693
|
{ pubkey: params.managerFeeAccount, isSigner: false, isWritable: true },
|
|
1673
1694
|
{ pubkey: params.poolMint, isSigner: false, isWritable: true },
|
|
1674
1695
|
{ pubkey: web3_js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
|
|
1675
1696
|
{ pubkey: params.tokenProgramId, isSigner: false, isWritable: false },
|
|
1676
1697
|
{ pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
|
|
1698
|
+
// Session-specific accounts
|
|
1677
1699
|
{ pubkey: params.programSigner, isSigner: false, isWritable: false },
|
|
1678
1700
|
{ pubkey: web3_js.SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1679
|
-
{ pubkey: params.
|
|
1701
|
+
{ pubkey: params.reserveStake, isSigner: false, isWritable: true },
|
|
1702
|
+
{ pubkey: web3_js.SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
|
|
1680
1703
|
];
|
|
1681
1704
|
return new web3_js.TransactionInstruction({
|
|
1682
1705
|
programId: params.programId,
|
|
@@ -2416,37 +2439,51 @@ async function getUserStakeAccounts(connection, programId, userPubkey, maxSeed =
|
|
|
2416
2439
|
* Withdraws stake from a stake pool using a Fogo session.
|
|
2417
2440
|
*
|
|
2418
2441
|
* The on-chain program creates stake account PDAs. The rent for these accounts
|
|
2419
|
-
* is
|
|
2442
|
+
* is funded from the reserve stake.
|
|
2420
2443
|
*
|
|
2421
2444
|
* @param connection - Solana connection
|
|
2422
2445
|
* @param stakePoolAddress - The stake pool to withdraw from
|
|
2423
2446
|
* @param signerOrSession - The session signer public key
|
|
2424
2447
|
* @param userPubkey - User's wallet (used for PDA derivation and token ownership)
|
|
2425
|
-
* @param payer - Payer for stake account rent (typically paymaster)
|
|
2426
2448
|
* @param amount - Amount of pool tokens to withdraw
|
|
2427
2449
|
* @param userStakeSeedStart - Starting seed for user stake PDA derivation (default: 0)
|
|
2428
2450
|
* @param useReserve - Whether to withdraw from reserve (default: false)
|
|
2429
2451
|
* @param voteAccountAddress - Optional specific validator to withdraw from
|
|
2430
2452
|
* @param minimumLamportsOut - Minimum lamports to receive (slippage protection)
|
|
2431
2453
|
* @param validatorComparator - Optional comparator for validator selection
|
|
2454
|
+
* @param allowPartial - If true, returns partial results instead of throwing when not enough stake available
|
|
2432
2455
|
*/
|
|
2433
|
-
async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSession, userPubkey,
|
|
2434
|
-
|
|
2456
|
+
async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, amount, userStakeSeedStart = 0, useReserve = false, voteAccountAddress, minimumLamportsOut = 0, validatorComparator, allowPartial = false) {
|
|
2457
|
+
var _c;
|
|
2435
2458
|
const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
|
|
2459
|
+
// First fetch: get stake pool to know other account addresses
|
|
2460
|
+
const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
|
|
2436
2461
|
const stakePool = stakePoolAccount.account.data;
|
|
2437
2462
|
const poolTokens = solToLamports(amount);
|
|
2438
2463
|
const poolAmount = new BN(poolTokens);
|
|
2439
2464
|
const poolTokenAccount = splToken.getAssociatedTokenAddressSync(stakePool.poolMint, userPubkey);
|
|
2440
|
-
|
|
2465
|
+
// Second fetch: get ALL remaining data in parallel
|
|
2466
|
+
const [tokenAccount, stakeAccountRentExemption, validatorListAcc, stakeMinimumDelegationResponse] = await Promise.all([
|
|
2467
|
+
splToken.getAccount(connection, poolTokenAccount),
|
|
2468
|
+
connection.getMinimumBalanceForRentExemption(web3_js.StakeProgram.space),
|
|
2469
|
+
connection.getAccountInfo(stakePool.validatorList),
|
|
2470
|
+
connection.getStakeMinimumDelegation(),
|
|
2471
|
+
]);
|
|
2472
|
+
// Pre-fetch data to avoid duplicate RPC calls in prepareWithdrawAccounts
|
|
2473
|
+
const prefetchedData = {
|
|
2474
|
+
validatorListData: (_c = validatorListAcc === null || validatorListAcc === void 0 ? void 0 : validatorListAcc.data) !== null && _c !== void 0 ? _c : null,
|
|
2475
|
+
minBalanceForRentExemption: stakeAccountRentExemption,
|
|
2476
|
+
stakeMinimumDelegation: Number(stakeMinimumDelegationResponse.value),
|
|
2477
|
+
};
|
|
2441
2478
|
if (tokenAccount.amount < poolTokens) {
|
|
2442
2479
|
throw new Error(`Not enough token balance to withdraw ${amount} pool tokens.
|
|
2443
2480
|
Maximum withdraw amount is ${lamportsToSol(tokenAccount.amount)} pool tokens.`);
|
|
2444
2481
|
}
|
|
2445
2482
|
const [programSigner] = web3_js.PublicKey.findProgramAddressSync([Buffer.from('fogo_session_program_signer')], stakePoolProgramId);
|
|
2446
2483
|
const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
|
|
2447
|
-
const stakeAccountRentExemption = await connection.getMinimumBalanceForRentExemption(web3_js.StakeProgram.space);
|
|
2448
2484
|
// Determine which stake accounts to withdraw from
|
|
2449
2485
|
const withdrawAccounts = [];
|
|
2486
|
+
let partialRemainingAmount;
|
|
2450
2487
|
if (useReserve) {
|
|
2451
2488
|
withdrawAccounts.push({
|
|
2452
2489
|
stakeAddress: stakePool.reserveStake,
|
|
@@ -2477,7 +2514,14 @@ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSe
|
|
|
2477
2514
|
}
|
|
2478
2515
|
else {
|
|
2479
2516
|
// Get the list of accounts to withdraw from automatically
|
|
2480
|
-
|
|
2517
|
+
if (allowPartial) {
|
|
2518
|
+
const result = await prepareWithdrawAccounts(connection, stakePool, stakePoolAddress, poolAmount, validatorComparator, poolTokenAccount.equals(stakePool.managerFeeAccount), true, prefetchedData);
|
|
2519
|
+
withdrawAccounts.push(...result.withdrawAccounts);
|
|
2520
|
+
partialRemainingAmount = result.remainingAmount;
|
|
2521
|
+
}
|
|
2522
|
+
else {
|
|
2523
|
+
withdrawAccounts.push(...(await prepareWithdrawAccounts(connection, stakePool, stakePoolAddress, poolAmount, validatorComparator, poolTokenAccount.equals(stakePool.managerFeeAccount), undefined, prefetchedData)));
|
|
2524
|
+
}
|
|
2481
2525
|
}
|
|
2482
2526
|
const instructions = [];
|
|
2483
2527
|
const stakeAccountPubkeys = [];
|
|
@@ -2494,7 +2538,7 @@ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSe
|
|
|
2494
2538
|
const stakeReceiverPubkey = findUserStakeProgramAddress(stakePoolProgramId, userPubkey, userStakeSeed);
|
|
2495
2539
|
stakeAccountPubkeys.push(stakeReceiverPubkey);
|
|
2496
2540
|
userStakeSeeds.push(userStakeSeed);
|
|
2497
|
-
// The on-chain program creates the stake account PDA and rent is
|
|
2541
|
+
// The on-chain program creates the stake account PDA and rent is funded from reserve.
|
|
2498
2542
|
instructions.push(StakePoolInstruction.withdrawStakeWithSession({
|
|
2499
2543
|
programId: stakePoolProgramId,
|
|
2500
2544
|
stakePool: stakePoolAddress,
|
|
@@ -2508,7 +2552,7 @@ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSe
|
|
|
2508
2552
|
poolMint: stakePool.poolMint,
|
|
2509
2553
|
tokenProgramId: stakePool.tokenProgramId,
|
|
2510
2554
|
programSigner,
|
|
2511
|
-
|
|
2555
|
+
reserveStake: stakePool.reserveStake,
|
|
2512
2556
|
poolTokensIn: withdrawAccount.poolAmount.toNumber(),
|
|
2513
2557
|
minimumLamportsOut,
|
|
2514
2558
|
userStakeSeed,
|
|
@@ -2519,6 +2563,7 @@ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSe
|
|
|
2519
2563
|
instructions,
|
|
2520
2564
|
stakeAccountPubkeys,
|
|
2521
2565
|
userStakeSeeds,
|
|
2566
|
+
remainingPoolTokens: partialRemainingAmount ? lamportsToSol(partialRemainingAmount) : 0,
|
|
2522
2567
|
};
|
|
2523
2568
|
}
|
|
2524
2569
|
async function addValidatorToPool(connection, stakePoolAddress, validatorVote, seed) {
|