@ignitionfi/spl-stake-pool 1.1.24 → 1.1.26
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 +129 -17
- package/dist/index.browser.cjs.js.map +1 -1
- package/dist/index.browser.esm.js +129 -18
- package/dist/index.browser.esm.js.map +1 -1
- package/dist/index.cjs.js +129 -17
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +40 -4
- package/dist/index.esm.js +129 -18
- package/dist/index.esm.js.map +1 -1
- package/dist/index.iife.js +129 -17
- 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 +22 -2
- package/package.json +1 -1
- package/src/constants.ts +1 -3
- package/src/index.ts +122 -16
- package/src/instructions.ts +54 -1
- package/src/utils/stake.ts +8 -5
|
@@ -832,7 +832,7 @@ async function prepareWithdrawAccounts(connection, stakePool, stakePoolAddress,
|
|
|
832
832
|
const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
|
|
833
833
|
const validatorListAcc = await connection.getAccountInfo(stakePool.validatorList);
|
|
834
834
|
const validatorList = ValidatorListLayout.decode(validatorListAcc === null || validatorListAcc === void 0 ? void 0 : validatorListAcc.data);
|
|
835
|
-
if (!(validatorList === null || validatorList === void 0 ? void 0 : validatorList.validators) || (validatorList === null || validatorList === void 0 ? void 0 : validatorList.validators.length)
|
|
835
|
+
if (!(validatorList === null || validatorList === void 0 ? void 0 : validatorList.validators) || (validatorList === null || validatorList === void 0 ? void 0 : validatorList.validators.length) === 0) {
|
|
836
836
|
throw new Error('No accounts found');
|
|
837
837
|
}
|
|
838
838
|
const minBalanceForRentExemption = await connection.getMinimumBalanceForRentExemption(web3_js.StakeProgram.space);
|
|
@@ -844,13 +844,16 @@ async function prepareWithdrawAccounts(connection, stakePool, stakePoolAddress,
|
|
|
844
844
|
continue;
|
|
845
845
|
}
|
|
846
846
|
const stakeAccountAddress = await findStakeProgramAddress(stakePoolProgramId, validator.voteAccountAddress, stakePoolAddress);
|
|
847
|
-
|
|
847
|
+
// For active stake accounts, subtract the minimum balance that must remain
|
|
848
|
+
// to allow for merges and maintain rent exemption
|
|
849
|
+
const availableActiveLamports = validator.activeStakeLamports.sub(minBalance);
|
|
850
|
+
if (availableActiveLamports.gt(new BN(0))) {
|
|
848
851
|
const isPreferred = (_a = stakePool === null || stakePool === void 0 ? void 0 : stakePool.preferredWithdrawValidatorVoteAddress) === null || _a === void 0 ? void 0 : _a.equals(validator.voteAccountAddress);
|
|
849
852
|
accounts.push({
|
|
850
853
|
type: isPreferred ? 'preferred' : 'active',
|
|
851
854
|
voteAddress: validator.voteAccountAddress,
|
|
852
855
|
stakeAddress: stakeAccountAddress,
|
|
853
|
-
lamports:
|
|
856
|
+
lamports: availableActiveLamports,
|
|
854
857
|
});
|
|
855
858
|
}
|
|
856
859
|
const transientStakeLamports = validator.transientStakeLamports.sub(minBalance);
|
|
@@ -884,9 +887,9 @@ async function prepareWithdrawAccounts(connection, stakePool, stakePoolAddress,
|
|
|
884
887
|
denominator: fee.denominator,
|
|
885
888
|
};
|
|
886
889
|
for (const type of ['preferred', 'active', 'transient', 'reserve']) {
|
|
887
|
-
const filteredAccounts = accounts.filter(a => a.type
|
|
890
|
+
const filteredAccounts = accounts.filter(a => a.type === type);
|
|
888
891
|
for (const { stakeAddress, voteAddress, lamports } of filteredAccounts) {
|
|
889
|
-
if (lamports.lte(minBalance) && type
|
|
892
|
+
if (lamports.lte(minBalance) && type === 'transient') {
|
|
890
893
|
continue;
|
|
891
894
|
}
|
|
892
895
|
let availableForWithdrawal = calcPoolTokensForDeposit(stakePool, lamports);
|
|
@@ -1141,6 +1144,14 @@ const STAKE_POOL_INSTRUCTION_LAYOUTS = Object.freeze({
|
|
|
1141
1144
|
BufferLayout__namespace.ns64('userStakeSeed'),
|
|
1142
1145
|
]),
|
|
1143
1146
|
},
|
|
1147
|
+
WithdrawFromStakeAccountWithSession: {
|
|
1148
|
+
index: 30,
|
|
1149
|
+
layout: BufferLayout__namespace.struct([
|
|
1150
|
+
BufferLayout__namespace.u8('instruction'),
|
|
1151
|
+
BufferLayout__namespace.ns64('lamports'),
|
|
1152
|
+
BufferLayout__namespace.ns64('userStakeSeed'),
|
|
1153
|
+
]),
|
|
1154
|
+
},
|
|
1144
1155
|
});
|
|
1145
1156
|
/**
|
|
1146
1157
|
* Stake Pool Instruction class
|
|
@@ -1617,7 +1628,7 @@ class StakePoolInstruction {
|
|
|
1617
1628
|
}
|
|
1618
1629
|
/**
|
|
1619
1630
|
* Creates a transaction instruction to withdraw stake from a stake pool using a Fogo session.
|
|
1620
|
-
* The stake account is created as a PDA and rent is paid
|
|
1631
|
+
* The stake account is created as a PDA and rent is paid by the payer (typically paymaster).
|
|
1621
1632
|
*/
|
|
1622
1633
|
static withdrawStakeWithSession(params) {
|
|
1623
1634
|
const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawStakeWithSession;
|
|
@@ -1642,6 +1653,31 @@ class StakePoolInstruction {
|
|
|
1642
1653
|
{ pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
|
|
1643
1654
|
{ pubkey: params.programSigner, isSigner: false, isWritable: false },
|
|
1644
1655
|
{ pubkey: web3_js.SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1656
|
+
{ pubkey: params.payer, isSigner: true, isWritable: true },
|
|
1657
|
+
];
|
|
1658
|
+
return new web3_js.TransactionInstruction({
|
|
1659
|
+
programId: params.programId,
|
|
1660
|
+
keys,
|
|
1661
|
+
data,
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
/**
|
|
1665
|
+
* Creates a transaction instruction to withdraw SOL from a deactivated user stake account using a Fogo session.
|
|
1666
|
+
* The stake account must be fully deactivated (inactive).
|
|
1667
|
+
*/
|
|
1668
|
+
static withdrawFromStakeAccountWithSession(params) {
|
|
1669
|
+
const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawFromStakeAccountWithSession;
|
|
1670
|
+
const data = encodeData(type, {
|
|
1671
|
+
lamports: params.lamports,
|
|
1672
|
+
userStakeSeed: params.userStakeSeed,
|
|
1673
|
+
});
|
|
1674
|
+
const keys = [
|
|
1675
|
+
{ pubkey: params.userStakeAccount, isSigner: false, isWritable: true },
|
|
1676
|
+
{ pubkey: params.userWallet, isSigner: false, isWritable: true },
|
|
1677
|
+
{ pubkey: web3_js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
|
|
1678
|
+
{ pubkey: web3_js.SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
|
|
1679
|
+
{ pubkey: params.sessionSigner, isSigner: true, isWritable: false },
|
|
1680
|
+
{ pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
|
|
1645
1681
|
];
|
|
1646
1682
|
return new web3_js.TransactionInstruction({
|
|
1647
1683
|
programId: params.programId,
|
|
@@ -1912,14 +1948,21 @@ async function depositStake(connection, stakePoolAddress, authorizedPubkey, vali
|
|
|
1912
1948
|
/**
|
|
1913
1949
|
* Creates instructions required to deposit sol to stake pool.
|
|
1914
1950
|
*/
|
|
1915
|
-
async function depositWsolWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, lamports, minimumPoolTokensOut = 0, destinationTokenAccount, referrerTokenAccount, depositAuthority, payer
|
|
1951
|
+
async function depositWsolWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, lamports, minimumPoolTokensOut = 0, destinationTokenAccount, referrerTokenAccount, depositAuthority, payer,
|
|
1952
|
+
/**
|
|
1953
|
+
* Skip WSOL balance validation. Set to true when adding wrap instructions
|
|
1954
|
+
* in the same transaction that will fund the WSOL account before deposit.
|
|
1955
|
+
*/
|
|
1956
|
+
skipBalanceCheck = false) {
|
|
1916
1957
|
const wsolTokenAccount = splToken.getAssociatedTokenAddressSync(splToken.NATIVE_MINT, userPubkey);
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1958
|
+
if (!skipBalanceCheck) {
|
|
1959
|
+
const tokenAccountInfo = await connection.getTokenAccountBalance(wsolTokenAccount, 'confirmed');
|
|
1960
|
+
const wsolBalance = tokenAccountInfo
|
|
1961
|
+
? parseInt(tokenAccountInfo.value.amount)
|
|
1962
|
+
: 0;
|
|
1963
|
+
if (wsolBalance < lamports) {
|
|
1964
|
+
throw new Error(`Not enough WSOL to deposit into pool. Maximum deposit amount is ${lamportsToSol(wsolBalance)} WSOL.`);
|
|
1965
|
+
}
|
|
1923
1966
|
}
|
|
1924
1967
|
const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
|
|
1925
1968
|
const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
|
|
@@ -2280,16 +2323,83 @@ async function findNextUserStakeSeed(connection, programId, userPubkey, startSee
|
|
|
2280
2323
|
}
|
|
2281
2324
|
throw new Error(`No available user stake seed found between ${startSeed} and ${startSeed + maxSeed - 1}`);
|
|
2282
2325
|
}
|
|
2326
|
+
/**
|
|
2327
|
+
* Fetches all user stake accounts created via WithdrawStakeWithSession.
|
|
2328
|
+
* These are PDAs derived from [b"user_stake", user_wallet, seed].
|
|
2329
|
+
*
|
|
2330
|
+
* @param connection - Solana connection
|
|
2331
|
+
* @param programId - The stake pool program ID
|
|
2332
|
+
* @param userPubkey - User's wallet address
|
|
2333
|
+
* @param maxSeed - Maximum seed to check (default: 100)
|
|
2334
|
+
* @returns Array of user stake accounts with their details
|
|
2335
|
+
*/
|
|
2336
|
+
async function getUserStakeAccounts(connection, programId, userPubkey, maxSeed = 100) {
|
|
2337
|
+
var _c, _d;
|
|
2338
|
+
const stakeAccounts = [];
|
|
2339
|
+
const currentEpoch = (await connection.getEpochInfo()).epoch;
|
|
2340
|
+
for (let seed = 0; seed < maxSeed; seed++) {
|
|
2341
|
+
const pda = findUserStakeProgramAddress(programId, userPubkey, seed);
|
|
2342
|
+
const accountInfo = await connection.getAccountInfo(pda);
|
|
2343
|
+
if (!accountInfo) {
|
|
2344
|
+
continue; // Skip empty slots, there might be gaps
|
|
2345
|
+
}
|
|
2346
|
+
// Check if owned by stake program
|
|
2347
|
+
if (!accountInfo.owner.equals(web3_js.StakeProgram.programId)) {
|
|
2348
|
+
continue;
|
|
2349
|
+
}
|
|
2350
|
+
// Parse stake account data
|
|
2351
|
+
const stakeAccount = {
|
|
2352
|
+
pubkey: pda,
|
|
2353
|
+
seed,
|
|
2354
|
+
lamports: accountInfo.lamports,
|
|
2355
|
+
state: 'inactive',
|
|
2356
|
+
};
|
|
2357
|
+
try {
|
|
2358
|
+
// Parse the stake account to get delegation info
|
|
2359
|
+
const parsedAccount = await connection.getParsedAccountInfo(pda);
|
|
2360
|
+
if (parsedAccount.value && 'parsed' in parsedAccount.value.data) {
|
|
2361
|
+
const parsed = parsedAccount.value.data.parsed;
|
|
2362
|
+
if (parsed.type === 'delegated' && ((_d = (_c = parsed.info) === null || _c === void 0 ? void 0 : _c.stake) === null || _d === void 0 ? void 0 : _d.delegation)) {
|
|
2363
|
+
const delegation = parsed.info.stake.delegation;
|
|
2364
|
+
stakeAccount.voter = new web3_js.PublicKey(delegation.voter);
|
|
2365
|
+
stakeAccount.activationEpoch = Number(delegation.activationEpoch);
|
|
2366
|
+
stakeAccount.deactivationEpoch = Number(delegation.deactivationEpoch);
|
|
2367
|
+
// Determine state based on epochs
|
|
2368
|
+
const activationEpoch = stakeAccount.activationEpoch;
|
|
2369
|
+
const deactivationEpoch = stakeAccount.deactivationEpoch;
|
|
2370
|
+
if (deactivationEpoch !== undefined && deactivationEpoch < Number.MAX_SAFE_INTEGER && deactivationEpoch <= currentEpoch) {
|
|
2371
|
+
stakeAccount.state = 'inactive';
|
|
2372
|
+
}
|
|
2373
|
+
else if (deactivationEpoch !== undefined && deactivationEpoch < Number.MAX_SAFE_INTEGER) {
|
|
2374
|
+
stakeAccount.state = 'deactivating';
|
|
2375
|
+
}
|
|
2376
|
+
else if (activationEpoch !== undefined && activationEpoch <= currentEpoch) {
|
|
2377
|
+
stakeAccount.state = 'active';
|
|
2378
|
+
}
|
|
2379
|
+
else {
|
|
2380
|
+
stakeAccount.state = 'activating';
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
catch {
|
|
2386
|
+
// If parsing fails, keep default 'inactive' state
|
|
2387
|
+
}
|
|
2388
|
+
stakeAccounts.push(stakeAccount);
|
|
2389
|
+
}
|
|
2390
|
+
return stakeAccounts;
|
|
2391
|
+
}
|
|
2283
2392
|
/**
|
|
2284
2393
|
* Withdraws stake from a stake pool using a Fogo session.
|
|
2285
2394
|
*
|
|
2286
|
-
* The on-chain program creates stake account PDAs
|
|
2287
|
-
*
|
|
2395
|
+
* The on-chain program creates stake account PDAs. The rent for these accounts
|
|
2396
|
+
* is paid by the payer (typically the paymaster), not deducted from the user's withdrawal.
|
|
2288
2397
|
*
|
|
2289
2398
|
* @param connection - Solana connection
|
|
2290
2399
|
* @param stakePoolAddress - The stake pool to withdraw from
|
|
2291
2400
|
* @param signerOrSession - The session signer public key
|
|
2292
2401
|
* @param userPubkey - User's wallet (used for PDA derivation and token ownership)
|
|
2402
|
+
* @param payer - Payer for stake account rent (typically paymaster)
|
|
2293
2403
|
* @param amount - Amount of pool tokens to withdraw
|
|
2294
2404
|
* @param userStakeSeedStart - Starting seed for user stake PDA derivation (default: 0)
|
|
2295
2405
|
* @param useReserve - Whether to withdraw from reserve (default: false)
|
|
@@ -2297,7 +2407,7 @@ async function findNextUserStakeSeed(connection, programId, userPubkey, startSee
|
|
|
2297
2407
|
* @param minimumLamportsOut - Minimum lamports to receive (slippage protection)
|
|
2298
2408
|
* @param validatorComparator - Optional comparator for validator selection
|
|
2299
2409
|
*/
|
|
2300
|
-
async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, amount, userStakeSeedStart = 0, useReserve = false, voteAccountAddress, minimumLamportsOut = 0, validatorComparator) {
|
|
2410
|
+
async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, payer, amount, userStakeSeedStart = 0, useReserve = false, voteAccountAddress, minimumLamportsOut = 0, validatorComparator) {
|
|
2301
2411
|
const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
|
|
2302
2412
|
const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
|
|
2303
2413
|
const stakePool = stakePoolAccount.account.data;
|
|
@@ -2361,7 +2471,7 @@ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSe
|
|
|
2361
2471
|
const stakeReceiverPubkey = findUserStakeProgramAddress(stakePoolProgramId, userPubkey, userStakeSeed);
|
|
2362
2472
|
stakeAccountPubkeys.push(stakeReceiverPubkey);
|
|
2363
2473
|
userStakeSeeds.push(userStakeSeed);
|
|
2364
|
-
// The on-chain program
|
|
2474
|
+
// The on-chain program creates the stake account PDA and rent is paid by payer.
|
|
2365
2475
|
instructions.push(StakePoolInstruction.withdrawStakeWithSession({
|
|
2366
2476
|
programId: stakePoolProgramId,
|
|
2367
2477
|
stakePool: stakePoolAddress,
|
|
@@ -2375,6 +2485,7 @@ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSe
|
|
|
2375
2485
|
poolMint: stakePool.poolMint,
|
|
2376
2486
|
tokenProgramId: stakePool.tokenProgramId,
|
|
2377
2487
|
programSigner,
|
|
2488
|
+
payer,
|
|
2378
2489
|
poolTokensIn: withdrawAccount.poolAmount.toNumber(),
|
|
2379
2490
|
minimumLamportsOut,
|
|
2380
2491
|
userStakeSeed,
|
|
@@ -2773,6 +2884,7 @@ exports.getStakeAccount = getStakeAccount;
|
|
|
2773
2884
|
exports.getStakePoolAccount = getStakePoolAccount;
|
|
2774
2885
|
exports.getStakePoolAccounts = getStakePoolAccounts;
|
|
2775
2886
|
exports.getStakePoolProgramId = getStakePoolProgramId;
|
|
2887
|
+
exports.getUserStakeAccounts = getUserStakeAccounts;
|
|
2776
2888
|
exports.increaseValidatorStake = increaseValidatorStake;
|
|
2777
2889
|
exports.removeValidatorFromPool = removeValidatorFromPool;
|
|
2778
2890
|
exports.stakePoolInfo = stakePoolInfo;
|