@ignitionfi/spl-stake-pool 1.1.21 → 1.1.23
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/constants.d.ts +1 -0
- package/dist/index.browser.cjs.js +77 -20
- package/dist/index.browser.cjs.js.map +1 -1
- package/dist/index.browser.esm.js +71 -21
- package/dist/index.browser.esm.js.map +1 -1
- package/dist/index.cjs.js +77 -20
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +33 -5
- package/dist/index.esm.js +71 -21
- package/dist/index.esm.js.map +1 -1
- package/dist/index.iife.js +78 -21
- 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 +5 -0
- package/dist/utils/program-address.d.ts +5 -0
- package/package.json +1 -1
- package/src/constants.ts +3 -0
- package/src/index.ts +72 -22
- package/src/instructions.ts +9 -0
- package/src/utils/program-address.ts +22 -0
package/dist/instructions.d.ts
CHANGED
|
@@ -169,6 +169,7 @@ export type WithdrawStakeWithSessionParams = {
|
|
|
169
169
|
validatorList: PublicKey;
|
|
170
170
|
withdrawAuthority: PublicKey;
|
|
171
171
|
stakeToSplit: PublicKey;
|
|
172
|
+
/** The stake account PDA that will receive the withdrawn stake (derived from user wallet + seed) */
|
|
172
173
|
stakeToReceive: PublicKey;
|
|
173
174
|
/** The session signer (user or session) - used as both stake authority and transfer authority */
|
|
174
175
|
sessionSigner: PublicKey;
|
|
@@ -178,8 +179,12 @@ export type WithdrawStakeWithSessionParams = {
|
|
|
178
179
|
tokenProgramId: PublicKey;
|
|
179
180
|
/** The program signer PDA derived from PROGRAM_SIGNER_SEED */
|
|
180
181
|
programSigner: PublicKey;
|
|
182
|
+
/** Payer for the stake account rent (paymaster payer) */
|
|
183
|
+
payer: PublicKey;
|
|
181
184
|
poolTokensIn: number;
|
|
182
185
|
minimumLamportsOut: number;
|
|
186
|
+
/** Seed used to derive the user stake PDA */
|
|
187
|
+
userStakeSeed: number;
|
|
183
188
|
};
|
|
184
189
|
/**
|
|
185
190
|
* Deposit SOL directly into the pool's reserve account. The output is a "pool" token
|
|
@@ -24,3 +24,8 @@ export declare function findEphemeralStakeProgramAddress(programId: PublicKey, s
|
|
|
24
24
|
* Generates the metadata program address for the stake pool
|
|
25
25
|
*/
|
|
26
26
|
export declare function findMetadataAddress(stakePoolMintAddress: PublicKey): PublicKey;
|
|
27
|
+
/**
|
|
28
|
+
* Generates the user stake account PDA for session-based withdrawals.
|
|
29
|
+
* The PDA is derived from the user's wallet and a unique seed.
|
|
30
|
+
*/
|
|
31
|
+
export declare function findUserStakeProgramAddress(programId: PublicKey, userWallet: PublicKey, seed: BN | number): PublicKey;
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
|
@@ -24,6 +24,9 @@ export const EPHEMERAL_STAKE_SEED_PREFIX = Buffer.from('ephemeral')
|
|
|
24
24
|
// Seed used to derive transient stake accounts.
|
|
25
25
|
export const TRANSIENT_STAKE_SEED_PREFIX = Buffer.from('transient')
|
|
26
26
|
|
|
27
|
+
// Seed for user stake account created during session withdrawal
|
|
28
|
+
export const USER_STAKE_SEED_PREFIX = Buffer.from('user_stake')
|
|
29
|
+
|
|
27
30
|
// Minimum amount of staked SOL required in a validator stake account to allow
|
|
28
31
|
// for merges without a mismatch on credits observed
|
|
29
32
|
export const MINIMUM_ACTIVE_STAKE = LAMPORTS_PER_SOL
|
package/src/index.ts
CHANGED
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
findMetadataAddress,
|
|
41
41
|
findStakeProgramAddress,
|
|
42
42
|
findTransientStakeProgramAddress,
|
|
43
|
+
findUserStakeProgramAddress,
|
|
43
44
|
findWithdrawAuthorityProgramAddress,
|
|
44
45
|
findWsolTransientProgramAddress,
|
|
45
46
|
getValidatorListAccount,
|
|
@@ -66,6 +67,14 @@ export {
|
|
|
66
67
|
ValidatorListLayout,
|
|
67
68
|
ValidatorStakeInfoLayout,
|
|
68
69
|
} from './layouts'
|
|
70
|
+
export {
|
|
71
|
+
findUserStakeProgramAddress,
|
|
72
|
+
findWithdrawAuthorityProgramAddress,
|
|
73
|
+
findStakeProgramAddress,
|
|
74
|
+
findTransientStakeProgramAddress,
|
|
75
|
+
findEphemeralStakeProgramAddress,
|
|
76
|
+
findWsolTransientProgramAddress,
|
|
77
|
+
} from './utils'
|
|
69
78
|
|
|
70
79
|
export interface ValidatorListAccount {
|
|
71
80
|
pubkey: PublicKey
|
|
@@ -891,19 +900,63 @@ export async function withdrawWsolWithSession(
|
|
|
891
900
|
}
|
|
892
901
|
|
|
893
902
|
/**
|
|
894
|
-
*
|
|
895
|
-
*
|
|
903
|
+
* Finds the next available seed for creating a user stake PDA.
|
|
904
|
+
* Scans from startSeed until an unused PDA is found.
|
|
905
|
+
*
|
|
906
|
+
* @param connection - Solana connection
|
|
907
|
+
* @param programId - The stake pool program ID
|
|
908
|
+
* @param userPubkey - User's wallet (used for PDA derivation)
|
|
909
|
+
* @param startSeed - Starting seed to search from (default: 0)
|
|
910
|
+
* @param maxSeed - Maximum seed to check before giving up (default: 1000)
|
|
911
|
+
* @returns The next available seed
|
|
912
|
+
* @throws Error if no available seed found within maxSeed
|
|
913
|
+
*/
|
|
914
|
+
export async function findNextUserStakeSeed(
|
|
915
|
+
connection: Connection,
|
|
916
|
+
programId: PublicKey,
|
|
917
|
+
userPubkey: PublicKey,
|
|
918
|
+
startSeed: number = 0,
|
|
919
|
+
maxSeed: number = 1000,
|
|
920
|
+
): Promise<number> {
|
|
921
|
+
for (let seed = startSeed; seed < startSeed + maxSeed; seed++) {
|
|
922
|
+
const pda = findUserStakeProgramAddress(programId, userPubkey, seed)
|
|
923
|
+
const account = await connection.getAccountInfo(pda)
|
|
924
|
+
if (!account) {
|
|
925
|
+
return seed
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
throw new Error(`No available user stake seed found between ${startSeed} and ${startSeed + maxSeed - 1}`)
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* Withdraws stake from a stake pool using a Fogo session.
|
|
933
|
+
*
|
|
934
|
+
* The on-chain program creates stake account PDAs, so no pre-creation step is needed.
|
|
935
|
+
* The paymaster payer provides rent for the stake account PDAs.
|
|
936
|
+
*
|
|
937
|
+
* @param connection - Solana connection
|
|
938
|
+
* @param stakePoolAddress - The stake pool to withdraw from
|
|
939
|
+
* @param signerOrSession - The session signer public key
|
|
940
|
+
* @param userPubkey - User's wallet (used for PDA derivation and token ownership)
|
|
941
|
+
* @param payer - Payer for stake account rent (paymaster payer in session context)
|
|
942
|
+
* @param amount - Amount of pool tokens to withdraw
|
|
943
|
+
* @param userStakeSeedStart - Starting seed for user stake PDA derivation (default: 0)
|
|
944
|
+
* @param useReserve - Whether to withdraw from reserve (default: false)
|
|
945
|
+
* @param voteAccountAddress - Optional specific validator to withdraw from
|
|
946
|
+
* @param minimumLamportsOut - Minimum lamports to receive (slippage protection)
|
|
947
|
+
* @param validatorComparator - Optional comparator for validator selection
|
|
896
948
|
*/
|
|
897
949
|
export async function withdrawStakeWithSession(
|
|
898
950
|
connection: Connection,
|
|
899
951
|
stakePoolAddress: PublicKey,
|
|
900
952
|
signerOrSession: PublicKey,
|
|
901
953
|
userPubkey: PublicKey,
|
|
954
|
+
payer: PublicKey,
|
|
902
955
|
amount: number,
|
|
956
|
+
userStakeSeedStart: number = 0,
|
|
903
957
|
useReserve = false,
|
|
904
958
|
voteAccountAddress?: PublicKey,
|
|
905
959
|
minimumLamportsOut: number = 0,
|
|
906
|
-
payer?: PublicKey,
|
|
907
960
|
validatorComparator?: (_a: ValidatorAccount, _b: ValidatorAccount) => number,
|
|
908
961
|
) {
|
|
909
962
|
const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress)
|
|
@@ -991,8 +1044,8 @@ export async function withdrawStakeWithSession(
|
|
|
991
1044
|
}
|
|
992
1045
|
|
|
993
1046
|
const instructions: TransactionInstruction[] = []
|
|
994
|
-
const signers: Signer[] = []
|
|
995
1047
|
const stakeAccountPubkeys: PublicKey[] = []
|
|
1048
|
+
const userStakeSeeds: number[] = []
|
|
996
1049
|
|
|
997
1050
|
// Max 5 accounts to prevent an error: "Transaction too large"
|
|
998
1051
|
const maxWithdrawAccounts = 5
|
|
@@ -1003,23 +1056,18 @@ export async function withdrawStakeWithSession(
|
|
|
1003
1056
|
break
|
|
1004
1057
|
}
|
|
1005
1058
|
|
|
1006
|
-
//
|
|
1007
|
-
const
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
instructions.push(
|
|
1013
|
-
SystemProgram.createAccount({
|
|
1014
|
-
fromPubkey: payer ?? userPubkey,
|
|
1015
|
-
newAccountPubkey: stakeReceiver.publicKey,
|
|
1016
|
-
lamports: stakeAccountRentExemption,
|
|
1017
|
-
space: StakeProgram.space,
|
|
1018
|
-
programId: StakeProgram.programId,
|
|
1019
|
-
}),
|
|
1059
|
+
// Derive the stake account PDA for this withdrawal
|
|
1060
|
+
const userStakeSeed = userStakeSeedStart + i
|
|
1061
|
+
const stakeReceiverPubkey = findUserStakeProgramAddress(
|
|
1062
|
+
stakePoolProgramId,
|
|
1063
|
+
userPubkey,
|
|
1064
|
+
userStakeSeed,
|
|
1020
1065
|
)
|
|
1021
1066
|
|
|
1022
|
-
|
|
1067
|
+
stakeAccountPubkeys.push(stakeReceiverPubkey)
|
|
1068
|
+
userStakeSeeds.push(userStakeSeed)
|
|
1069
|
+
|
|
1070
|
+
// The on-chain program will create the stake account PDA
|
|
1023
1071
|
instructions.push(
|
|
1024
1072
|
StakePoolInstruction.withdrawStakeWithSession({
|
|
1025
1073
|
programId: stakePoolProgramId,
|
|
@@ -1027,15 +1075,17 @@ export async function withdrawStakeWithSession(
|
|
|
1027
1075
|
validatorList: stakePool.validatorList,
|
|
1028
1076
|
withdrawAuthority,
|
|
1029
1077
|
stakeToSplit: withdrawAccount.stakeAddress,
|
|
1030
|
-
stakeToReceive:
|
|
1078
|
+
stakeToReceive: stakeReceiverPubkey,
|
|
1031
1079
|
sessionSigner: signerOrSession,
|
|
1032
1080
|
burnFromPool: poolTokenAccount,
|
|
1033
1081
|
managerFeeAccount: stakePool.managerFeeAccount,
|
|
1034
1082
|
poolMint: stakePool.poolMint,
|
|
1035
1083
|
tokenProgramId: stakePool.tokenProgramId,
|
|
1036
1084
|
programSigner,
|
|
1085
|
+
payer,
|
|
1037
1086
|
poolTokensIn: withdrawAccount.poolAmount.toNumber(),
|
|
1038
|
-
minimumLamportsOut
|
|
1087
|
+
minimumLamportsOut,
|
|
1088
|
+
userStakeSeed,
|
|
1039
1089
|
}),
|
|
1040
1090
|
)
|
|
1041
1091
|
i++
|
|
@@ -1043,8 +1093,8 @@ export async function withdrawStakeWithSession(
|
|
|
1043
1093
|
|
|
1044
1094
|
return {
|
|
1045
1095
|
instructions,
|
|
1046
|
-
signers,
|
|
1047
1096
|
stakeAccountPubkeys,
|
|
1097
|
+
userStakeSeeds,
|
|
1048
1098
|
}
|
|
1049
1099
|
}
|
|
1050
1100
|
|
package/src/instructions.ts
CHANGED
|
@@ -234,6 +234,7 @@ export const STAKE_POOL_INSTRUCTION_LAYOUTS: {
|
|
|
234
234
|
BufferLayout.u8('instruction'),
|
|
235
235
|
BufferLayout.ns64('poolTokensIn'),
|
|
236
236
|
BufferLayout.ns64('minimumLamportsOut'),
|
|
237
|
+
BufferLayout.ns64('userStakeSeed'),
|
|
237
238
|
]),
|
|
238
239
|
},
|
|
239
240
|
})
|
|
@@ -407,6 +408,7 @@ export type WithdrawStakeWithSessionParams = {
|
|
|
407
408
|
validatorList: PublicKey
|
|
408
409
|
withdrawAuthority: PublicKey
|
|
409
410
|
stakeToSplit: PublicKey
|
|
411
|
+
/** The stake account PDA that will receive the withdrawn stake (derived from user wallet + seed) */
|
|
410
412
|
stakeToReceive: PublicKey
|
|
411
413
|
/** The session signer (user or session) - used as both stake authority and transfer authority */
|
|
412
414
|
sessionSigner: PublicKey
|
|
@@ -416,8 +418,12 @@ export type WithdrawStakeWithSessionParams = {
|
|
|
416
418
|
tokenProgramId: PublicKey
|
|
417
419
|
/** The program signer PDA derived from PROGRAM_SIGNER_SEED */
|
|
418
420
|
programSigner: PublicKey
|
|
421
|
+
/** Payer for the stake account rent (paymaster payer) */
|
|
422
|
+
payer: PublicKey
|
|
419
423
|
poolTokensIn: number
|
|
420
424
|
minimumLamportsOut: number
|
|
425
|
+
/** Seed used to derive the user stake PDA */
|
|
426
|
+
userStakeSeed: number
|
|
421
427
|
}
|
|
422
428
|
|
|
423
429
|
/**
|
|
@@ -1200,6 +1206,7 @@ export class StakePoolInstruction {
|
|
|
1200
1206
|
const data = encodeData(type, {
|
|
1201
1207
|
poolTokensIn: params.poolTokensIn,
|
|
1202
1208
|
minimumLamportsOut: params.minimumLamportsOut,
|
|
1209
|
+
userStakeSeed: params.userStakeSeed,
|
|
1203
1210
|
})
|
|
1204
1211
|
|
|
1205
1212
|
const keys = [
|
|
@@ -1217,6 +1224,8 @@ export class StakePoolInstruction {
|
|
|
1217
1224
|
{ pubkey: params.tokenProgramId, isSigner: false, isWritable: false },
|
|
1218
1225
|
{ pubkey: StakeProgram.programId, isSigner: false, isWritable: false },
|
|
1219
1226
|
{ pubkey: params.programSigner, isSigner: false, isWritable: false },
|
|
1227
|
+
{ pubkey: params.payer, isSigner: true, isWritable: true },
|
|
1228
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1220
1229
|
]
|
|
1221
1230
|
|
|
1222
1231
|
return new TransactionInstruction({
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
EPHEMERAL_STAKE_SEED_PREFIX,
|
|
6
6
|
METADATA_PROGRAM_ID,
|
|
7
7
|
TRANSIENT_STAKE_SEED_PREFIX,
|
|
8
|
+
USER_STAKE_SEED_PREFIX,
|
|
8
9
|
} from '../constants'
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -101,3 +102,24 @@ export function findMetadataAddress(stakePoolMintAddress: PublicKey) {
|
|
|
101
102
|
)
|
|
102
103
|
return publicKey
|
|
103
104
|
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Generates the user stake account PDA for session-based withdrawals.
|
|
108
|
+
* The PDA is derived from the user's wallet and a unique seed.
|
|
109
|
+
*/
|
|
110
|
+
export function findUserStakeProgramAddress(
|
|
111
|
+
programId: PublicKey,
|
|
112
|
+
userWallet: PublicKey,
|
|
113
|
+
seed: BN | number,
|
|
114
|
+
) {
|
|
115
|
+
const seedBN = typeof seed === 'number' ? new BN(seed) : seed
|
|
116
|
+
const [publicKey] = PublicKey.findProgramAddressSync(
|
|
117
|
+
[
|
|
118
|
+
USER_STAKE_SEED_PREFIX,
|
|
119
|
+
userWallet.toBuffer(),
|
|
120
|
+
seedBN.toArrayLike(Buffer, 'le', 8),
|
|
121
|
+
],
|
|
122
|
+
programId,
|
|
123
|
+
)
|
|
124
|
+
return publicKey
|
|
125
|
+
}
|