@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
package/dist/index.d.ts
CHANGED
|
@@ -144,24 +144,25 @@ export declare function getUserStakeAccounts(connection: Connection, programId:
|
|
|
144
144
|
* Withdraws stake from a stake pool using a Fogo session.
|
|
145
145
|
*
|
|
146
146
|
* The on-chain program creates stake account PDAs. The rent for these accounts
|
|
147
|
-
* is
|
|
147
|
+
* is funded from the reserve stake.
|
|
148
148
|
*
|
|
149
149
|
* @param connection - Solana connection
|
|
150
150
|
* @param stakePoolAddress - The stake pool to withdraw from
|
|
151
151
|
* @param signerOrSession - The session signer public key
|
|
152
152
|
* @param userPubkey - User's wallet (used for PDA derivation and token ownership)
|
|
153
|
-
* @param payer - Payer for stake account rent (typically paymaster)
|
|
154
153
|
* @param amount - Amount of pool tokens to withdraw
|
|
155
154
|
* @param userStakeSeedStart - Starting seed for user stake PDA derivation (default: 0)
|
|
156
155
|
* @param useReserve - Whether to withdraw from reserve (default: false)
|
|
157
156
|
* @param voteAccountAddress - Optional specific validator to withdraw from
|
|
158
157
|
* @param minimumLamportsOut - Minimum lamports to receive (slippage protection)
|
|
159
158
|
* @param validatorComparator - Optional comparator for validator selection
|
|
159
|
+
* @param allowPartial - If true, returns partial results instead of throwing when not enough stake available
|
|
160
160
|
*/
|
|
161
|
-
export declare function withdrawStakeWithSession(connection: Connection, stakePoolAddress: PublicKey, signerOrSession: PublicKey, userPubkey: PublicKey,
|
|
161
|
+
export declare function withdrawStakeWithSession(connection: Connection, stakePoolAddress: PublicKey, signerOrSession: PublicKey, userPubkey: PublicKey, amount: number, userStakeSeedStart?: number, useReserve?: boolean, voteAccountAddress?: PublicKey, minimumLamportsOut?: number, validatorComparator?: (_a: ValidatorAccount, _b: ValidatorAccount) => number, allowPartial?: boolean): Promise<{
|
|
162
162
|
instructions: TransactionInstruction[];
|
|
163
163
|
stakeAccountPubkeys: PublicKey[];
|
|
164
164
|
userStakeSeeds: number[];
|
|
165
|
+
remainingPoolTokens: number;
|
|
165
166
|
}>;
|
|
166
167
|
export declare function addValidatorToPool(connection: Connection, stakePoolAddress: PublicKey, validatorVote: PublicKey, seed?: number): Promise<{
|
|
167
168
|
instructions: TransactionInstruction[];
|
package/dist/index.esm.js
CHANGED
|
@@ -806,80 +806,84 @@ async function getValidatorListAccount(connection, pubkey) {
|
|
|
806
806
|
},
|
|
807
807
|
};
|
|
808
808
|
}
|
|
809
|
-
async function prepareWithdrawAccounts(connection, stakePool, stakePoolAddress, amount, compareFn, skipFee) {
|
|
809
|
+
async function prepareWithdrawAccounts(connection, stakePool, stakePoolAddress, amount, compareFn, skipFee, allowPartial, prefetchedData) {
|
|
810
810
|
var _a, _b;
|
|
811
811
|
const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
812
|
+
// Use prefetched data if available, otherwise fetch from RPC
|
|
813
|
+
let validatorListData;
|
|
814
|
+
let minBalanceForRentExemption;
|
|
815
|
+
let stakeMinimumDelegation;
|
|
816
|
+
if (prefetchedData) {
|
|
817
|
+
validatorListData = prefetchedData.validatorListData;
|
|
818
|
+
minBalanceForRentExemption = prefetchedData.minBalanceForRentExemption;
|
|
819
|
+
stakeMinimumDelegation = prefetchedData.stakeMinimumDelegation;
|
|
816
820
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
+
else {
|
|
822
|
+
const [validatorListAcc, rentExemption, stakeMinimumDelegationResponse] = await Promise.all([
|
|
823
|
+
connection.getAccountInfo(stakePool.validatorList),
|
|
824
|
+
connection.getMinimumBalanceForRentExemption(StakeProgram.space),
|
|
825
|
+
connection.getStakeMinimumDelegation(),
|
|
826
|
+
]);
|
|
827
|
+
validatorListData = (_a = validatorListAcc === null || validatorListAcc === void 0 ? void 0 : validatorListAcc.data) !== null && _a !== void 0 ? _a : null;
|
|
828
|
+
minBalanceForRentExemption = rentExemption;
|
|
829
|
+
stakeMinimumDelegation = Number(stakeMinimumDelegationResponse.value);
|
|
830
|
+
}
|
|
831
|
+
if (!validatorListData) {
|
|
832
|
+
throw new Error('No staked funds available for delayed unstake. Use instant unstake instead.');
|
|
833
|
+
}
|
|
834
|
+
const validatorList = ValidatorListLayout.decode(validatorListData);
|
|
835
|
+
if (!(validatorList === null || validatorList === void 0 ? void 0 : validatorList.validators) || (validatorList === null || validatorList === void 0 ? void 0 : validatorList.validators.length) === 0) {
|
|
836
|
+
throw new Error('No staked funds available for delayed unstake. Use instant unstake instead.');
|
|
837
|
+
}
|
|
838
|
+
// minBalance = rent + max(stake_minimum_delegation, MINIMUM_ACTIVE_STAKE)
|
|
839
|
+
const minimumDelegation = Math.max(stakeMinimumDelegation, MINIMUM_ACTIVE_STAKE);
|
|
840
|
+
const minBalance = new BN(minBalanceForRentExemption + minimumDelegation);
|
|
841
|
+
// Threshold for has_active_stake check (ceiling division for lamports_per_pool_token)
|
|
842
|
+
const lamportsPerPoolToken = stakePool.totalLamports
|
|
843
|
+
.add(stakePool.poolTokenSupply)
|
|
844
|
+
.sub(new BN(1))
|
|
845
|
+
.div(stakePool.poolTokenSupply);
|
|
846
|
+
const minimumLamportsWithTolerance = minBalance.add(lamportsPerPoolToken);
|
|
847
|
+
const hasActiveStake = validatorList.validators.some(v => v.status === ValidatorStakeInfoStatus.Active
|
|
848
|
+
&& v.activeStakeLamports.gt(minimumLamportsWithTolerance));
|
|
849
|
+
const hasTransientStake = validatorList.validators.some(v => v.status === ValidatorStakeInfoStatus.Active
|
|
850
|
+
&& v.transientStakeLamports.gt(minimumLamportsWithTolerance));
|
|
851
|
+
// ValidatorRemoval mode: no validator above threshold
|
|
852
|
+
const isValidatorRemovalMode = !hasActiveStake && !hasTransientStake;
|
|
853
|
+
let accounts = [];
|
|
821
854
|
for (const validator of validatorList.validators) {
|
|
822
855
|
if (validator.status !== ValidatorStakeInfoStatus.Active) {
|
|
823
856
|
continue;
|
|
824
857
|
}
|
|
825
858
|
const stakeAccountAddress = await findStakeProgramAddress(stakePoolProgramId, validator.voteAccountAddress, stakePoolAddress);
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
859
|
+
// ValidatorRemoval: full balance available; Normal: leave minBalance
|
|
860
|
+
const availableActiveLamports = isValidatorRemovalMode
|
|
861
|
+
? validator.activeStakeLamports
|
|
862
|
+
: validator.activeStakeLamports.sub(minBalance);
|
|
863
|
+
if (availableActiveLamports.gt(new BN(0))) {
|
|
864
|
+
const isPreferred = (_b = stakePool === null || stakePool === void 0 ? void 0 : stakePool.preferredWithdrawValidatorVoteAddress) === null || _b === void 0 ? void 0 : _b.equals(validator.voteAccountAddress);
|
|
865
|
+
accounts.push({
|
|
830
866
|
type: isPreferred ? 'preferred' : 'active',
|
|
831
867
|
voteAddress: validator.voteAccountAddress,
|
|
832
868
|
stakeAddress: stakeAccountAddress,
|
|
869
|
+
lamports: availableActiveLamports,
|
|
833
870
|
});
|
|
834
871
|
}
|
|
835
|
-
|
|
836
|
-
|
|
872
|
+
const availableTransientLamports = isValidatorRemovalMode
|
|
873
|
+
? validator.transientStakeLamports
|
|
874
|
+
: validator.transientStakeLamports.sub(minBalance);
|
|
875
|
+
if (availableTransientLamports.gt(new BN(0))) {
|
|
837
876
|
const transientStakeAccountAddress = await findTransientStakeProgramAddress(stakePoolProgramId, validator.voteAccountAddress, stakePoolAddress, validator.transientSeedSuffixStart);
|
|
838
|
-
|
|
877
|
+
accounts.push({
|
|
839
878
|
type: 'transient',
|
|
840
879
|
voteAddress: validator.voteAccountAddress,
|
|
841
880
|
stakeAddress: transientStakeAccountAddress,
|
|
842
|
-
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
// Fetch all stake accounts + reserve in one batch call
|
|
846
|
-
const addressesToFetch = [
|
|
847
|
-
...accountsToFetch.map(a => a.stakeAddress),
|
|
848
|
-
stakePool.reserveStake,
|
|
849
|
-
];
|
|
850
|
-
const accountInfos = await connection.getMultipleAccountsInfo(addressesToFetch);
|
|
851
|
-
// Build accounts list using actual on-chain balances
|
|
852
|
-
let accounts = [];
|
|
853
|
-
for (let i = 0; i < accountsToFetch.length; i++) {
|
|
854
|
-
const { type, voteAddress, stakeAddress } = accountsToFetch[i];
|
|
855
|
-
const accountInfo = accountInfos[i];
|
|
856
|
-
if (!accountInfo) {
|
|
857
|
-
continue;
|
|
858
|
-
}
|
|
859
|
-
// Use actual on-chain balance instead of validator list value
|
|
860
|
-
const actualLamports = new BN(accountInfo.lamports);
|
|
861
|
-
const availableLamports = actualLamports.sub(minBalance);
|
|
862
|
-
if (availableLamports.gt(new BN(0))) {
|
|
863
|
-
accounts.push({
|
|
864
|
-
type,
|
|
865
|
-
voteAddress,
|
|
866
|
-
stakeAddress,
|
|
867
|
-
lamports: availableLamports,
|
|
881
|
+
lamports: availableTransientLamports,
|
|
868
882
|
});
|
|
869
883
|
}
|
|
870
884
|
}
|
|
871
885
|
// Sort from highest to lowest balance
|
|
872
886
|
accounts = accounts.sort(compareFn || ((a, b) => b.lamports.sub(a.lamports).toNumber()));
|
|
873
|
-
// Add reserve stake using actual balance (last item in batch fetch)
|
|
874
|
-
const reserveAccountInfo = accountInfos[accountInfos.length - 1];
|
|
875
|
-
const reserveStakeBalance = new BN(((_b = reserveAccountInfo === null || reserveAccountInfo === void 0 ? void 0 : reserveAccountInfo.lamports) !== null && _b !== void 0 ? _b : 0) - minBalanceForRentExemption);
|
|
876
|
-
if (reserveStakeBalance.gt(new BN(0))) {
|
|
877
|
-
accounts.push({
|
|
878
|
-
type: 'reserve',
|
|
879
|
-
stakeAddress: stakePool.reserveStake,
|
|
880
|
-
lamports: reserveStakeBalance,
|
|
881
|
-
});
|
|
882
|
-
}
|
|
883
887
|
// Prepare the list of accounts to withdraw from
|
|
884
888
|
const withdrawFrom = [];
|
|
885
889
|
let remainingAmount = new BN(amount);
|
|
@@ -888,23 +892,24 @@ async function prepareWithdrawAccounts(connection, stakePool, stakePoolAddress,
|
|
|
888
892
|
numerator: fee.denominator.sub(fee.numerator),
|
|
889
893
|
denominator: fee.denominator,
|
|
890
894
|
};
|
|
891
|
-
for (const type of ['preferred', 'active', 'transient'
|
|
895
|
+
for (const type of ['preferred', 'active', 'transient']) {
|
|
892
896
|
const filteredAccounts = accounts.filter(a => a.type === type);
|
|
893
897
|
for (const { stakeAddress, voteAddress, lamports } of filteredAccounts) {
|
|
894
|
-
if (lamports.lte(minBalance) && type === 'transient') {
|
|
895
|
-
continue;
|
|
896
|
-
}
|
|
897
898
|
let availableForWithdrawal = calcPoolTokensForDeposit(stakePool, lamports);
|
|
898
899
|
if (!skipFee && !inverseFee.numerator.isZero()) {
|
|
899
900
|
availableForWithdrawal = availableForWithdrawal
|
|
900
901
|
.mul(inverseFee.denominator)
|
|
901
902
|
.div(inverseFee.numerator);
|
|
902
903
|
}
|
|
904
|
+
// In ValidatorRemoval mode, must withdraw full validator balance (no partial)
|
|
905
|
+
// Skip if remaining amount is less than full validator balance
|
|
906
|
+
if (isValidatorRemovalMode && remainingAmount.lt(availableForWithdrawal)) {
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
903
909
|
const poolAmount = BN.min(availableForWithdrawal, remainingAmount);
|
|
904
910
|
if (poolAmount.lte(new BN(0))) {
|
|
905
911
|
continue;
|
|
906
912
|
}
|
|
907
|
-
// Those accounts will be withdrawn completely with `claim` instruction
|
|
908
913
|
withdrawFrom.push({ stakeAddress, voteAddress, poolAmount });
|
|
909
914
|
remainingAmount = remainingAmount.sub(poolAmount);
|
|
910
915
|
if (remainingAmount.isZero()) {
|
|
@@ -917,7 +922,23 @@ async function prepareWithdrawAccounts(connection, stakePool, stakePoolAddress,
|
|
|
917
922
|
}
|
|
918
923
|
// Not enough stake to withdraw the specified amount
|
|
919
924
|
if (remainingAmount.gt(new BN(0))) {
|
|
920
|
-
|
|
925
|
+
if (allowPartial) {
|
|
926
|
+
const delayedAmount = amount.sub(remainingAmount);
|
|
927
|
+
return {
|
|
928
|
+
withdrawAccounts: withdrawFrom,
|
|
929
|
+
delayedAmount,
|
|
930
|
+
remainingAmount,
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
const availableAmount = amount.sub(remainingAmount);
|
|
934
|
+
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.`);
|
|
935
|
+
}
|
|
936
|
+
if (allowPartial) {
|
|
937
|
+
return {
|
|
938
|
+
withdrawAccounts: withdrawFrom,
|
|
939
|
+
delayedAmount: amount,
|
|
940
|
+
remainingAmount: new BN(0),
|
|
941
|
+
};
|
|
921
942
|
}
|
|
922
943
|
return withdrawFrom;
|
|
923
944
|
}
|
|
@@ -1630,7 +1651,7 @@ class StakePoolInstruction {
|
|
|
1630
1651
|
}
|
|
1631
1652
|
/**
|
|
1632
1653
|
* Creates a transaction instruction to withdraw stake from a stake pool using a Fogo session.
|
|
1633
|
-
* The stake account is created as a PDA and rent is
|
|
1654
|
+
* The stake account is created as a PDA and rent is funded from the reserve.
|
|
1634
1655
|
*/
|
|
1635
1656
|
static withdrawStakeWithSession(params) {
|
|
1636
1657
|
const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawStakeWithSession;
|
|
@@ -1645,17 +1666,19 @@ class StakePoolInstruction {
|
|
|
1645
1666
|
{ pubkey: params.withdrawAuthority, isSigner: false, isWritable: false },
|
|
1646
1667
|
{ pubkey: params.stakeToSplit, isSigner: false, isWritable: true },
|
|
1647
1668
|
{ pubkey: params.stakeToReceive, isSigner: false, isWritable: true },
|
|
1648
|
-
{ pubkey: params.sessionSigner, isSigner: true, isWritable: false }, // user_stake_authority_info
|
|
1649
|
-
{ pubkey: params.sessionSigner, isSigner: false, isWritable: false }, // user_transfer_authority_info (
|
|
1669
|
+
{ pubkey: params.sessionSigner, isSigner: true, isWritable: false }, // user_stake_authority_info
|
|
1670
|
+
{ pubkey: params.sessionSigner, isSigner: false, isWritable: false }, // user_transfer_authority_info (unused in session path)
|
|
1650
1671
|
{ pubkey: params.burnFromPool, isSigner: false, isWritable: true },
|
|
1651
1672
|
{ pubkey: params.managerFeeAccount, isSigner: false, isWritable: true },
|
|
1652
1673
|
{ pubkey: params.poolMint, isSigner: false, isWritable: true },
|
|
1653
1674
|
{ pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
|
|
1654
1675
|
{ pubkey: params.tokenProgramId, isSigner: false, isWritable: false },
|
|
1655
1676
|
{ pubkey: StakeProgram.programId, isSigner: false, isWritable: false },
|
|
1677
|
+
// Session-specific accounts
|
|
1656
1678
|
{ pubkey: params.programSigner, isSigner: false, isWritable: false },
|
|
1657
1679
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1658
|
-
{ pubkey: params.
|
|
1680
|
+
{ pubkey: params.reserveStake, isSigner: false, isWritable: true },
|
|
1681
|
+
{ pubkey: SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
|
|
1659
1682
|
];
|
|
1660
1683
|
return new TransactionInstruction({
|
|
1661
1684
|
programId: params.programId,
|
|
@@ -2395,37 +2418,51 @@ async function getUserStakeAccounts(connection, programId, userPubkey, maxSeed =
|
|
|
2395
2418
|
* Withdraws stake from a stake pool using a Fogo session.
|
|
2396
2419
|
*
|
|
2397
2420
|
* The on-chain program creates stake account PDAs. The rent for these accounts
|
|
2398
|
-
* is
|
|
2421
|
+
* is funded from the reserve stake.
|
|
2399
2422
|
*
|
|
2400
2423
|
* @param connection - Solana connection
|
|
2401
2424
|
* @param stakePoolAddress - The stake pool to withdraw from
|
|
2402
2425
|
* @param signerOrSession - The session signer public key
|
|
2403
2426
|
* @param userPubkey - User's wallet (used for PDA derivation and token ownership)
|
|
2404
|
-
* @param payer - Payer for stake account rent (typically paymaster)
|
|
2405
2427
|
* @param amount - Amount of pool tokens to withdraw
|
|
2406
2428
|
* @param userStakeSeedStart - Starting seed for user stake PDA derivation (default: 0)
|
|
2407
2429
|
* @param useReserve - Whether to withdraw from reserve (default: false)
|
|
2408
2430
|
* @param voteAccountAddress - Optional specific validator to withdraw from
|
|
2409
2431
|
* @param minimumLamportsOut - Minimum lamports to receive (slippage protection)
|
|
2410
2432
|
* @param validatorComparator - Optional comparator for validator selection
|
|
2433
|
+
* @param allowPartial - If true, returns partial results instead of throwing when not enough stake available
|
|
2411
2434
|
*/
|
|
2412
|
-
async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSession, userPubkey,
|
|
2413
|
-
|
|
2435
|
+
async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, amount, userStakeSeedStart = 0, useReserve = false, voteAccountAddress, minimumLamportsOut = 0, validatorComparator, allowPartial = false) {
|
|
2436
|
+
var _c;
|
|
2414
2437
|
const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
|
|
2438
|
+
// First fetch: get stake pool to know other account addresses
|
|
2439
|
+
const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
|
|
2415
2440
|
const stakePool = stakePoolAccount.account.data;
|
|
2416
2441
|
const poolTokens = solToLamports(amount);
|
|
2417
2442
|
const poolAmount = new BN(poolTokens);
|
|
2418
2443
|
const poolTokenAccount = getAssociatedTokenAddressSync(stakePool.poolMint, userPubkey);
|
|
2419
|
-
|
|
2444
|
+
// Second fetch: get ALL remaining data in parallel
|
|
2445
|
+
const [tokenAccount, stakeAccountRentExemption, validatorListAcc, stakeMinimumDelegationResponse] = await Promise.all([
|
|
2446
|
+
getAccount(connection, poolTokenAccount),
|
|
2447
|
+
connection.getMinimumBalanceForRentExemption(StakeProgram.space),
|
|
2448
|
+
connection.getAccountInfo(stakePool.validatorList),
|
|
2449
|
+
connection.getStakeMinimumDelegation(),
|
|
2450
|
+
]);
|
|
2451
|
+
// Pre-fetch data to avoid duplicate RPC calls in prepareWithdrawAccounts
|
|
2452
|
+
const prefetchedData = {
|
|
2453
|
+
validatorListData: (_c = validatorListAcc === null || validatorListAcc === void 0 ? void 0 : validatorListAcc.data) !== null && _c !== void 0 ? _c : null,
|
|
2454
|
+
minBalanceForRentExemption: stakeAccountRentExemption,
|
|
2455
|
+
stakeMinimumDelegation: Number(stakeMinimumDelegationResponse.value),
|
|
2456
|
+
};
|
|
2420
2457
|
if (tokenAccount.amount < poolTokens) {
|
|
2421
2458
|
throw new Error(`Not enough token balance to withdraw ${amount} pool tokens.
|
|
2422
2459
|
Maximum withdraw amount is ${lamportsToSol(tokenAccount.amount)} pool tokens.`);
|
|
2423
2460
|
}
|
|
2424
2461
|
const [programSigner] = PublicKey.findProgramAddressSync([Buffer.from('fogo_session_program_signer')], stakePoolProgramId);
|
|
2425
2462
|
const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
|
|
2426
|
-
const stakeAccountRentExemption = await connection.getMinimumBalanceForRentExemption(StakeProgram.space);
|
|
2427
2463
|
// Determine which stake accounts to withdraw from
|
|
2428
2464
|
const withdrawAccounts = [];
|
|
2465
|
+
let partialRemainingAmount;
|
|
2429
2466
|
if (useReserve) {
|
|
2430
2467
|
withdrawAccounts.push({
|
|
2431
2468
|
stakeAddress: stakePool.reserveStake,
|
|
@@ -2456,7 +2493,14 @@ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSe
|
|
|
2456
2493
|
}
|
|
2457
2494
|
else {
|
|
2458
2495
|
// Get the list of accounts to withdraw from automatically
|
|
2459
|
-
|
|
2496
|
+
if (allowPartial) {
|
|
2497
|
+
const result = await prepareWithdrawAccounts(connection, stakePool, stakePoolAddress, poolAmount, validatorComparator, poolTokenAccount.equals(stakePool.managerFeeAccount), true, prefetchedData);
|
|
2498
|
+
withdrawAccounts.push(...result.withdrawAccounts);
|
|
2499
|
+
partialRemainingAmount = result.remainingAmount;
|
|
2500
|
+
}
|
|
2501
|
+
else {
|
|
2502
|
+
withdrawAccounts.push(...(await prepareWithdrawAccounts(connection, stakePool, stakePoolAddress, poolAmount, validatorComparator, poolTokenAccount.equals(stakePool.managerFeeAccount), undefined, prefetchedData)));
|
|
2503
|
+
}
|
|
2460
2504
|
}
|
|
2461
2505
|
const instructions = [];
|
|
2462
2506
|
const stakeAccountPubkeys = [];
|
|
@@ -2473,7 +2517,7 @@ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSe
|
|
|
2473
2517
|
const stakeReceiverPubkey = findUserStakeProgramAddress(stakePoolProgramId, userPubkey, userStakeSeed);
|
|
2474
2518
|
stakeAccountPubkeys.push(stakeReceiverPubkey);
|
|
2475
2519
|
userStakeSeeds.push(userStakeSeed);
|
|
2476
|
-
// The on-chain program creates the stake account PDA and rent is
|
|
2520
|
+
// The on-chain program creates the stake account PDA and rent is funded from reserve.
|
|
2477
2521
|
instructions.push(StakePoolInstruction.withdrawStakeWithSession({
|
|
2478
2522
|
programId: stakePoolProgramId,
|
|
2479
2523
|
stakePool: stakePoolAddress,
|
|
@@ -2487,7 +2531,7 @@ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSe
|
|
|
2487
2531
|
poolMint: stakePool.poolMint,
|
|
2488
2532
|
tokenProgramId: stakePool.tokenProgramId,
|
|
2489
2533
|
programSigner,
|
|
2490
|
-
|
|
2534
|
+
reserveStake: stakePool.reserveStake,
|
|
2491
2535
|
poolTokensIn: withdrawAccount.poolAmount.toNumber(),
|
|
2492
2536
|
minimumLamportsOut,
|
|
2493
2537
|
userStakeSeed,
|
|
@@ -2498,6 +2542,7 @@ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSe
|
|
|
2498
2542
|
instructions,
|
|
2499
2543
|
stakeAccountPubkeys,
|
|
2500
2544
|
userStakeSeeds,
|
|
2545
|
+
remainingPoolTokens: partialRemainingAmount ? lamportsToSol(partialRemainingAmount) : 0,
|
|
2501
2546
|
};
|
|
2502
2547
|
}
|
|
2503
2548
|
async function addValidatorToPool(connection, stakePoolAddress, validatorVote, seed) {
|