spl_stake_pool/
instruction.rs

1//! Instruction types
2
3#![allow(clippy::too_many_arguments)]
4
5use {
6    crate::{
7        find_deposit_authority_program_address, find_ephemeral_stake_program_address,
8        find_stake_program_address, find_transient_stake_program_address,
9        find_withdraw_authority_program_address,
10        state::{Fee, FeeType, StakePool, ValidatorList},
11        MAX_VALIDATORS_TO_UPDATE,
12    },
13    borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
14    mpl_token_metadata::pda::find_metadata_account,
15    solana_program::{
16        instruction::{AccountMeta, Instruction},
17        pubkey::Pubkey,
18        stake, system_program, sysvar,
19    },
20    std::num::NonZeroU32,
21};
22
23/// Defines which validator vote account is set during the
24/// `SetPreferredValidator` instruction
25#[repr(C)]
26#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, BorshSchema)]
27pub enum PreferredValidatorType {
28    /// Set preferred validator for deposits
29    Deposit,
30    /// Set preferred validator for withdraws
31    Withdraw,
32}
33
34/// Defines which authority to update in the `SetFundingAuthority`
35/// instruction
36#[repr(C)]
37#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, BorshSchema)]
38pub enum FundingType {
39    /// Sets the stake deposit authority
40    StakeDeposit,
41    /// Sets the SOL deposit authority
42    SolDeposit,
43    /// Sets the SOL withdraw authority
44    SolWithdraw,
45}
46
47/// Instructions supported by the StakePool program.
48#[repr(C)]
49#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
50pub enum StakePoolInstruction {
51    ///   Initializes a new StakePool.
52    ///
53    ///   0. `[w]` New StakePool to create.
54    ///   1. `[s]` Manager
55    ///   2. `[]` Staker
56    ///   3. `[]` Stake pool withdraw authority
57    ///   4. `[w]` Uninitialized validator stake list storage account
58    ///   5. `[]` Reserve stake account must be initialized, have zero balance,
59    ///       and staker / withdrawer authority set to pool withdraw authority.
60    ///   6. `[]` Pool token mint. Must have zero supply, owned by withdraw authority.
61    ///   7. `[]` Pool account to deposit the generated fee for manager.
62    ///   8. `[]` Token program id
63    ///   9. `[]` (Optional) Deposit authority that must sign all deposits.
64    ///      Defaults to the program address generated using
65    ///      `find_deposit_authority_program_address`, making deposits permissionless.
66    Initialize {
67        /// Fee assessed as percentage of perceived rewards
68        fee: Fee,
69        /// Fee charged per withdrawal as percentage of withdrawal
70        withdrawal_fee: Fee,
71        /// Fee charged per deposit as percentage of deposit
72        deposit_fee: Fee,
73        /// Percentage [0-100] of deposit_fee that goes to referrer
74        referral_fee: u8,
75        /// Maximum expected number of validators
76        max_validators: u32,
77    },
78
79    ///   (Staker only) Adds stake account delegated to validator to the pool's
80    ///   list of managed validators.
81    ///
82    ///   The stake account will have the rent-exempt amount plus
83    ///   `max(crate::MINIMUM_ACTIVE_STAKE, solana_program::stake::tools::get_minimum_delegation())`.
84    ///   It is funded from the stake pool reserve.
85    ///
86    ///   0. `[w]` Stake pool
87    ///   1. `[s]` Staker
88    ///   2. `[w]` Reserve stake account
89    ///   3. `[]` Stake pool withdraw authority
90    ///   4. `[w]` Validator stake list storage account
91    ///   5. `[w]` Stake account to add to the pool
92    ///   6. `[]` Validator this stake account will be delegated to
93    ///   7. `[]` Rent sysvar
94    ///   8. `[]` Clock sysvar
95    ///   9. '[]' Stake history sysvar
96    ///  10. '[]' Stake config sysvar
97    ///  11. `[]` System program
98    ///  12. `[]` Stake program
99    ///
100    ///  userdata: optional non-zero u32 seed used for generating the validator
101    ///  stake address
102    AddValidatorToPool(u32),
103
104    ///   (Staker only) Removes validator from the pool, deactivating its stake
105    ///
106    ///   Only succeeds if the validator stake account has the minimum of
107    ///   `max(crate::MINIMUM_ACTIVE_STAKE, solana_program::stake::tools::get_minimum_delegation())`.
108    ///   plus the rent-exempt amount.
109    ///
110    ///   0. `[w]` Stake pool
111    ///   1. `[s]` Staker
112    ///   2. `[]` Stake pool withdraw authority
113    ///   3. `[w]` Validator stake list storage account
114    ///   4. `[w]` Stake account to remove from the pool
115    ///   5. `[]` Transient stake account, to check that that we're not trying to activate
116    ///   6. `[]` Sysvar clock
117    ///   7. `[]` Stake program id,
118    RemoveValidatorFromPool,
119
120    /// (Staker only) Decrease active stake on a validator, eventually moving it to the reserve
121    ///
122    /// Internally, this instruction splits a validator stake account into its
123    /// corresponding transient stake account and deactivates it.
124    ///
125    /// In order to rebalance the pool without taking custody, the staker needs
126    /// a way of reducing the stake on a stake account. This instruction splits
127    /// some amount of stake, up to the total activated stake, from the canonical
128    /// validator stake account, into its "transient" stake account.
129    ///
130    /// The instruction only succeeds if the transient stake account does not
131    /// exist. The amount of lamports to move must be at least rent-exemption plus
132    /// `max(crate::MINIMUM_ACTIVE_STAKE, solana_program::stake::tools::get_minimum_delegation())`.
133    ///
134    ///  0. `[]` Stake pool
135    ///  1. `[s]` Stake pool staker
136    ///  2. `[]` Stake pool withdraw authority
137    ///  3. `[w]` Validator list
138    ///  4. `[w]` Canonical stake account to split from
139    ///  5. `[w]` Transient stake account to receive split
140    ///  6. `[]` Clock sysvar
141    ///  7. `[]` Rent sysvar
142    ///  8. `[]` System program
143    ///  9. `[]` Stake program
144    DecreaseValidatorStake {
145        /// amount of lamports to split into the transient stake account
146        lamports: u64,
147        /// seed used to create transient stake account
148        transient_stake_seed: u64,
149    },
150
151    /// (Staker only) Increase stake on a validator from the reserve account
152    ///
153    /// Internally, this instruction splits reserve stake into a transient stake
154    /// account and delegate to the appropriate validator. `UpdateValidatorListBalance`
155    /// will do the work of merging once it's ready.
156    ///
157    /// This instruction only succeeds if the transient stake account does not exist.
158    /// The minimum amount to move is rent-exemption plus
159    /// `max(crate::MINIMUM_ACTIVE_STAKE, solana_program::stake::tools::get_minimum_delegation())`.
160    ///
161    ///  0. `[]` Stake pool
162    ///  1. `[s]` Stake pool staker
163    ///  2. `[]` Stake pool withdraw authority
164    ///  3. `[w]` Validator list
165    ///  4. `[w]` Stake pool reserve stake
166    ///  5. `[w]` Transient stake account
167    ///  6. `[]` Validator stake account
168    ///  7. `[]` Validator vote account to delegate to
169    ///  8. '[]' Clock sysvar
170    ///  9. '[]' Rent sysvar
171    /// 10. `[]` Stake History sysvar
172    /// 11. `[]` Stake Config sysvar
173    /// 12. `[]` System program
174    /// 13. `[]` Stake program
175    ///  userdata: amount of lamports to increase on the given validator.
176    ///  The actual amount split into the transient stake account is:
177    ///  `lamports + stake_rent_exemption`
178    ///  The rent-exemption of the stake account is withdrawn back to the reserve
179    ///  after it is merged.
180    IncreaseValidatorStake {
181        /// amount of lamports to increase on the given validator
182        lamports: u64,
183        /// seed used to create transient stake account
184        transient_stake_seed: u64,
185    },
186
187    /// (Staker only) Set the preferred deposit or withdraw stake account for the
188    /// stake pool
189    ///
190    /// In order to avoid users abusing the stake pool as a free conversion
191    /// between SOL staked on different validators, the staker can force all
192    /// deposits and/or withdraws to go to one chosen account, or unset that account.
193    ///
194    /// 0. `[w]` Stake pool
195    /// 1. `[s]` Stake pool staker
196    /// 2. `[]` Validator list
197    ///
198    /// Fails if the validator is not part of the stake pool.
199    SetPreferredValidator {
200        /// Affected operation (deposit or withdraw)
201        validator_type: PreferredValidatorType,
202        /// Validator vote account that deposits or withdraws must go through,
203        /// unset with None
204        validator_vote_address: Option<Pubkey>,
205    },
206
207    ///  Updates balances of validator and transient stake accounts in the pool
208    ///
209    ///  While going through the pairs of validator and transient stake accounts,
210    ///  if the transient stake is inactive, it is merged into the reserve stake
211    ///  account. If the transient stake is active and has matching credits
212    ///  observed, it is merged into the canonical validator stake account. In
213    ///  all other states, nothing is done, and the balance is simply added to
214    ///  the canonical stake account balance.
215    ///
216    ///  0. `[]` Stake pool
217    ///  1. `[]` Stake pool withdraw authority
218    ///  2. `[w]` Validator stake list storage account
219    ///  3. `[w]` Reserve stake account
220    ///  4. `[]` Sysvar clock
221    ///  5. `[]` Sysvar stake history
222    ///  6. `[]` Stake program
223    ///  7. ..7+2N ` [] N pairs of validator and transient stake accounts
224    UpdateValidatorListBalance {
225        /// Index to start updating on the validator list
226        start_index: u32,
227        /// If true, don't try merging transient stake accounts into the reserve or
228        /// validator stake account.  Useful for testing or if a particular stake
229        /// account is in a bad state, but we still want to update
230        no_merge: bool,
231    },
232
233    ///   Updates total pool balance based on balances in the reserve and validator list
234    ///
235    ///   0. `[w]` Stake pool
236    ///   1. `[]` Stake pool withdraw authority
237    ///   2. `[w]` Validator stake list storage account
238    ///   3. `[]` Reserve stake account
239    ///   4. `[w]` Account to receive pool fee tokens
240    ///   5. `[w]` Pool mint account
241    ///   6. `[]` Pool token program
242    UpdateStakePoolBalance,
243
244    ///   Cleans up validator stake account entries marked as `ReadyForRemoval`
245    ///
246    ///   0. `[]` Stake pool
247    ///   1. `[w]` Validator stake list storage account
248    CleanupRemovedValidatorEntries,
249
250    ///   Deposit some stake into the pool.  The output is a "pool" token representing ownership
251    ///   into the pool. Inputs are converted to the current ratio.
252    ///
253    ///   0. `[w]` Stake pool
254    ///   1. `[w]` Validator stake list storage account
255    ///   2. `[s]/[]` Stake pool deposit authority
256    ///   3. `[]` Stake pool withdraw authority
257    ///   4. `[w]` Stake account to join the pool (withdraw authority for the stake account should be first set to the stake pool deposit authority)
258    ///   5. `[w]` Validator stake account for the stake account to be merged with
259    ///   6. `[w]` Reserve stake account, to withdraw rent exempt reserve
260    ///   7. `[w]` User account to receive pool tokens
261    ///   8. `[w]` Account to receive pool fee tokens
262    ///   9. `[w]` Account to receive a portion of pool fee tokens as referral fees
263    ///   10. `[w]` Pool token mint account
264    ///   11. '[]' Sysvar clock account
265    ///   12. '[]' Sysvar stake history account
266    ///   13. `[]` Pool token program id,
267    ///   14. `[]` Stake program id,
268    DepositStake,
269
270    ///   Withdraw the token from the pool at the current ratio.
271    ///
272    ///   Succeeds if the stake account has enough SOL to cover the desired amount
273    ///   of pool tokens, and if the withdrawal keeps the total staked amount
274    ///   above the minimum of rent-exempt amount +
275    ///   `max(crate::MINIMUM_ACTIVE_STAKE, solana_program::stake::tools::get_minimum_delegation())`.
276    ///
277    ///   When allowing withdrawals, the order of priority goes:
278    ///
279    ///   * preferred withdraw validator stake account (if set)
280    ///   * validator stake accounts
281    ///   * transient stake accounts
282    ///   * reserve stake account OR totally remove validator stake accounts
283    ///
284    ///   A user can freely withdraw from a validator stake account, and if they
285    ///   are all at the minimum, then they can withdraw from transient stake
286    ///   accounts, and if they are all at minimum, then they can withdraw from
287    ///   the reserve or remove any validator from the pool.
288    ///
289    ///   0. `[w]` Stake pool
290    ///   1. `[w]` Validator stake list storage account
291    ///   2. `[]` Stake pool withdraw authority
292    ///   3. `[w]` Validator or reserve stake account to split
293    ///   4. `[w]` Unitialized stake account to receive withdrawal
294    ///   5. `[]` User account to set as a new withdraw authority
295    ///   6. `[s]` User transfer authority, for pool token account
296    ///   7. `[w]` User account with pool tokens to burn from
297    ///   8. `[w]` Account to receive pool fee tokens
298    ///   9. `[w]` Pool token mint account
299    ///  10. `[]` Sysvar clock account (required)
300    ///  11. `[]` Pool token program id
301    ///  12. `[]` Stake program id,
302    ///  userdata: amount of pool tokens to withdraw
303    WithdrawStake(u64),
304
305    ///  (Manager only) Update manager
306    ///
307    ///  0. `[w]` StakePool
308    ///  1. `[s]` Manager
309    ///  2. `[s]` New manager
310    ///  3. `[]` New manager fee account
311    SetManager,
312
313    ///  (Manager only) Update fee
314    ///
315    ///  0. `[w]` StakePool
316    ///  1. `[s]` Manager
317    SetFee {
318        /// Type of fee to update and value to update it to
319        fee: FeeType,
320    },
321
322    ///  (Manager or staker only) Update staker
323    ///
324    ///  0. `[w]` StakePool
325    ///  1. `[s]` Manager or current staker
326    ///  2. '[]` New staker pubkey
327    SetStaker,
328
329    ///   Deposit SOL directly into the pool's reserve account. The output is a "pool" token
330    ///   representing ownership into the pool. Inputs are converted to the current ratio.
331    ///
332    ///   0. `[w]` Stake pool
333    ///   1. `[]` Stake pool withdraw authority
334    ///   2. `[w]` Reserve stake account, to deposit SOL
335    ///   3. `[s]` Account providing the lamports to be deposited into the pool
336    ///   4. `[w]` User account to receive pool tokens
337    ///   5. `[w]` Account to receive fee tokens
338    ///   6. `[w]` Account to receive a portion of fee as referral fees
339    ///   7. `[w]` Pool token mint account
340    ///   8. `[]` System program account
341    ///   9. `[]` Token program id
342    ///  10. `[s]` (Optional) Stake pool sol deposit authority.
343    DepositSol(u64),
344
345    ///  (Manager only) Update SOL deposit, stake deposit, or SOL withdrawal authority.
346    ///
347    ///  0. `[w]` StakePool
348    ///  1. `[s]` Manager
349    ///  2. '[]` New authority pubkey or none
350    SetFundingAuthority(FundingType),
351
352    ///   Withdraw SOL directly from the pool's reserve account. Fails if the
353    ///   reserve does not have enough SOL.
354    ///
355    ///   0. `[w]` Stake pool
356    ///   1. `[]` Stake pool withdraw authority
357    ///   2. `[s]` User transfer authority, for pool token account
358    ///   3. `[w]` User account to burn pool tokens
359    ///   4. `[w]` Reserve stake account, to withdraw SOL
360    ///   5. `[w]` Account receiving the lamports from the reserve, must be a system account
361    ///   6. `[w]` Account to receive pool fee tokens
362    ///   7. `[w]` Pool token mint account
363    ///   8. '[]' Clock sysvar
364    ///   9. '[]' Stake history sysvar
365    ///  10. `[]` Stake program account
366    ///  11. `[]` Token program id
367    ///  12. `[s]` (Optional) Stake pool sol withdraw authority
368    WithdrawSol(u64),
369
370    /// Create token metadata for the stake-pool token in the
371    /// metaplex-token program
372    /// 0. `[]` Stake pool
373    /// 1. `[s]` Manager
374    /// 2. `[]` Stake pool withdraw authority
375    /// 3. `[]` Pool token mint account
376    /// 4. `[s, w]` Payer for creation of token metadata account
377    /// 5. `[w]` Token metadata account
378    /// 6. `[]` Metadata program id
379    /// 7. `[]` System program id
380    CreateTokenMetadata {
381        /// Token name
382        name: String,
383        /// Token symbol e.g. stkSOL
384        symbol: String,
385        /// URI of the uploaded metadata of the solarti-token
386        uri: String,
387    },
388    /// Update token metadata for the stake-pool token in the
389    /// metaplex-token program
390    ///
391    /// 0. `[]` Stake pool
392    /// 1. `[s]` Manager
393    /// 2. `[]` Stake pool withdraw authority
394    /// 3. `[w]` Token metadata account
395    /// 4. `[]` Metadata program id
396    UpdateTokenMetadata {
397        /// Token name
398        name: String,
399        /// Token symbol e.g. stkSOL
400        symbol: String,
401        /// URI of the uploaded metadata of the solarti-token
402        uri: String,
403    },
404
405    /// (Staker only) Increase stake on a validator again in an epoch.
406    ///
407    /// Works regardless if the transient stake account exists.
408    ///
409    /// Internally, this instruction splits reserve stake into an ephemeral stake
410    /// account, activates it, then merges or splits it into the transient stake
411    /// account delegated to the appropriate validator. `UpdateValidatorListBalance`
412    /// will do the work of merging once it's ready.
413    ///
414    /// The minimum amount to move is rent-exemption plus
415    /// `max(crate::MINIMUM_ACTIVE_STAKE, solana_program::stake::tools::get_minimum_delegation())`.
416    ///
417    ///  0. `[]` Stake pool
418    ///  1. `[s]` Stake pool staker
419    ///  2. `[]` Stake pool withdraw authority
420    ///  3. `[w]` Validator list
421    ///  4. `[w]` Stake pool reserve stake
422    ///  5. `[w]` Uninitialized ephemeral stake account to receive stake
423    ///  6. `[w]` Transient stake account
424    ///  7. `[]` Validator stake account
425    ///  8. `[]` Validator vote account to delegate to
426    ///  9. '[]' Clock sysvar
427    /// 10. `[]` Stake History sysvar
428    /// 11. `[]` Stake Config sysvar
429    /// 12. `[]` System program
430    /// 13. `[]` Stake program
431    ///  userdata: amount of lamports to increase on the given validator.
432    ///  The actual amount split into the transient stake account is:
433    ///  `lamports + stake_rent_exemption`
434    ///  The rent-exemption of the stake account is withdrawn back to the reserve
435    ///  after it is merged.
436    IncreaseAdditionalValidatorStake {
437        /// amount of lamports to increase on the given validator
438        lamports: u64,
439        /// seed used to create transient stake account
440        transient_stake_seed: u64,
441        /// seed used to create ephemeral account.
442        ephemeral_stake_seed: u64,
443    },
444
445    /// (Staker only) Decrease active stake again from a validator, eventually moving it to the reserve
446    ///
447    /// Works regardless if the transient stake account already exists.
448    ///
449    /// Internally, this instruction splits a validator stake account into an
450    /// ephemeral stake account, deactivates it, then merges or splits it into
451    /// the transient stake account delegated to the appropriate validator.
452    ///
453    ///  The amount of lamports to move must be at least rent-exemption plus
454    /// `max(crate::MINIMUM_ACTIVE_STAKE, solana_program::stake::tools::get_minimum_delegation())`.
455    ///
456    ///  0. `[]` Stake pool
457    ///  1. `[s]` Stake pool staker
458    ///  2. `[]` Stake pool withdraw authority
459    ///  3. `[w]` Validator list
460    ///  4. `[w]` Canonical stake account to split from
461    ///  5. `[w]` Uninitialized ephemeral stake account to receive stake
462    ///  6. `[w]` Transient stake account
463    ///  7. `[]` Clock sysvar
464    ///  8. '[]' Stake history sysvar
465    ///  9. `[]` System program
466    /// 10. `[]` Stake program
467    DecreaseAdditionalValidatorStake {
468        /// amount of lamports to split into the transient stake account
469        lamports: u64,
470        /// seed used to create transient stake account
471        transient_stake_seed: u64,
472        /// seed used to create ephemeral account.
473        ephemeral_stake_seed: u64,
474    },
475
476    /// (Staker only) Redelegate active stake on a validator, eventually moving it to another
477    ///
478    /// Internally, this instruction splits a validator stake account into its
479    /// corresponding transient stake account, redelegates it to an ephemeral stake
480    /// account, then merges that stake into the destination transient stake account.
481    ///
482    /// In order to rebalance the pool without taking custody, the staker needs
483    /// a way of reducing the stake on a stake account. This instruction splits
484    /// some amount of stake, up to the total activated stake, from the canonical
485    /// validator stake account, into its "transient" stake account.
486    ///
487    /// The instruction only succeeds if the source transient stake account and
488    /// ephemeral stake account do not exist.
489    ///
490    /// The amount of lamports to move must be at least twice rent-exemption
491    /// plus the minimum delegation amount. Rent-exemption is required for the
492    /// source transient stake account, and rent-exemption plus minimum delegation
493    /// is required for the destination ephemeral stake account.
494    ///
495    /// The amount that arrives at the destination validator in the end is
496    /// `redelegate_lamports - 2 * rent_exemption` if the destination transient
497    /// account does *not* exist, and `redelegate_lamports - rent_exemption` if
498    /// the destination transient account already exists. One `rent_exemption`
499    /// is deactivated with the source transient account during redelegation,
500    /// and another `rent_exemption` is deactivated when creating the destination
501    /// transient stake account.
502    ///
503    ///  0. `[]` Stake pool
504    ///  1. `[s]` Stake pool staker
505    ///  2. `[]` Stake pool withdraw authority
506    ///  3. `[w]` Validator list
507    ///  4. `[w]` Source canonical stake account to split from
508    ///  5. `[w]` Source transient stake account to receive split and be redelegated
509    ///  6. `[w]` Uninitialized ephemeral stake account to receive redelegation
510    ///  7. `[w]` Destination transient stake account to receive ephemeral stake by merge
511    ///  8. `[]` Destination stake account to receive transient stake after activation
512    ///  9. `[]` Destination validator vote account
513    /// 10. `[]` Clock sysvar
514    /// 11. `[]` Stake History sysvar
515    /// 12. `[]` Stake Config sysvar
516    /// 13. `[]` System program
517    /// 14. `[]` Stake program
518    Redelegate {
519        /// Amount of lamports to redelegate
520        #[allow(dead_code)] // but it's not
521        lamports: u64,
522        /// Seed used to create source transient stake account
523        #[allow(dead_code)] // but it's not
524        source_transient_stake_seed: u64,
525        /// Seed used to create destination ephemeral account.
526        #[allow(dead_code)] // but it's not
527        ephemeral_stake_seed: u64,
528        /// Seed used to create destination transient stake account. If there is
529        /// already transient stake, this must match the current seed, otherwise
530        /// it can be anything
531        #[allow(dead_code)] // but it's not
532        destination_transient_stake_seed: u64,
533    },
534
535    ///   Deposit some stake into the pool, with a specified slippage constraint.
536    ///   The output is a "pool" token representing ownership into the pool.
537    ///   Inputs are converted at the current ratio.
538    ///
539    ///   0. `[w]` Stake pool
540    ///   1. `[w]` Validator stake list storage account
541    ///   2. `[s]/[]` Stake pool deposit authority
542    ///   3. `[]` Stake pool withdraw authority
543    ///   4. `[w]` Stake account to join the pool (withdraw authority for the stake account should be first set to the stake pool deposit authority)
544    ///   5. `[w]` Validator stake account for the stake account to be merged with
545    ///   6. `[w]` Reserve stake account, to withdraw rent exempt reserve
546    ///   7. `[w]` User account to receive pool tokens
547    ///   8. `[w]` Account to receive pool fee tokens
548    ///   9. `[w]` Account to receive a portion of pool fee tokens as referral fees
549    ///   10. `[w]` Pool token mint account
550    ///   11. '[]' Sysvar clock account
551    ///   12. '[]' Sysvar stake history account
552    ///   13. `[]` Pool token program id,
553    ///   14. `[]` Stake program id,
554    DepositStakeWithSlippage {
555        /// Minimum amount of pool tokens that must be received
556        minimum_pool_tokens_out: u64,
557    },
558
559    ///   Withdraw the token from the pool at the current ratio, specifying a
560    ///   minimum expected output lamport amount.
561    ///
562    ///   Succeeds if the stake account has enough SOL to cover the desired amount
563    ///   of pool tokens, and if the withdrawal keeps the total staked amount
564    ///   above the minimum of rent-exempt amount +
565    ///   `max(crate::MINIMUM_ACTIVE_STAKE, solana_program::stake::tools::get_minimum_delegation())`.
566    ///
567    ///   0. `[w]` Stake pool
568    ///   1. `[w]` Validator stake list storage account
569    ///   2. `[]` Stake pool withdraw authority
570    ///   3. `[w]` Validator or reserve stake account to split
571    ///   4. `[w]` Unitialized stake account to receive withdrawal
572    ///   5. `[]` User account to set as a new withdraw authority
573    ///   6. `[s]` User transfer authority, for pool token account
574    ///   7. `[w]` User account with pool tokens to burn from
575    ///   8. `[w]` Account to receive pool fee tokens
576    ///   9. `[w]` Pool token mint account
577    ///  10. `[]` Sysvar clock account (required)
578    ///  11. `[]` Pool token program id
579    ///  12. `[]` Stake program id,
580    ///  userdata: amount of pool tokens to withdraw
581    WithdrawStakeWithSlippage {
582        /// Pool tokens to burn in exchange for lamports
583        pool_tokens_in: u64,
584        /// Minimum amount of lamports that must be received
585        minimum_lamports_out: u64,
586    },
587
588    ///   Deposit SOL directly into the pool's reserve account, with a specified
589    ///   slippage constraint. The output is a "pool" token representing ownership
590    ///   into the pool. Inputs are converted at the current ratio.
591    ///
592    ///   0. `[w]` Stake pool
593    ///   1. `[]` Stake pool withdraw authority
594    ///   2. `[w]` Reserve stake account, to deposit SOL
595    ///   3. `[s]` Account providing the lamports to be deposited into the pool
596    ///   4. `[w]` User account to receive pool tokens
597    ///   5. `[w]` Account to receive fee tokens
598    ///   6. `[w]` Account to receive a portion of fee as referral fees
599    ///   7. `[w]` Pool token mint account
600    ///   8. `[]` System program account
601    ///   9. `[]` Token program id
602    ///  10. `[s]` (Optional) Stake pool sol deposit authority.
603    DepositSolWithSlippage {
604        /// Amount of lamports to deposit into the reserve
605        lamports_in: u64,
606        /// Minimum amount of pool tokens that must be received
607        minimum_pool_tokens_out: u64,
608    },
609
610    ///   Withdraw SOL directly from the pool's reserve account. Fails if the
611    ///   reserve does not have enough SOL or if the slippage constraint is not
612    ///   met.
613    ///
614    ///   0. `[w]` Stake pool
615    ///   1. `[]` Stake pool withdraw authority
616    ///   2. `[s]` User transfer authority, for pool token account
617    ///   3. `[w]` User account to burn pool tokens
618    ///   4. `[w]` Reserve stake account, to withdraw SOL
619    ///   5. `[w]` Account receiving the lamports from the reserve, must be a system account
620    ///   6. `[w]` Account to receive pool fee tokens
621    ///   7. `[w]` Pool token mint account
622    ///   8. '[]' Clock sysvar
623    ///   9. '[]' Stake history sysvar
624    ///  10. `[]` Stake program account
625    ///  11. `[]` Token program id
626    ///  12. `[s]` (Optional) Stake pool sol withdraw authority
627    WithdrawSolWithSlippage {
628        /// Pool tokens to burn in exchange for lamports
629        pool_tokens_in: u64,
630        /// Minimum amount of lamports that must be received
631        minimum_lamports_out: u64,
632    },
633}
634
635/// Creates an 'initialize' instruction.
636pub fn initialize(
637    program_id: &Pubkey,
638    stake_pool: &Pubkey,
639    manager: &Pubkey,
640    staker: &Pubkey,
641    stake_pool_withdraw_authority: &Pubkey,
642    validator_list: &Pubkey,
643    reserve_stake: &Pubkey,
644    pool_mint: &Pubkey,
645    manager_pool_account: &Pubkey,
646    token_program_id: &Pubkey,
647    deposit_authority: Option<Pubkey>,
648    fee: Fee,
649    withdrawal_fee: Fee,
650    deposit_fee: Fee,
651    referral_fee: u8,
652    max_validators: u32,
653) -> Instruction {
654    let init_data = StakePoolInstruction::Initialize {
655        fee,
656        withdrawal_fee,
657        deposit_fee,
658        referral_fee,
659        max_validators,
660    };
661    let data = init_data.try_to_vec().unwrap();
662    let mut accounts = vec![
663        AccountMeta::new(*stake_pool, false),
664        AccountMeta::new_readonly(*manager, true),
665        AccountMeta::new_readonly(*staker, false),
666        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
667        AccountMeta::new(*validator_list, false),
668        AccountMeta::new_readonly(*reserve_stake, false),
669        AccountMeta::new(*pool_mint, false),
670        AccountMeta::new(*manager_pool_account, false),
671        AccountMeta::new_readonly(*token_program_id, false),
672    ];
673    if let Some(deposit_authority) = deposit_authority {
674        accounts.push(AccountMeta::new_readonly(deposit_authority, true));
675    }
676    Instruction {
677        program_id: *program_id,
678        accounts,
679        data,
680    }
681}
682
683/// Creates `AddValidatorToPool` instruction (add new validator stake account to the pool)
684pub fn add_validator_to_pool(
685    program_id: &Pubkey,
686    stake_pool: &Pubkey,
687    staker: &Pubkey,
688    reserve: &Pubkey,
689    stake_pool_withdraw: &Pubkey,
690    validator_list: &Pubkey,
691    stake: &Pubkey,
692    validator: &Pubkey,
693    seed: Option<NonZeroU32>,
694) -> Instruction {
695    let accounts = vec![
696        AccountMeta::new(*stake_pool, false),
697        AccountMeta::new_readonly(*staker, true),
698        AccountMeta::new(*reserve, false),
699        AccountMeta::new_readonly(*stake_pool_withdraw, false),
700        AccountMeta::new(*validator_list, false),
701        AccountMeta::new(*stake, false),
702        AccountMeta::new_readonly(*validator, false),
703        AccountMeta::new_readonly(sysvar::rent::id(), false),
704        AccountMeta::new_readonly(sysvar::clock::id(), false),
705        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
706        AccountMeta::new_readonly(stake::config::id(), false),
707        AccountMeta::new_readonly(system_program::id(), false),
708        AccountMeta::new_readonly(stake::program::id(), false),
709    ];
710    let data = StakePoolInstruction::AddValidatorToPool(seed.map(|s| s.get()).unwrap_or(0))
711        .try_to_vec()
712        .unwrap();
713    Instruction {
714        program_id: *program_id,
715        accounts,
716        data,
717    }
718}
719
720/// Creates `RemoveValidatorFromPool` instruction (remove validator stake account from the pool)
721pub fn remove_validator_from_pool(
722    program_id: &Pubkey,
723    stake_pool: &Pubkey,
724    staker: &Pubkey,
725    stake_pool_withdraw: &Pubkey,
726    validator_list: &Pubkey,
727    stake_account: &Pubkey,
728    transient_stake_account: &Pubkey,
729) -> Instruction {
730    let accounts = vec![
731        AccountMeta::new(*stake_pool, false),
732        AccountMeta::new_readonly(*staker, true),
733        AccountMeta::new_readonly(*stake_pool_withdraw, false),
734        AccountMeta::new(*validator_list, false),
735        AccountMeta::new(*stake_account, false),
736        AccountMeta::new_readonly(*transient_stake_account, false),
737        AccountMeta::new_readonly(sysvar::clock::id(), false),
738        AccountMeta::new_readonly(stake::program::id(), false),
739    ];
740    Instruction {
741        program_id: *program_id,
742        accounts,
743        data: StakePoolInstruction::RemoveValidatorFromPool
744            .try_to_vec()
745            .unwrap(),
746    }
747}
748
749/// Creates `DecreaseValidatorStake` instruction (rebalance from validator account to
750/// transient account)
751pub fn decrease_validator_stake(
752    program_id: &Pubkey,
753    stake_pool: &Pubkey,
754    staker: &Pubkey,
755    stake_pool_withdraw_authority: &Pubkey,
756    validator_list: &Pubkey,
757    validator_stake: &Pubkey,
758    transient_stake: &Pubkey,
759    lamports: u64,
760    transient_stake_seed: u64,
761) -> Instruction {
762    let accounts = vec![
763        AccountMeta::new_readonly(*stake_pool, false),
764        AccountMeta::new_readonly(*staker, true),
765        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
766        AccountMeta::new(*validator_list, false),
767        AccountMeta::new(*validator_stake, false),
768        AccountMeta::new(*transient_stake, false),
769        AccountMeta::new_readonly(sysvar::clock::id(), false),
770        AccountMeta::new_readonly(sysvar::rent::id(), false),
771        AccountMeta::new_readonly(system_program::id(), false),
772        AccountMeta::new_readonly(stake::program::id(), false),
773    ];
774    Instruction {
775        program_id: *program_id,
776        accounts,
777        data: StakePoolInstruction::DecreaseValidatorStake {
778            lamports,
779            transient_stake_seed,
780        }
781        .try_to_vec()
782        .unwrap(),
783    }
784}
785
786/// Creates `DecreaseAdditionalValidatorStake` instruction (rebalance from
787/// validator account to transient account)
788pub fn decrease_additional_validator_stake(
789    program_id: &Pubkey,
790    stake_pool: &Pubkey,
791    staker: &Pubkey,
792    stake_pool_withdraw_authority: &Pubkey,
793    validator_list: &Pubkey,
794    validator_stake: &Pubkey,
795    ephemeral_stake: &Pubkey,
796    transient_stake: &Pubkey,
797    lamports: u64,
798    transient_stake_seed: u64,
799    ephemeral_stake_seed: u64,
800) -> Instruction {
801    let accounts = vec![
802        AccountMeta::new_readonly(*stake_pool, false),
803        AccountMeta::new_readonly(*staker, true),
804        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
805        AccountMeta::new(*validator_list, false),
806        AccountMeta::new(*validator_stake, false),
807        AccountMeta::new(*ephemeral_stake, false),
808        AccountMeta::new(*transient_stake, false),
809        AccountMeta::new_readonly(sysvar::clock::id(), false),
810        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
811        AccountMeta::new_readonly(system_program::id(), false),
812        AccountMeta::new_readonly(stake::program::id(), false),
813    ];
814    Instruction {
815        program_id: *program_id,
816        accounts,
817        data: StakePoolInstruction::DecreaseAdditionalValidatorStake {
818            lamports,
819            transient_stake_seed,
820            ephemeral_stake_seed,
821        }
822        .try_to_vec()
823        .unwrap(),
824    }
825}
826
827/// Creates `IncreaseValidatorStake` instruction (rebalance from reserve account to
828/// transient account)
829pub fn increase_validator_stake(
830    program_id: &Pubkey,
831    stake_pool: &Pubkey,
832    staker: &Pubkey,
833    stake_pool_withdraw_authority: &Pubkey,
834    validator_list: &Pubkey,
835    reserve_stake: &Pubkey,
836    transient_stake: &Pubkey,
837    validator_stake: &Pubkey,
838    validator: &Pubkey,
839    lamports: u64,
840    transient_stake_seed: u64,
841) -> Instruction {
842    let accounts = vec![
843        AccountMeta::new_readonly(*stake_pool, false),
844        AccountMeta::new_readonly(*staker, true),
845        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
846        AccountMeta::new(*validator_list, false),
847        AccountMeta::new(*reserve_stake, false),
848        AccountMeta::new(*transient_stake, false),
849        AccountMeta::new_readonly(*validator_stake, false),
850        AccountMeta::new_readonly(*validator, false),
851        AccountMeta::new_readonly(sysvar::clock::id(), false),
852        AccountMeta::new_readonly(sysvar::rent::id(), false),
853        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
854        AccountMeta::new_readonly(stake::config::id(), false),
855        AccountMeta::new_readonly(system_program::id(), false),
856        AccountMeta::new_readonly(stake::program::id(), false),
857    ];
858    Instruction {
859        program_id: *program_id,
860        accounts,
861        data: StakePoolInstruction::IncreaseValidatorStake {
862            lamports,
863            transient_stake_seed,
864        }
865        .try_to_vec()
866        .unwrap(),
867    }
868}
869
870/// Creates `IncreaseAdditionalValidatorStake` instruction (rebalance from reserve account to
871/// transient account)
872pub fn increase_additional_validator_stake(
873    program_id: &Pubkey,
874    stake_pool: &Pubkey,
875    staker: &Pubkey,
876    stake_pool_withdraw_authority: &Pubkey,
877    validator_list: &Pubkey,
878    reserve_stake: &Pubkey,
879    ephemeral_stake: &Pubkey,
880    transient_stake: &Pubkey,
881    validator_stake: &Pubkey,
882    validator: &Pubkey,
883    lamports: u64,
884    transient_stake_seed: u64,
885    ephemeral_stake_seed: u64,
886) -> Instruction {
887    let accounts = vec![
888        AccountMeta::new_readonly(*stake_pool, false),
889        AccountMeta::new_readonly(*staker, true),
890        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
891        AccountMeta::new(*validator_list, false),
892        AccountMeta::new(*reserve_stake, false),
893        AccountMeta::new(*ephemeral_stake, false),
894        AccountMeta::new(*transient_stake, false),
895        AccountMeta::new_readonly(*validator_stake, false),
896        AccountMeta::new_readonly(*validator, false),
897        AccountMeta::new_readonly(sysvar::clock::id(), false),
898        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
899        AccountMeta::new_readonly(stake::config::id(), false),
900        AccountMeta::new_readonly(system_program::id(), false),
901        AccountMeta::new_readonly(stake::program::id(), false),
902    ];
903    Instruction {
904        program_id: *program_id,
905        accounts,
906        data: StakePoolInstruction::IncreaseAdditionalValidatorStake {
907            lamports,
908            transient_stake_seed,
909            ephemeral_stake_seed,
910        }
911        .try_to_vec()
912        .unwrap(),
913    }
914}
915
916/// Creates `Redelegate` instruction (rebalance from one validator account to another)
917pub fn redelegate(
918    program_id: &Pubkey,
919    stake_pool: &Pubkey,
920    staker: &Pubkey,
921    stake_pool_withdraw_authority: &Pubkey,
922    validator_list: &Pubkey,
923    source_validator_stake: &Pubkey,
924    source_transient_stake: &Pubkey,
925    ephemeral_stake: &Pubkey,
926    destination_transient_stake: &Pubkey,
927    destination_validator_stake: &Pubkey,
928    validator: &Pubkey,
929    lamports: u64,
930    source_transient_stake_seed: u64,
931    ephemeral_stake_seed: u64,
932    destination_transient_stake_seed: u64,
933) -> Instruction {
934    let accounts = vec![
935        AccountMeta::new_readonly(*stake_pool, false),
936        AccountMeta::new_readonly(*staker, true),
937        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
938        AccountMeta::new(*validator_list, false),
939        AccountMeta::new(*source_validator_stake, false),
940        AccountMeta::new(*source_transient_stake, false),
941        AccountMeta::new(*ephemeral_stake, false),
942        AccountMeta::new(*destination_transient_stake, false),
943        AccountMeta::new_readonly(*destination_validator_stake, false),
944        AccountMeta::new_readonly(*validator, false),
945        AccountMeta::new_readonly(sysvar::clock::id(), false),
946        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
947        AccountMeta::new_readonly(stake::config::id(), false),
948        AccountMeta::new_readonly(system_program::id(), false),
949        AccountMeta::new_readonly(stake::program::id(), false),
950    ];
951    Instruction {
952        program_id: *program_id,
953        accounts,
954        data: StakePoolInstruction::Redelegate {
955            lamports,
956            source_transient_stake_seed,
957            ephemeral_stake_seed,
958            destination_transient_stake_seed,
959        }
960        .try_to_vec()
961        .unwrap(),
962    }
963}
964
965/// Creates `SetPreferredDepositValidator` instruction
966pub fn set_preferred_validator(
967    program_id: &Pubkey,
968    stake_pool_address: &Pubkey,
969    staker: &Pubkey,
970    validator_list_address: &Pubkey,
971    validator_type: PreferredValidatorType,
972    validator_vote_address: Option<Pubkey>,
973) -> Instruction {
974    Instruction {
975        program_id: *program_id,
976        accounts: vec![
977            AccountMeta::new(*stake_pool_address, false),
978            AccountMeta::new_readonly(*staker, true),
979            AccountMeta::new_readonly(*validator_list_address, false),
980        ],
981        data: StakePoolInstruction::SetPreferredValidator {
982            validator_type,
983            validator_vote_address,
984        }
985        .try_to_vec()
986        .unwrap(),
987    }
988}
989
990/// Create an `AddValidatorToPool` instruction given an existing stake pool and
991/// vote account
992pub fn add_validator_to_pool_with_vote(
993    program_id: &Pubkey,
994    stake_pool: &StakePool,
995    stake_pool_address: &Pubkey,
996    vote_account_address: &Pubkey,
997    seed: Option<NonZeroU32>,
998) -> Instruction {
999    let pool_withdraw_authority =
1000        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1001    let (stake_account_address, _) =
1002        find_stake_program_address(program_id, vote_account_address, stake_pool_address, seed);
1003    add_validator_to_pool(
1004        program_id,
1005        stake_pool_address,
1006        &stake_pool.staker,
1007        &stake_pool.reserve_stake,
1008        &pool_withdraw_authority,
1009        &stake_pool.validator_list,
1010        &stake_account_address,
1011        vote_account_address,
1012        seed,
1013    )
1014}
1015
1016/// Create an `RemoveValidatorFromPool` instruction given an existing stake pool and
1017/// vote account
1018pub fn remove_validator_from_pool_with_vote(
1019    program_id: &Pubkey,
1020    stake_pool: &StakePool,
1021    stake_pool_address: &Pubkey,
1022    vote_account_address: &Pubkey,
1023    validator_stake_seed: Option<NonZeroU32>,
1024    transient_stake_seed: u64,
1025) -> Instruction {
1026    let pool_withdraw_authority =
1027        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1028    let (stake_account_address, _) = find_stake_program_address(
1029        program_id,
1030        vote_account_address,
1031        stake_pool_address,
1032        validator_stake_seed,
1033    );
1034    let (transient_stake_account, _) = find_transient_stake_program_address(
1035        program_id,
1036        vote_account_address,
1037        stake_pool_address,
1038        transient_stake_seed,
1039    );
1040    remove_validator_from_pool(
1041        program_id,
1042        stake_pool_address,
1043        &stake_pool.staker,
1044        &pool_withdraw_authority,
1045        &stake_pool.validator_list,
1046        &stake_account_address,
1047        &transient_stake_account,
1048    )
1049}
1050
1051/// Create an `IncreaseValidatorStake` instruction given an existing stake pool and
1052/// vote account
1053pub fn increase_validator_stake_with_vote(
1054    program_id: &Pubkey,
1055    stake_pool: &StakePool,
1056    stake_pool_address: &Pubkey,
1057    vote_account_address: &Pubkey,
1058    lamports: u64,
1059    validator_stake_seed: Option<NonZeroU32>,
1060    transient_stake_seed: u64,
1061) -> Instruction {
1062    let pool_withdraw_authority =
1063        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1064    let (transient_stake_address, _) = find_transient_stake_program_address(
1065        program_id,
1066        vote_account_address,
1067        stake_pool_address,
1068        transient_stake_seed,
1069    );
1070    let (validator_stake_address, _) = find_stake_program_address(
1071        program_id,
1072        vote_account_address,
1073        stake_pool_address,
1074        validator_stake_seed,
1075    );
1076
1077    increase_validator_stake(
1078        program_id,
1079        stake_pool_address,
1080        &stake_pool.staker,
1081        &pool_withdraw_authority,
1082        &stake_pool.validator_list,
1083        &stake_pool.reserve_stake,
1084        &transient_stake_address,
1085        &validator_stake_address,
1086        vote_account_address,
1087        lamports,
1088        transient_stake_seed,
1089    )
1090}
1091
1092/// Create an `IncreaseAdditionalValidatorStake` instruction given an existing
1093/// stake pool and vote account
1094pub fn increase_additional_validator_stake_with_vote(
1095    program_id: &Pubkey,
1096    stake_pool: &StakePool,
1097    stake_pool_address: &Pubkey,
1098    vote_account_address: &Pubkey,
1099    lamports: u64,
1100    validator_stake_seed: Option<NonZeroU32>,
1101    transient_stake_seed: u64,
1102    ephemeral_stake_seed: u64,
1103) -> Instruction {
1104    let pool_withdraw_authority =
1105        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1106    let (ephemeral_stake_address, _) =
1107        find_ephemeral_stake_program_address(program_id, stake_pool_address, ephemeral_stake_seed);
1108    let (transient_stake_address, _) = find_transient_stake_program_address(
1109        program_id,
1110        vote_account_address,
1111        stake_pool_address,
1112        transient_stake_seed,
1113    );
1114    let (validator_stake_address, _) = find_stake_program_address(
1115        program_id,
1116        vote_account_address,
1117        stake_pool_address,
1118        validator_stake_seed,
1119    );
1120
1121    increase_additional_validator_stake(
1122        program_id,
1123        stake_pool_address,
1124        &stake_pool.staker,
1125        &pool_withdraw_authority,
1126        &stake_pool.validator_list,
1127        &stake_pool.reserve_stake,
1128        &ephemeral_stake_address,
1129        &transient_stake_address,
1130        &validator_stake_address,
1131        vote_account_address,
1132        lamports,
1133        transient_stake_seed,
1134        ephemeral_stake_seed,
1135    )
1136}
1137
1138/// Create a `DecreaseValidatorStake` instruction given an existing stake pool and
1139/// vote account
1140pub fn decrease_validator_stake_with_vote(
1141    program_id: &Pubkey,
1142    stake_pool: &StakePool,
1143    stake_pool_address: &Pubkey,
1144    vote_account_address: &Pubkey,
1145    lamports: u64,
1146    validator_stake_seed: Option<NonZeroU32>,
1147    transient_stake_seed: u64,
1148) -> Instruction {
1149    let pool_withdraw_authority =
1150        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1151    let (validator_stake_address, _) = find_stake_program_address(
1152        program_id,
1153        vote_account_address,
1154        stake_pool_address,
1155        validator_stake_seed,
1156    );
1157    let (transient_stake_address, _) = find_transient_stake_program_address(
1158        program_id,
1159        vote_account_address,
1160        stake_pool_address,
1161        transient_stake_seed,
1162    );
1163    decrease_validator_stake(
1164        program_id,
1165        stake_pool_address,
1166        &stake_pool.staker,
1167        &pool_withdraw_authority,
1168        &stake_pool.validator_list,
1169        &validator_stake_address,
1170        &transient_stake_address,
1171        lamports,
1172        transient_stake_seed,
1173    )
1174}
1175
1176/// Create a `DecreaseAdditionalValidatorStake` instruction given an existing
1177/// stake pool and vote account
1178pub fn decrease_additional_validator_stake_with_vote(
1179    program_id: &Pubkey,
1180    stake_pool: &StakePool,
1181    stake_pool_address: &Pubkey,
1182    vote_account_address: &Pubkey,
1183    lamports: u64,
1184    validator_stake_seed: Option<NonZeroU32>,
1185    transient_stake_seed: u64,
1186    ephemeral_stake_seed: u64,
1187) -> Instruction {
1188    let pool_withdraw_authority =
1189        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1190    let (validator_stake_address, _) = find_stake_program_address(
1191        program_id,
1192        vote_account_address,
1193        stake_pool_address,
1194        validator_stake_seed,
1195    );
1196    let (ephemeral_stake_address, _) =
1197        find_ephemeral_stake_program_address(program_id, stake_pool_address, ephemeral_stake_seed);
1198    let (transient_stake_address, _) = find_transient_stake_program_address(
1199        program_id,
1200        vote_account_address,
1201        stake_pool_address,
1202        transient_stake_seed,
1203    );
1204    decrease_additional_validator_stake(
1205        program_id,
1206        stake_pool_address,
1207        &stake_pool.staker,
1208        &pool_withdraw_authority,
1209        &stake_pool.validator_list,
1210        &validator_stake_address,
1211        &ephemeral_stake_address,
1212        &transient_stake_address,
1213        lamports,
1214        transient_stake_seed,
1215        ephemeral_stake_seed,
1216    )
1217}
1218
1219/// Creates `UpdateValidatorListBalance` instruction (update validator stake account balances)
1220pub fn update_validator_list_balance(
1221    program_id: &Pubkey,
1222    stake_pool: &Pubkey,
1223    stake_pool_withdraw_authority: &Pubkey,
1224    validator_list_address: &Pubkey,
1225    reserve_stake: &Pubkey,
1226    validator_list: &ValidatorList,
1227    validator_vote_accounts: &[Pubkey],
1228    start_index: u32,
1229    no_merge: bool,
1230) -> Instruction {
1231    let mut accounts = vec![
1232        AccountMeta::new_readonly(*stake_pool, false),
1233        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
1234        AccountMeta::new(*validator_list_address, false),
1235        AccountMeta::new(*reserve_stake, false),
1236        AccountMeta::new_readonly(sysvar::clock::id(), false),
1237        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
1238        AccountMeta::new_readonly(stake::program::id(), false),
1239    ];
1240    accounts.append(
1241        &mut validator_vote_accounts
1242            .iter()
1243            .flat_map(|vote_account_address| {
1244                let validator_stake_info = validator_list.find(vote_account_address);
1245                if let Some(validator_stake_info) = validator_stake_info {
1246                    let (validator_stake_account, _) = find_stake_program_address(
1247                        program_id,
1248                        vote_account_address,
1249                        stake_pool,
1250                        NonZeroU32::new(validator_stake_info.validator_seed_suffix),
1251                    );
1252                    let (transient_stake_account, _) = find_transient_stake_program_address(
1253                        program_id,
1254                        vote_account_address,
1255                        stake_pool,
1256                        validator_stake_info.transient_seed_suffix,
1257                    );
1258                    vec![
1259                        AccountMeta::new(validator_stake_account, false),
1260                        AccountMeta::new(transient_stake_account, false),
1261                    ]
1262                } else {
1263                    vec![]
1264                }
1265            })
1266            .collect::<Vec<AccountMeta>>(),
1267    );
1268    Instruction {
1269        program_id: *program_id,
1270        accounts,
1271        data: StakePoolInstruction::UpdateValidatorListBalance {
1272            start_index,
1273            no_merge,
1274        }
1275        .try_to_vec()
1276        .unwrap(),
1277    }
1278}
1279
1280/// Creates `UpdateStakePoolBalance` instruction (pool balance from the stake account list balances)
1281pub fn update_stake_pool_balance(
1282    program_id: &Pubkey,
1283    stake_pool: &Pubkey,
1284    withdraw_authority: &Pubkey,
1285    validator_list_storage: &Pubkey,
1286    reserve_stake: &Pubkey,
1287    manager_fee_account: &Pubkey,
1288    stake_pool_mint: &Pubkey,
1289    token_program_id: &Pubkey,
1290) -> Instruction {
1291    let accounts = vec![
1292        AccountMeta::new(*stake_pool, false),
1293        AccountMeta::new_readonly(*withdraw_authority, false),
1294        AccountMeta::new(*validator_list_storage, false),
1295        AccountMeta::new_readonly(*reserve_stake, false),
1296        AccountMeta::new(*manager_fee_account, false),
1297        AccountMeta::new(*stake_pool_mint, false),
1298        AccountMeta::new_readonly(*token_program_id, false),
1299    ];
1300    Instruction {
1301        program_id: *program_id,
1302        accounts,
1303        data: StakePoolInstruction::UpdateStakePoolBalance
1304            .try_to_vec()
1305            .unwrap(),
1306    }
1307}
1308
1309/// Creates `CleanupRemovedValidatorEntries` instruction (removes entries from the validator list)
1310pub fn cleanup_removed_validator_entries(
1311    program_id: &Pubkey,
1312    stake_pool: &Pubkey,
1313    validator_list_storage: &Pubkey,
1314) -> Instruction {
1315    let accounts = vec![
1316        AccountMeta::new_readonly(*stake_pool, false),
1317        AccountMeta::new(*validator_list_storage, false),
1318    ];
1319    Instruction {
1320        program_id: *program_id,
1321        accounts,
1322        data: StakePoolInstruction::CleanupRemovedValidatorEntries
1323            .try_to_vec()
1324            .unwrap(),
1325    }
1326}
1327
1328/// Creates all `UpdateValidatorListBalance` and `UpdateStakePoolBalance`
1329/// instructions for fully updating a stake pool each epoch
1330pub fn update_stake_pool(
1331    program_id: &Pubkey,
1332    stake_pool: &StakePool,
1333    validator_list: &ValidatorList,
1334    stake_pool_address: &Pubkey,
1335    no_merge: bool,
1336) -> (Vec<Instruction>, Vec<Instruction>) {
1337    let vote_accounts: Vec<Pubkey> = validator_list
1338        .validators
1339        .iter()
1340        .map(|item| item.vote_account_address)
1341        .collect();
1342
1343    let (withdraw_authority, _) =
1344        find_withdraw_authority_program_address(program_id, stake_pool_address);
1345
1346    let mut update_list_instructions: Vec<Instruction> = vec![];
1347    let mut start_index = 0;
1348    for accounts_chunk in vote_accounts.chunks(MAX_VALIDATORS_TO_UPDATE) {
1349        update_list_instructions.push(update_validator_list_balance(
1350            program_id,
1351            stake_pool_address,
1352            &withdraw_authority,
1353            &stake_pool.validator_list,
1354            &stake_pool.reserve_stake,
1355            validator_list,
1356            accounts_chunk,
1357            start_index,
1358            no_merge,
1359        ));
1360        start_index = start_index.saturating_add(MAX_VALIDATORS_TO_UPDATE as u32);
1361    }
1362
1363    let final_instructions = vec![
1364        update_stake_pool_balance(
1365            program_id,
1366            stake_pool_address,
1367            &withdraw_authority,
1368            &stake_pool.validator_list,
1369            &stake_pool.reserve_stake,
1370            &stake_pool.manager_fee_account,
1371            &stake_pool.pool_mint,
1372            &stake_pool.token_program_id,
1373        ),
1374        cleanup_removed_validator_entries(
1375            program_id,
1376            stake_pool_address,
1377            &stake_pool.validator_list,
1378        ),
1379    ];
1380    (update_list_instructions, final_instructions)
1381}
1382
1383fn deposit_stake_internal(
1384    program_id: &Pubkey,
1385    stake_pool: &Pubkey,
1386    validator_list_storage: &Pubkey,
1387    stake_pool_deposit_authority: Option<&Pubkey>,
1388    stake_pool_withdraw_authority: &Pubkey,
1389    deposit_stake_address: &Pubkey,
1390    deposit_stake_withdraw_authority: &Pubkey,
1391    validator_stake_account: &Pubkey,
1392    reserve_stake_account: &Pubkey,
1393    pool_tokens_to: &Pubkey,
1394    manager_fee_account: &Pubkey,
1395    referrer_pool_tokens_account: &Pubkey,
1396    pool_mint: &Pubkey,
1397    token_program_id: &Pubkey,
1398    minimum_pool_tokens_out: Option<u64>,
1399) -> Vec<Instruction> {
1400    let mut instructions = vec![];
1401    let mut accounts = vec![
1402        AccountMeta::new(*stake_pool, false),
1403        AccountMeta::new(*validator_list_storage, false),
1404    ];
1405    if let Some(stake_pool_deposit_authority) = stake_pool_deposit_authority {
1406        accounts.push(AccountMeta::new_readonly(
1407            *stake_pool_deposit_authority,
1408            true,
1409        ));
1410        instructions.extend_from_slice(&[
1411            stake::instruction::authorize(
1412                deposit_stake_address,
1413                deposit_stake_withdraw_authority,
1414                stake_pool_deposit_authority,
1415                stake::state::StakeAuthorize::Staker,
1416                None,
1417            ),
1418            stake::instruction::authorize(
1419                deposit_stake_address,
1420                deposit_stake_withdraw_authority,
1421                stake_pool_deposit_authority,
1422                stake::state::StakeAuthorize::Withdrawer,
1423                None,
1424            ),
1425        ]);
1426    } else {
1427        let stake_pool_deposit_authority =
1428            find_deposit_authority_program_address(program_id, stake_pool).0;
1429        accounts.push(AccountMeta::new_readonly(
1430            stake_pool_deposit_authority,
1431            false,
1432        ));
1433        instructions.extend_from_slice(&[
1434            stake::instruction::authorize(
1435                deposit_stake_address,
1436                deposit_stake_withdraw_authority,
1437                &stake_pool_deposit_authority,
1438                stake::state::StakeAuthorize::Staker,
1439                None,
1440            ),
1441            stake::instruction::authorize(
1442                deposit_stake_address,
1443                deposit_stake_withdraw_authority,
1444                &stake_pool_deposit_authority,
1445                stake::state::StakeAuthorize::Withdrawer,
1446                None,
1447            ),
1448        ]);
1449    };
1450
1451    accounts.extend_from_slice(&[
1452        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
1453        AccountMeta::new(*deposit_stake_address, false),
1454        AccountMeta::new(*validator_stake_account, false),
1455        AccountMeta::new(*reserve_stake_account, false),
1456        AccountMeta::new(*pool_tokens_to, false),
1457        AccountMeta::new(*manager_fee_account, false),
1458        AccountMeta::new(*referrer_pool_tokens_account, false),
1459        AccountMeta::new(*pool_mint, false),
1460        AccountMeta::new_readonly(sysvar::clock::id(), false),
1461        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
1462        AccountMeta::new_readonly(*token_program_id, false),
1463        AccountMeta::new_readonly(stake::program::id(), false),
1464    ]);
1465    instructions.push(
1466        if let Some(minimum_pool_tokens_out) = minimum_pool_tokens_out {
1467            Instruction {
1468                program_id: *program_id,
1469                accounts,
1470                data: StakePoolInstruction::DepositStakeWithSlippage {
1471                    minimum_pool_tokens_out,
1472                }
1473                .try_to_vec()
1474                .unwrap(),
1475            }
1476        } else {
1477            Instruction {
1478                program_id: *program_id,
1479                accounts,
1480                data: StakePoolInstruction::DepositStake.try_to_vec().unwrap(),
1481            }
1482        },
1483    );
1484    instructions
1485}
1486
1487/// Creates instructions required to deposit into a stake pool, given a stake
1488/// account owned by the user.
1489pub fn deposit_stake(
1490    program_id: &Pubkey,
1491    stake_pool: &Pubkey,
1492    validator_list_storage: &Pubkey,
1493    stake_pool_withdraw_authority: &Pubkey,
1494    deposit_stake_address: &Pubkey,
1495    deposit_stake_withdraw_authority: &Pubkey,
1496    validator_stake_account: &Pubkey,
1497    reserve_stake_account: &Pubkey,
1498    pool_tokens_to: &Pubkey,
1499    manager_fee_account: &Pubkey,
1500    referrer_pool_tokens_account: &Pubkey,
1501    pool_mint: &Pubkey,
1502    token_program_id: &Pubkey,
1503) -> Vec<Instruction> {
1504    deposit_stake_internal(
1505        program_id,
1506        stake_pool,
1507        validator_list_storage,
1508        None,
1509        stake_pool_withdraw_authority,
1510        deposit_stake_address,
1511        deposit_stake_withdraw_authority,
1512        validator_stake_account,
1513        reserve_stake_account,
1514        pool_tokens_to,
1515        manager_fee_account,
1516        referrer_pool_tokens_account,
1517        pool_mint,
1518        token_program_id,
1519        None,
1520    )
1521}
1522
1523/// Creates instructions to deposit into a stake pool with slippage
1524pub fn deposit_stake_with_slippage(
1525    program_id: &Pubkey,
1526    stake_pool: &Pubkey,
1527    validator_list_storage: &Pubkey,
1528    stake_pool_withdraw_authority: &Pubkey,
1529    deposit_stake_address: &Pubkey,
1530    deposit_stake_withdraw_authority: &Pubkey,
1531    validator_stake_account: &Pubkey,
1532    reserve_stake_account: &Pubkey,
1533    pool_tokens_to: &Pubkey,
1534    manager_fee_account: &Pubkey,
1535    referrer_pool_tokens_account: &Pubkey,
1536    pool_mint: &Pubkey,
1537    token_program_id: &Pubkey,
1538    minimum_pool_tokens_out: u64,
1539) -> Vec<Instruction> {
1540    deposit_stake_internal(
1541        program_id,
1542        stake_pool,
1543        validator_list_storage,
1544        None,
1545        stake_pool_withdraw_authority,
1546        deposit_stake_address,
1547        deposit_stake_withdraw_authority,
1548        validator_stake_account,
1549        reserve_stake_account,
1550        pool_tokens_to,
1551        manager_fee_account,
1552        referrer_pool_tokens_account,
1553        pool_mint,
1554        token_program_id,
1555        Some(minimum_pool_tokens_out),
1556    )
1557}
1558
1559/// Creates instructions required to deposit into a stake pool, given a stake
1560/// account owned by the user. The difference with `deposit()` is that a deposit
1561/// authority must sign this instruction, which is required for private pools.
1562pub fn deposit_stake_with_authority(
1563    program_id: &Pubkey,
1564    stake_pool: &Pubkey,
1565    validator_list_storage: &Pubkey,
1566    stake_pool_deposit_authority: &Pubkey,
1567    stake_pool_withdraw_authority: &Pubkey,
1568    deposit_stake_address: &Pubkey,
1569    deposit_stake_withdraw_authority: &Pubkey,
1570    validator_stake_account: &Pubkey,
1571    reserve_stake_account: &Pubkey,
1572    pool_tokens_to: &Pubkey,
1573    manager_fee_account: &Pubkey,
1574    referrer_pool_tokens_account: &Pubkey,
1575    pool_mint: &Pubkey,
1576    token_program_id: &Pubkey,
1577) -> Vec<Instruction> {
1578    deposit_stake_internal(
1579        program_id,
1580        stake_pool,
1581        validator_list_storage,
1582        Some(stake_pool_deposit_authority),
1583        stake_pool_withdraw_authority,
1584        deposit_stake_address,
1585        deposit_stake_withdraw_authority,
1586        validator_stake_account,
1587        reserve_stake_account,
1588        pool_tokens_to,
1589        manager_fee_account,
1590        referrer_pool_tokens_account,
1591        pool_mint,
1592        token_program_id,
1593        None,
1594    )
1595}
1596
1597/// Creates instructions required to deposit into a stake pool with slippage, given
1598/// a stake account owned by the user. The difference with `deposit()` is that a deposit
1599/// authority must sign this instruction, which is required for private pools.
1600pub fn deposit_stake_with_authority_and_slippage(
1601    program_id: &Pubkey,
1602    stake_pool: &Pubkey,
1603    validator_list_storage: &Pubkey,
1604    stake_pool_deposit_authority: &Pubkey,
1605    stake_pool_withdraw_authority: &Pubkey,
1606    deposit_stake_address: &Pubkey,
1607    deposit_stake_withdraw_authority: &Pubkey,
1608    validator_stake_account: &Pubkey,
1609    reserve_stake_account: &Pubkey,
1610    pool_tokens_to: &Pubkey,
1611    manager_fee_account: &Pubkey,
1612    referrer_pool_tokens_account: &Pubkey,
1613    pool_mint: &Pubkey,
1614    token_program_id: &Pubkey,
1615    minimum_pool_tokens_out: u64,
1616) -> Vec<Instruction> {
1617    deposit_stake_internal(
1618        program_id,
1619        stake_pool,
1620        validator_list_storage,
1621        Some(stake_pool_deposit_authority),
1622        stake_pool_withdraw_authority,
1623        deposit_stake_address,
1624        deposit_stake_withdraw_authority,
1625        validator_stake_account,
1626        reserve_stake_account,
1627        pool_tokens_to,
1628        manager_fee_account,
1629        referrer_pool_tokens_account,
1630        pool_mint,
1631        token_program_id,
1632        Some(minimum_pool_tokens_out),
1633    )
1634}
1635
1636/// Creates instructions required to deposit SOL directly into a stake pool.
1637fn deposit_sol_internal(
1638    program_id: &Pubkey,
1639    stake_pool: &Pubkey,
1640    stake_pool_withdraw_authority: &Pubkey,
1641    reserve_stake_account: &Pubkey,
1642    lamports_from: &Pubkey,
1643    pool_tokens_to: &Pubkey,
1644    manager_fee_account: &Pubkey,
1645    referrer_pool_tokens_account: &Pubkey,
1646    pool_mint: &Pubkey,
1647    token_program_id: &Pubkey,
1648    sol_deposit_authority: Option<&Pubkey>,
1649    lamports_in: u64,
1650    minimum_pool_tokens_out: Option<u64>,
1651) -> Instruction {
1652    let mut accounts = vec![
1653        AccountMeta::new(*stake_pool, false),
1654        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
1655        AccountMeta::new(*reserve_stake_account, false),
1656        AccountMeta::new(*lamports_from, true),
1657        AccountMeta::new(*pool_tokens_to, false),
1658        AccountMeta::new(*manager_fee_account, false),
1659        AccountMeta::new(*referrer_pool_tokens_account, false),
1660        AccountMeta::new(*pool_mint, false),
1661        AccountMeta::new_readonly(system_program::id(), false),
1662        AccountMeta::new_readonly(*token_program_id, false),
1663    ];
1664    if let Some(sol_deposit_authority) = sol_deposit_authority {
1665        accounts.push(AccountMeta::new_readonly(*sol_deposit_authority, true));
1666    }
1667    if let Some(minimum_pool_tokens_out) = minimum_pool_tokens_out {
1668        Instruction {
1669            program_id: *program_id,
1670            accounts,
1671            data: StakePoolInstruction::DepositSolWithSlippage {
1672                lamports_in,
1673                minimum_pool_tokens_out,
1674            }
1675            .try_to_vec()
1676            .unwrap(),
1677        }
1678    } else {
1679        Instruction {
1680            program_id: *program_id,
1681            accounts,
1682            data: StakePoolInstruction::DepositSol(lamports_in)
1683                .try_to_vec()
1684                .unwrap(),
1685        }
1686    }
1687}
1688
1689/// Creates instruction to deposit SOL directly into a stake pool.
1690pub fn deposit_sol(
1691    program_id: &Pubkey,
1692    stake_pool: &Pubkey,
1693    stake_pool_withdraw_authority: &Pubkey,
1694    reserve_stake_account: &Pubkey,
1695    lamports_from: &Pubkey,
1696    pool_tokens_to: &Pubkey,
1697    manager_fee_account: &Pubkey,
1698    referrer_pool_tokens_account: &Pubkey,
1699    pool_mint: &Pubkey,
1700    token_program_id: &Pubkey,
1701    lamports_in: u64,
1702) -> Instruction {
1703    deposit_sol_internal(
1704        program_id,
1705        stake_pool,
1706        stake_pool_withdraw_authority,
1707        reserve_stake_account,
1708        lamports_from,
1709        pool_tokens_to,
1710        manager_fee_account,
1711        referrer_pool_tokens_account,
1712        pool_mint,
1713        token_program_id,
1714        None,
1715        lamports_in,
1716        None,
1717    )
1718}
1719
1720/// Creates instruction to deposit SOL directly into a stake pool with slippage constraint.
1721pub fn deposit_sol_with_slippage(
1722    program_id: &Pubkey,
1723    stake_pool: &Pubkey,
1724    stake_pool_withdraw_authority: &Pubkey,
1725    reserve_stake_account: &Pubkey,
1726    lamports_from: &Pubkey,
1727    pool_tokens_to: &Pubkey,
1728    manager_fee_account: &Pubkey,
1729    referrer_pool_tokens_account: &Pubkey,
1730    pool_mint: &Pubkey,
1731    token_program_id: &Pubkey,
1732    lamports_in: u64,
1733    minimum_pool_tokens_out: u64,
1734) -> Instruction {
1735    deposit_sol_internal(
1736        program_id,
1737        stake_pool,
1738        stake_pool_withdraw_authority,
1739        reserve_stake_account,
1740        lamports_from,
1741        pool_tokens_to,
1742        manager_fee_account,
1743        referrer_pool_tokens_account,
1744        pool_mint,
1745        token_program_id,
1746        None,
1747        lamports_in,
1748        Some(minimum_pool_tokens_out),
1749    )
1750}
1751
1752/// Creates instruction required to deposit SOL directly into a stake pool.
1753/// The difference with `deposit_sol()` is that a deposit
1754/// authority must sign this instruction.
1755pub fn deposit_sol_with_authority(
1756    program_id: &Pubkey,
1757    stake_pool: &Pubkey,
1758    sol_deposit_authority: &Pubkey,
1759    stake_pool_withdraw_authority: &Pubkey,
1760    reserve_stake_account: &Pubkey,
1761    lamports_from: &Pubkey,
1762    pool_tokens_to: &Pubkey,
1763    manager_fee_account: &Pubkey,
1764    referrer_pool_tokens_account: &Pubkey,
1765    pool_mint: &Pubkey,
1766    token_program_id: &Pubkey,
1767    lamports_in: u64,
1768) -> Instruction {
1769    deposit_sol_internal(
1770        program_id,
1771        stake_pool,
1772        stake_pool_withdraw_authority,
1773        reserve_stake_account,
1774        lamports_from,
1775        pool_tokens_to,
1776        manager_fee_account,
1777        referrer_pool_tokens_account,
1778        pool_mint,
1779        token_program_id,
1780        Some(sol_deposit_authority),
1781        lamports_in,
1782        None,
1783    )
1784}
1785
1786/// Creates instruction to deposit SOL directly into a stake pool with slippage constraint.
1787pub fn deposit_sol_with_authority_and_slippage(
1788    program_id: &Pubkey,
1789    stake_pool: &Pubkey,
1790    sol_deposit_authority: &Pubkey,
1791    stake_pool_withdraw_authority: &Pubkey,
1792    reserve_stake_account: &Pubkey,
1793    lamports_from: &Pubkey,
1794    pool_tokens_to: &Pubkey,
1795    manager_fee_account: &Pubkey,
1796    referrer_pool_tokens_account: &Pubkey,
1797    pool_mint: &Pubkey,
1798    token_program_id: &Pubkey,
1799    lamports_in: u64,
1800    minimum_pool_tokens_out: u64,
1801) -> Instruction {
1802    deposit_sol_internal(
1803        program_id,
1804        stake_pool,
1805        stake_pool_withdraw_authority,
1806        reserve_stake_account,
1807        lamports_from,
1808        pool_tokens_to,
1809        manager_fee_account,
1810        referrer_pool_tokens_account,
1811        pool_mint,
1812        token_program_id,
1813        Some(sol_deposit_authority),
1814        lamports_in,
1815        Some(minimum_pool_tokens_out),
1816    )
1817}
1818
1819fn withdraw_stake_internal(
1820    program_id: &Pubkey,
1821    stake_pool: &Pubkey,
1822    validator_list_storage: &Pubkey,
1823    stake_pool_withdraw: &Pubkey,
1824    stake_to_split: &Pubkey,
1825    stake_to_receive: &Pubkey,
1826    user_stake_authority: &Pubkey,
1827    user_transfer_authority: &Pubkey,
1828    user_pool_token_account: &Pubkey,
1829    manager_fee_account: &Pubkey,
1830    pool_mint: &Pubkey,
1831    token_program_id: &Pubkey,
1832    pool_tokens_in: u64,
1833    minimum_lamports_out: Option<u64>,
1834) -> Instruction {
1835    let accounts = vec![
1836        AccountMeta::new(*stake_pool, false),
1837        AccountMeta::new(*validator_list_storage, false),
1838        AccountMeta::new_readonly(*stake_pool_withdraw, false),
1839        AccountMeta::new(*stake_to_split, false),
1840        AccountMeta::new(*stake_to_receive, false),
1841        AccountMeta::new_readonly(*user_stake_authority, false),
1842        AccountMeta::new_readonly(*user_transfer_authority, true),
1843        AccountMeta::new(*user_pool_token_account, false),
1844        AccountMeta::new(*manager_fee_account, false),
1845        AccountMeta::new(*pool_mint, false),
1846        AccountMeta::new_readonly(sysvar::clock::id(), false),
1847        AccountMeta::new_readonly(*token_program_id, false),
1848        AccountMeta::new_readonly(stake::program::id(), false),
1849    ];
1850    if let Some(minimum_lamports_out) = minimum_lamports_out {
1851        Instruction {
1852            program_id: *program_id,
1853            accounts,
1854            data: StakePoolInstruction::WithdrawStakeWithSlippage {
1855                pool_tokens_in,
1856                minimum_lamports_out,
1857            }
1858            .try_to_vec()
1859            .unwrap(),
1860        }
1861    } else {
1862        Instruction {
1863            program_id: *program_id,
1864            accounts,
1865            data: StakePoolInstruction::WithdrawStake(pool_tokens_in)
1866                .try_to_vec()
1867                .unwrap(),
1868        }
1869    }
1870}
1871
1872/// Creates a 'WithdrawStake' instruction.
1873pub fn withdraw_stake(
1874    program_id: &Pubkey,
1875    stake_pool: &Pubkey,
1876    validator_list_storage: &Pubkey,
1877    stake_pool_withdraw: &Pubkey,
1878    stake_to_split: &Pubkey,
1879    stake_to_receive: &Pubkey,
1880    user_stake_authority: &Pubkey,
1881    user_transfer_authority: &Pubkey,
1882    user_pool_token_account: &Pubkey,
1883    manager_fee_account: &Pubkey,
1884    pool_mint: &Pubkey,
1885    token_program_id: &Pubkey,
1886    pool_tokens_in: u64,
1887) -> Instruction {
1888    withdraw_stake_internal(
1889        program_id,
1890        stake_pool,
1891        validator_list_storage,
1892        stake_pool_withdraw,
1893        stake_to_split,
1894        stake_to_receive,
1895        user_stake_authority,
1896        user_transfer_authority,
1897        user_pool_token_account,
1898        manager_fee_account,
1899        pool_mint,
1900        token_program_id,
1901        pool_tokens_in,
1902        None,
1903    )
1904}
1905
1906/// Creates a 'WithdrawStakeWithSlippage' instruction.
1907pub fn withdraw_stake_with_slippage(
1908    program_id: &Pubkey,
1909    stake_pool: &Pubkey,
1910    validator_list_storage: &Pubkey,
1911    stake_pool_withdraw: &Pubkey,
1912    stake_to_split: &Pubkey,
1913    stake_to_receive: &Pubkey,
1914    user_stake_authority: &Pubkey,
1915    user_transfer_authority: &Pubkey,
1916    user_pool_token_account: &Pubkey,
1917    manager_fee_account: &Pubkey,
1918    pool_mint: &Pubkey,
1919    token_program_id: &Pubkey,
1920    pool_tokens_in: u64,
1921    minimum_lamports_out: u64,
1922) -> Instruction {
1923    withdraw_stake_internal(
1924        program_id,
1925        stake_pool,
1926        validator_list_storage,
1927        stake_pool_withdraw,
1928        stake_to_split,
1929        stake_to_receive,
1930        user_stake_authority,
1931        user_transfer_authority,
1932        user_pool_token_account,
1933        manager_fee_account,
1934        pool_mint,
1935        token_program_id,
1936        pool_tokens_in,
1937        Some(minimum_lamports_out),
1938    )
1939}
1940
1941fn withdraw_sol_internal(
1942    program_id: &Pubkey,
1943    stake_pool: &Pubkey,
1944    stake_pool_withdraw_authority: &Pubkey,
1945    user_transfer_authority: &Pubkey,
1946    pool_tokens_from: &Pubkey,
1947    reserve_stake_account: &Pubkey,
1948    lamports_to: &Pubkey,
1949    manager_fee_account: &Pubkey,
1950    pool_mint: &Pubkey,
1951    token_program_id: &Pubkey,
1952    sol_withdraw_authority: Option<&Pubkey>,
1953    pool_tokens_in: u64,
1954    minimum_lamports_out: Option<u64>,
1955) -> Instruction {
1956    let mut accounts = vec![
1957        AccountMeta::new(*stake_pool, false),
1958        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
1959        AccountMeta::new_readonly(*user_transfer_authority, true),
1960        AccountMeta::new(*pool_tokens_from, false),
1961        AccountMeta::new(*reserve_stake_account, false),
1962        AccountMeta::new(*lamports_to, false),
1963        AccountMeta::new(*manager_fee_account, false),
1964        AccountMeta::new(*pool_mint, false),
1965        AccountMeta::new_readonly(sysvar::clock::id(), false),
1966        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
1967        AccountMeta::new_readonly(stake::program::id(), false),
1968        AccountMeta::new_readonly(*token_program_id, false),
1969    ];
1970    if let Some(sol_withdraw_authority) = sol_withdraw_authority {
1971        accounts.push(AccountMeta::new_readonly(*sol_withdraw_authority, true));
1972    }
1973    if let Some(minimum_lamports_out) = minimum_lamports_out {
1974        Instruction {
1975            program_id: *program_id,
1976            accounts,
1977            data: StakePoolInstruction::WithdrawSolWithSlippage {
1978                pool_tokens_in,
1979                minimum_lamports_out,
1980            }
1981            .try_to_vec()
1982            .unwrap(),
1983        }
1984    } else {
1985        Instruction {
1986            program_id: *program_id,
1987            accounts,
1988            data: StakePoolInstruction::WithdrawSol(pool_tokens_in)
1989                .try_to_vec()
1990                .unwrap(),
1991        }
1992    }
1993}
1994
1995/// Creates instruction required to withdraw SOL directly from a stake pool.
1996pub fn withdraw_sol(
1997    program_id: &Pubkey,
1998    stake_pool: &Pubkey,
1999    stake_pool_withdraw_authority: &Pubkey,
2000    user_transfer_authority: &Pubkey,
2001    pool_tokens_from: &Pubkey,
2002    reserve_stake_account: &Pubkey,
2003    lamports_to: &Pubkey,
2004    manager_fee_account: &Pubkey,
2005    pool_mint: &Pubkey,
2006    token_program_id: &Pubkey,
2007    pool_tokens_in: u64,
2008) -> Instruction {
2009    withdraw_sol_internal(
2010        program_id,
2011        stake_pool,
2012        stake_pool_withdraw_authority,
2013        user_transfer_authority,
2014        pool_tokens_from,
2015        reserve_stake_account,
2016        lamports_to,
2017        manager_fee_account,
2018        pool_mint,
2019        token_program_id,
2020        None,
2021        pool_tokens_in,
2022        None,
2023    )
2024}
2025
2026/// Creates instruction required to withdraw SOL directly from a stake pool with
2027/// slippage constraints.
2028pub fn withdraw_sol_with_slippage(
2029    program_id: &Pubkey,
2030    stake_pool: &Pubkey,
2031    stake_pool_withdraw_authority: &Pubkey,
2032    user_transfer_authority: &Pubkey,
2033    pool_tokens_from: &Pubkey,
2034    reserve_stake_account: &Pubkey,
2035    lamports_to: &Pubkey,
2036    manager_fee_account: &Pubkey,
2037    pool_mint: &Pubkey,
2038    token_program_id: &Pubkey,
2039    pool_tokens_in: u64,
2040    minimum_lamports_out: u64,
2041) -> Instruction {
2042    withdraw_sol_internal(
2043        program_id,
2044        stake_pool,
2045        stake_pool_withdraw_authority,
2046        user_transfer_authority,
2047        pool_tokens_from,
2048        reserve_stake_account,
2049        lamports_to,
2050        manager_fee_account,
2051        pool_mint,
2052        token_program_id,
2053        None,
2054        pool_tokens_in,
2055        Some(minimum_lamports_out),
2056    )
2057}
2058
2059/// Creates instruction required to withdraw SOL directly from a stake pool.
2060/// The difference with `withdraw_sol()` is that the sol withdraw authority
2061/// must sign this instruction.
2062pub fn withdraw_sol_with_authority(
2063    program_id: &Pubkey,
2064    stake_pool: &Pubkey,
2065    sol_withdraw_authority: &Pubkey,
2066    stake_pool_withdraw_authority: &Pubkey,
2067    user_transfer_authority: &Pubkey,
2068    pool_tokens_from: &Pubkey,
2069    reserve_stake_account: &Pubkey,
2070    lamports_to: &Pubkey,
2071    manager_fee_account: &Pubkey,
2072    pool_mint: &Pubkey,
2073    token_program_id: &Pubkey,
2074    pool_tokens_in: u64,
2075) -> Instruction {
2076    withdraw_sol_internal(
2077        program_id,
2078        stake_pool,
2079        stake_pool_withdraw_authority,
2080        user_transfer_authority,
2081        pool_tokens_from,
2082        reserve_stake_account,
2083        lamports_to,
2084        manager_fee_account,
2085        pool_mint,
2086        token_program_id,
2087        Some(sol_withdraw_authority),
2088        pool_tokens_in,
2089        None,
2090    )
2091}
2092
2093/// Creates instruction required to withdraw SOL directly from a stake pool with
2094/// a slippage constraint.
2095/// The difference with `withdraw_sol()` is that the sol withdraw authority
2096/// must sign this instruction.
2097pub fn withdraw_sol_with_authority_and_slippage(
2098    program_id: &Pubkey,
2099    stake_pool: &Pubkey,
2100    sol_withdraw_authority: &Pubkey,
2101    stake_pool_withdraw_authority: &Pubkey,
2102    user_transfer_authority: &Pubkey,
2103    pool_tokens_from: &Pubkey,
2104    reserve_stake_account: &Pubkey,
2105    lamports_to: &Pubkey,
2106    manager_fee_account: &Pubkey,
2107    pool_mint: &Pubkey,
2108    token_program_id: &Pubkey,
2109    pool_tokens_in: u64,
2110    minimum_lamports_out: u64,
2111) -> Instruction {
2112    withdraw_sol_internal(
2113        program_id,
2114        stake_pool,
2115        stake_pool_withdraw_authority,
2116        user_transfer_authority,
2117        pool_tokens_from,
2118        reserve_stake_account,
2119        lamports_to,
2120        manager_fee_account,
2121        pool_mint,
2122        token_program_id,
2123        Some(sol_withdraw_authority),
2124        pool_tokens_in,
2125        Some(minimum_lamports_out),
2126    )
2127}
2128
2129/// Creates a 'set manager' instruction.
2130pub fn set_manager(
2131    program_id: &Pubkey,
2132    stake_pool: &Pubkey,
2133    manager: &Pubkey,
2134    new_manager: &Pubkey,
2135    new_fee_receiver: &Pubkey,
2136) -> Instruction {
2137    let accounts = vec![
2138        AccountMeta::new(*stake_pool, false),
2139        AccountMeta::new_readonly(*manager, true),
2140        AccountMeta::new_readonly(*new_manager, true),
2141        AccountMeta::new_readonly(*new_fee_receiver, false),
2142    ];
2143    Instruction {
2144        program_id: *program_id,
2145        accounts,
2146        data: StakePoolInstruction::SetManager.try_to_vec().unwrap(),
2147    }
2148}
2149
2150/// Creates a 'set fee' instruction.
2151pub fn set_fee(
2152    program_id: &Pubkey,
2153    stake_pool: &Pubkey,
2154    manager: &Pubkey,
2155    fee: FeeType,
2156) -> Instruction {
2157    let accounts = vec![
2158        AccountMeta::new(*stake_pool, false),
2159        AccountMeta::new_readonly(*manager, true),
2160    ];
2161    Instruction {
2162        program_id: *program_id,
2163        accounts,
2164        data: StakePoolInstruction::SetFee { fee }.try_to_vec().unwrap(),
2165    }
2166}
2167
2168/// Creates a 'set staker' instruction.
2169pub fn set_staker(
2170    program_id: &Pubkey,
2171    stake_pool: &Pubkey,
2172    set_staker_authority: &Pubkey,
2173    new_staker: &Pubkey,
2174) -> Instruction {
2175    let accounts = vec![
2176        AccountMeta::new(*stake_pool, false),
2177        AccountMeta::new_readonly(*set_staker_authority, true),
2178        AccountMeta::new_readonly(*new_staker, false),
2179    ];
2180    Instruction {
2181        program_id: *program_id,
2182        accounts,
2183        data: StakePoolInstruction::SetStaker.try_to_vec().unwrap(),
2184    }
2185}
2186
2187/// Creates a 'SetFundingAuthority' instruction.
2188pub fn set_funding_authority(
2189    program_id: &Pubkey,
2190    stake_pool: &Pubkey,
2191    manager: &Pubkey,
2192    new_sol_deposit_authority: Option<&Pubkey>,
2193    funding_type: FundingType,
2194) -> Instruction {
2195    let mut accounts = vec![
2196        AccountMeta::new(*stake_pool, false),
2197        AccountMeta::new_readonly(*manager, true),
2198    ];
2199    if let Some(auth) = new_sol_deposit_authority {
2200        accounts.push(AccountMeta::new_readonly(*auth, false))
2201    }
2202    Instruction {
2203        program_id: *program_id,
2204        accounts,
2205        data: StakePoolInstruction::SetFundingAuthority(funding_type)
2206            .try_to_vec()
2207            .unwrap(),
2208    }
2209}
2210
2211/// Creates an instruction to update metadata in the mpl token metadata program account for
2212/// the pool token
2213pub fn update_token_metadata(
2214    program_id: &Pubkey,
2215    stake_pool: &Pubkey,
2216    manager: &Pubkey,
2217    pool_mint: &Pubkey,
2218    name: String,
2219    symbol: String,
2220    uri: String,
2221) -> Instruction {
2222    let (stake_pool_withdraw_authority, _) =
2223        find_withdraw_authority_program_address(program_id, stake_pool);
2224    let (token_metadata, _) = find_metadata_account(pool_mint);
2225
2226    let accounts = vec![
2227        AccountMeta::new_readonly(*stake_pool, false),
2228        AccountMeta::new_readonly(*manager, true),
2229        AccountMeta::new_readonly(stake_pool_withdraw_authority, false),
2230        AccountMeta::new(token_metadata, false),
2231        AccountMeta::new_readonly(mpl_token_metadata::id(), false),
2232    ];
2233
2234    Instruction {
2235        program_id: *program_id,
2236        accounts,
2237        data: StakePoolInstruction::UpdateTokenMetadata { name, symbol, uri }
2238            .try_to_vec()
2239            .unwrap(),
2240    }
2241}
2242
2243/// Creates an instruction to create metadata using the mpl token metadata program for
2244/// the pool token
2245pub fn create_token_metadata(
2246    program_id: &Pubkey,
2247    stake_pool: &Pubkey,
2248    manager: &Pubkey,
2249    pool_mint: &Pubkey,
2250    payer: &Pubkey,
2251    name: String,
2252    symbol: String,
2253    uri: String,
2254) -> Instruction {
2255    let (stake_pool_withdraw_authority, _) =
2256        find_withdraw_authority_program_address(program_id, stake_pool);
2257    let (token_metadata, _) = find_metadata_account(pool_mint);
2258
2259    let accounts = vec![
2260        AccountMeta::new_readonly(*stake_pool, false),
2261        AccountMeta::new_readonly(*manager, true),
2262        AccountMeta::new_readonly(stake_pool_withdraw_authority, false),
2263        AccountMeta::new_readonly(*pool_mint, false),
2264        AccountMeta::new(*payer, true),
2265        AccountMeta::new(token_metadata, false),
2266        AccountMeta::new_readonly(mpl_token_metadata::id(), false),
2267        AccountMeta::new_readonly(system_program::id(), false),
2268    ];
2269
2270    Instruction {
2271        program_id: *program_id,
2272        accounts,
2273        data: StakePoolInstruction::CreateTokenMetadata { name, symbol, uri }
2274            .try_to_vec()
2275            .unwrap(),
2276    }
2277}