@ignitionfi/spl-stake-pool 1.1.20 → 1.1.21

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.
@@ -4,7 +4,7 @@ import { InstructionType } from './utils';
4
4
  /**
5
5
  * An enumeration of valid StakePoolInstructionType's
6
6
  */
7
- export type StakePoolInstructionType = 'IncreaseValidatorStake' | 'DecreaseValidatorStake' | 'UpdateValidatorListBalance' | 'UpdateStakePoolBalance' | 'CleanupRemovedValidatorEntries' | 'DepositStake' | 'DepositSol' | 'WithdrawStake' | 'WithdrawSol' | 'IncreaseAdditionalValidatorStake' | 'DecreaseAdditionalValidatorStake' | 'DecreaseValidatorStakeWithReserve' | 'Redelegate' | 'AddValidatorToPool' | 'RemoveValidatorFromPool' | 'DepositWsolWithSession' | 'WithdrawWsolWithSession';
7
+ export type StakePoolInstructionType = 'IncreaseValidatorStake' | 'DecreaseValidatorStake' | 'UpdateValidatorListBalance' | 'UpdateStakePoolBalance' | 'CleanupRemovedValidatorEntries' | 'DepositStake' | 'DepositSol' | 'WithdrawStake' | 'WithdrawSol' | 'IncreaseAdditionalValidatorStake' | 'DecreaseAdditionalValidatorStake' | 'DecreaseValidatorStakeWithReserve' | 'Redelegate' | 'AddValidatorToPool' | 'RemoveValidatorFromPool' | 'DepositWsolWithSession' | 'WithdrawWsolWithSession' | 'WithdrawStakeWithSession';
8
8
  export declare function tokenMetadataLayout(instruction: number, nameLength: number, symbolLength: number, uriLength: number): {
9
9
  index: number;
10
10
  layout: BufferLayout.Structure<any>;
@@ -163,6 +163,24 @@ export type WithdrawWsolWithSessionParams = {
163
163
  payer?: PublicKey;
164
164
  solWithdrawAuthority?: PublicKey;
165
165
  };
166
+ export type WithdrawStakeWithSessionParams = {
167
+ programId: PublicKey;
168
+ stakePool: PublicKey;
169
+ validatorList: PublicKey;
170
+ withdrawAuthority: PublicKey;
171
+ stakeToSplit: PublicKey;
172
+ stakeToReceive: PublicKey;
173
+ /** The session signer (user or session) - used as both stake authority and transfer authority */
174
+ sessionSigner: PublicKey;
175
+ burnFromPool: PublicKey;
176
+ managerFeeAccount: PublicKey;
177
+ poolMint: PublicKey;
178
+ tokenProgramId: PublicKey;
179
+ /** The program signer PDA derived from PROGRAM_SIGNER_SEED */
180
+ programSigner: PublicKey;
181
+ poolTokensIn: number;
182
+ minimumLamportsOut: number;
183
+ };
166
184
  /**
167
185
  * Deposit SOL directly into the pool's reserve account. The output is a "pool" token
168
186
  * representing ownership into the pool. Inputs are converted to the current ratio.
@@ -306,6 +324,10 @@ export declare class StakePoolInstruction {
306
324
  * Creates a transaction instruction to withdraw WSOL from a stake pool using a session.
307
325
  */
308
326
  static withdrawWsolWithSession(params: WithdrawWsolWithSessionParams): TransactionInstruction;
327
+ /**
328
+ * Creates a transaction instruction to withdraw stake from a stake pool using a Fogo session.
329
+ */
330
+ static withdrawStakeWithSession(params: WithdrawStakeWithSessionParams): TransactionInstruction;
309
331
  /**
310
332
  * Creates an instruction to create metadata
311
333
  * using the mpl token metadata program for the pool token
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ignitionfi/spl-stake-pool",
3
- "version": "1.1.20",
3
+ "version": "1.1.21",
4
4
  "description": "Ignition Stake Pool SDK for FOGO",
5
5
  "contributors": [
6
6
  "Anza Maintainers <maintainers@anza.xyz>",
package/src/index.ts CHANGED
@@ -890,6 +890,164 @@ export async function withdrawWsolWithSession(
890
890
  }
891
891
  }
892
892
 
893
+ /**
894
+ * Creates instructions required to withdraw stake from a stake pool using a Fogo session.
895
+ * The withdrawn stake account will be authorized to the user's wallet.
896
+ */
897
+ export async function withdrawStakeWithSession(
898
+ connection: Connection,
899
+ stakePoolAddress: PublicKey,
900
+ signerOrSession: PublicKey,
901
+ userPubkey: PublicKey,
902
+ amount: number,
903
+ useReserve = false,
904
+ voteAccountAddress?: PublicKey,
905
+ minimumLamportsOut: number = 0,
906
+ payer?: PublicKey,
907
+ validatorComparator?: (_a: ValidatorAccount, _b: ValidatorAccount) => number,
908
+ ) {
909
+ const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress)
910
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint)
911
+ const stakePool = stakePoolAccount.account.data
912
+ const poolTokens = solToLamports(amount)
913
+ const poolAmount = new BN(poolTokens)
914
+
915
+ const poolTokenAccount = getAssociatedTokenAddressSync(stakePool.poolMint, userPubkey)
916
+ const tokenAccount = await getAccount(connection, poolTokenAccount)
917
+
918
+ if (tokenAccount.amount < poolTokens) {
919
+ throw new Error(
920
+ `Not enough token balance to withdraw ${amount} pool tokens.
921
+ Maximum withdraw amount is ${lamportsToSol(tokenAccount.amount)} pool tokens.`,
922
+ )
923
+ }
924
+
925
+ const [programSigner] = PublicKey.findProgramAddressSync(
926
+ [Buffer.from('fogo_session_program_signer')],
927
+ stakePoolProgramId,
928
+ )
929
+
930
+ const withdrawAuthority = await findWithdrawAuthorityProgramAddress(
931
+ stakePoolProgramId,
932
+ stakePoolAddress,
933
+ )
934
+
935
+ const stakeAccountRentExemption = await connection.getMinimumBalanceForRentExemption(StakeProgram.space)
936
+
937
+ // Determine which stake accounts to withdraw from
938
+ const withdrawAccounts: WithdrawAccount[] = []
939
+
940
+ if (useReserve) {
941
+ withdrawAccounts.push({
942
+ stakeAddress: stakePool.reserveStake,
943
+ voteAddress: undefined,
944
+ poolAmount,
945
+ })
946
+ } else if (voteAccountAddress) {
947
+ const stakeAccountAddress = await findStakeProgramAddress(
948
+ stakePoolProgramId,
949
+ voteAccountAddress,
950
+ stakePoolAddress,
951
+ )
952
+ const stakeAccount = await connection.getAccountInfo(stakeAccountAddress)
953
+ if (!stakeAccount) {
954
+ throw new Error(`Validator stake account not found for vote address ${voteAccountAddress.toBase58()}`)
955
+ }
956
+
957
+ const availableLamports = new BN(
958
+ stakeAccount.lamports - MINIMUM_ACTIVE_STAKE - stakeAccountRentExemption,
959
+ )
960
+ if (availableLamports.lt(new BN(0))) {
961
+ throw new Error('Invalid Stake Account')
962
+ }
963
+ const availableForWithdrawal = calcLamportsWithdrawAmount(
964
+ stakePool,
965
+ availableLamports,
966
+ )
967
+
968
+ if (availableForWithdrawal.lt(poolAmount)) {
969
+ throw new Error(
970
+ `Not enough lamports available for withdrawal from ${stakeAccountAddress},
971
+ ${poolAmount} asked, ${availableForWithdrawal} available.`,
972
+ )
973
+ }
974
+ withdrawAccounts.push({
975
+ stakeAddress: stakeAccountAddress,
976
+ voteAddress: voteAccountAddress,
977
+ poolAmount,
978
+ })
979
+ } else {
980
+ // Get the list of accounts to withdraw from automatically
981
+ withdrawAccounts.push(
982
+ ...(await prepareWithdrawAccounts(
983
+ connection,
984
+ stakePool,
985
+ stakePoolAddress,
986
+ poolAmount,
987
+ validatorComparator,
988
+ poolTokenAccount.equals(stakePool.managerFeeAccount),
989
+ )),
990
+ )
991
+ }
992
+
993
+ const instructions: TransactionInstruction[] = []
994
+ const signers: Signer[] = []
995
+ const stakeAccountPubkeys: PublicKey[] = []
996
+
997
+ // Max 5 accounts to prevent an error: "Transaction too large"
998
+ const maxWithdrawAccounts = 5
999
+ let i = 0
1000
+
1001
+ for (const withdrawAccount of withdrawAccounts) {
1002
+ if (i >= maxWithdrawAccounts) {
1003
+ break
1004
+ }
1005
+
1006
+ // Create a new stake account to receive the withdrawal
1007
+ const stakeReceiver = Keypair.generate()
1008
+ signers.push(stakeReceiver)
1009
+ stakeAccountPubkeys.push(stakeReceiver.publicKey)
1010
+
1011
+ // Create the stake account that will receive the split stake
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
+ }),
1020
+ )
1021
+
1022
+ // Add the withdraw stake with session instruction
1023
+ instructions.push(
1024
+ StakePoolInstruction.withdrawStakeWithSession({
1025
+ programId: stakePoolProgramId,
1026
+ stakePool: stakePoolAddress,
1027
+ validatorList: stakePool.validatorList,
1028
+ withdrawAuthority,
1029
+ stakeToSplit: withdrawAccount.stakeAddress,
1030
+ stakeToReceive: stakeReceiver.publicKey,
1031
+ sessionSigner: signerOrSession,
1032
+ burnFromPool: poolTokenAccount,
1033
+ managerFeeAccount: stakePool.managerFeeAccount,
1034
+ poolMint: stakePool.poolMint,
1035
+ tokenProgramId: stakePool.tokenProgramId,
1036
+ programSigner,
1037
+ poolTokensIn: withdrawAccount.poolAmount.toNumber(),
1038
+ minimumLamportsOut: solToLamports(minimumLamportsOut),
1039
+ }),
1040
+ )
1041
+ i++
1042
+ }
1043
+
1044
+ return {
1045
+ instructions,
1046
+ signers,
1047
+ stakeAccountPubkeys,
1048
+ }
1049
+ }
1050
+
893
1051
  export async function addValidatorToPool(
894
1052
  connection: Connection,
895
1053
  stakePoolAddress: PublicKey,
@@ -41,6 +41,7 @@ export type StakePoolInstructionType
41
41
  | 'RemoveValidatorFromPool'
42
42
  | 'DepositWsolWithSession'
43
43
  | 'WithdrawWsolWithSession'
44
+ | 'WithdrawStakeWithSession'
44
45
 
45
46
  // 'UpdateTokenMetadata' and 'CreateTokenMetadata' have dynamic layouts
46
47
 
@@ -193,7 +194,8 @@ export const STAKE_POOL_INSTRUCTION_LAYOUTS: {
193
194
  index: 24,
194
195
  layout: BufferLayout.struct<any>([
195
196
  BufferLayout.u8('instruction'),
196
- BufferLayout.ns64('lamports'),
197
+ BufferLayout.ns64('poolTokensIn'),
198
+ BufferLayout.ns64('minimumLamportsOut'),
197
199
  ]),
198
200
  },
199
201
  DepositSolWithSlippage: {
@@ -226,6 +228,14 @@ export const STAKE_POOL_INSTRUCTION_LAYOUTS: {
226
228
  BufferLayout.ns64('minimumLamportsOut'),
227
229
  ]),
228
230
  },
231
+ WithdrawStakeWithSession: {
232
+ index: 29,
233
+ layout: BufferLayout.struct<any>([
234
+ BufferLayout.u8('instruction'),
235
+ BufferLayout.ns64('poolTokensIn'),
236
+ BufferLayout.ns64('minimumLamportsOut'),
237
+ ]),
238
+ },
229
239
  })
230
240
 
231
241
  /**
@@ -391,6 +401,25 @@ export type WithdrawWsolWithSessionParams = {
391
401
  solWithdrawAuthority?: PublicKey
392
402
  }
393
403
 
404
+ export type WithdrawStakeWithSessionParams = {
405
+ programId: PublicKey
406
+ stakePool: PublicKey
407
+ validatorList: PublicKey
408
+ withdrawAuthority: PublicKey
409
+ stakeToSplit: PublicKey
410
+ stakeToReceive: PublicKey
411
+ /** The session signer (user or session) - used as both stake authority and transfer authority */
412
+ sessionSigner: PublicKey
413
+ burnFromPool: PublicKey
414
+ managerFeeAccount: PublicKey
415
+ poolMint: PublicKey
416
+ tokenProgramId: PublicKey
417
+ /** The program signer PDA derived from PROGRAM_SIGNER_SEED */
418
+ programSigner: PublicKey
419
+ poolTokensIn: number
420
+ minimumLamportsOut: number
421
+ }
422
+
394
423
  /**
395
424
  * Deposit SOL directly into the pool's reserve account. The output is a "pool" token
396
425
  * representing ownership into the pool. Inputs are converted to the current ratio.
@@ -1163,6 +1192,40 @@ export class StakePoolInstruction {
1163
1192
  })
1164
1193
  }
1165
1194
 
1195
+ /**
1196
+ * Creates a transaction instruction to withdraw stake from a stake pool using a Fogo session.
1197
+ */
1198
+ static withdrawStakeWithSession(params: WithdrawStakeWithSessionParams): TransactionInstruction {
1199
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawStakeWithSession
1200
+ const data = encodeData(type, {
1201
+ poolTokensIn: params.poolTokensIn,
1202
+ minimumLamportsOut: params.minimumLamportsOut,
1203
+ })
1204
+
1205
+ const keys = [
1206
+ { pubkey: params.stakePool, isSigner: false, isWritable: true },
1207
+ { pubkey: params.validatorList, isSigner: false, isWritable: true },
1208
+ { pubkey: params.withdrawAuthority, isSigner: false, isWritable: false },
1209
+ { pubkey: params.stakeToSplit, isSigner: false, isWritable: true },
1210
+ { pubkey: params.stakeToReceive, isSigner: false, isWritable: true },
1211
+ { pubkey: params.sessionSigner, isSigner: true, isWritable: false }, // user_stake_authority_info (signer_or_session)
1212
+ { pubkey: params.sessionSigner, isSigner: false, isWritable: false }, // user_transfer_authority_info (not used in session path)
1213
+ { pubkey: params.burnFromPool, isSigner: false, isWritable: true },
1214
+ { pubkey: params.managerFeeAccount, isSigner: false, isWritable: true },
1215
+ { pubkey: params.poolMint, isSigner: false, isWritable: true },
1216
+ { pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1217
+ { pubkey: params.tokenProgramId, isSigner: false, isWritable: false },
1218
+ { pubkey: StakeProgram.programId, isSigner: false, isWritable: false },
1219
+ { pubkey: params.programSigner, isSigner: false, isWritable: false },
1220
+ ]
1221
+
1222
+ return new TransactionInstruction({
1223
+ programId: params.programId,
1224
+ keys,
1225
+ data,
1226
+ })
1227
+ }
1228
+
1166
1229
  /**
1167
1230
  * Creates an instruction to create metadata
1168
1231
  * using the mpl token metadata program for the pool token