Skip to main content

spl_stake_pool/
instruction.rs

1//! Instruction types
2
3// Remove the following `allow` when `Redelegate` is removed, required to avoid
4// warnings from uses of deprecated types during trait derivations.
5#![allow(deprecated)]
6#![allow(clippy::too_many_arguments)]
7
8use spl_token::native_mint;
9use {
10    crate::{
11        find_deposit_authority_program_address, find_ephemeral_stake_program_address,
12        find_stake_program_address, find_transient_stake_program_address,
13        find_withdraw_authority_program_address,
14        inline_mpl_token_metadata::{self, pda::find_metadata_account},
15        state::{Fee, FeeType, StakePool, ValidatorList, ValidatorStakeInfo},
16        MAX_VALIDATORS_TO_UPDATE,
17    },
18    borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
19    solana_program::{
20        instruction::{AccountMeta, Instruction},
21        program_error::ProgramError,
22        pubkey::Pubkey,
23        stake,
24        stake_history::Epoch,
25        system_program, sysvar,
26    },
27    std::num::NonZeroU32,
28};
29
30/// Defines which validator vote account is set during the
31/// `SetPreferredValidator` instruction
32#[repr(C)]
33#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, BorshSchema)]
34pub enum PreferredValidatorType {
35    /// Set preferred validator for deposits
36    Deposit,
37    /// Set preferred validator for withdraws
38    Withdraw,
39}
40
41/// Defines which authority to update in the `SetFundingAuthority`
42/// instruction
43#[repr(C)]
44#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, BorshSchema)]
45pub enum FundingType {
46    /// Sets the stake deposit authority
47    StakeDeposit,
48    /// Sets the SOL deposit authority
49    SolDeposit,
50    /// Sets the SOL withdraw authority
51    SolWithdraw,
52}
53
54/// Instructions supported by the `StakePool` program.
55#[repr(C)]
56#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
57pub enum StakePoolInstruction {
58    ///   Initializes a new `StakePool`.
59    ///
60    ///   0. `[w]` New `StakePool` to create.
61    ///   1. `[s]` Manager
62    ///   2. `[]` Staker
63    ///   3. `[]` Stake pool withdraw authority
64    ///   4. `[w]` Uninitialized validator stake list storage account
65    ///   5. `[]` Reserve stake account must be initialized, have zero balance,
66    ///      and staker / withdrawer authority set to pool withdraw authority.
67    ///   6. `[]` Pool token mint. Must have zero supply, owned by withdraw
68    ///      authority.
69    ///   7. `[]` Pool account to deposit the generated fee for manager.
70    ///   8. `[]` Token program id
71    ///   9. `[]` (Optional) Deposit authority that must sign all deposits.
72    ///      Defaults to the program address generated using
73    ///      `find_deposit_authority_program_address`, making deposits
74    ///      permissionless.
75    Initialize {
76        /// Fee assessed as percentage of perceived rewards
77        fee: Fee,
78        /// Fee charged per withdrawal as percentage of withdrawal
79        withdrawal_fee: Fee,
80        /// Fee charged per deposit as percentage of deposit
81        deposit_fee: Fee,
82        /// Percentage [0-100] of `deposit_fee` that goes to referrer
83        referral_fee: u8,
84        /// Maximum expected number of validators
85        max_validators: u32,
86    },
87
88    ///   (Staker only) Adds stake account delegated to validator to the pool's
89    ///   list of managed validators.
90    ///
91    ///   The stake account will have the rent-exempt amount plus
92    ///   `max(
93    ///     crate::MINIMUM_ACTIVE_STAKE,
94    ///     solana_program::stake::tools::get_minimum_delegation()
95    ///   )`.
96    ///   It is funded from the stake pool reserve.
97    ///
98    ///   0. `[w]` Stake pool
99    ///   1. `[s]` Staker
100    ///   2. `[w]` Reserve stake account
101    ///   3. `[]` Stake pool withdraw authority
102    ///   4. `[w]` Validator stake list storage account
103    ///   5. `[w]` Stake account to add to the pool
104    ///   6. `[]` Validator this stake account will be delegated to
105    ///   7. `[]` Rent sysvar
106    ///   8. `[]` Clock sysvar
107    ///   9. '[]' Stake history sysvar
108    ///  10. '[]' Stake config sysvar
109    ///  11. `[]` System program
110    ///  12. `[]` Stake program
111    ///
112    ///  User data: optional non-zero `u32` seed used for generating the
113    ///  validator stake address
114    AddValidatorToPool(u32),
115
116    ///   (Staker only) Removes validator from the pool, deactivating its stake
117    ///
118    ///   Only succeeds if the validator stake account has the minimum of
119    ///   `max(crate::MINIMUM_ACTIVE_STAKE,
120    /// solana_program::stake::tools::get_minimum_delegation())`.   plus the
121    /// rent-exempt amount.
122    ///
123    ///   0. `[w]` Stake pool
124    ///   1. `[s]` Staker
125    ///   2. `[]` Stake pool withdraw authority
126    ///   3. `[w]` Validator stake list storage account
127    ///   4. `[w]` Stake account to remove from the pool
128    ///   5. `[w]` Transient stake account, to deactivate if necessary
129    ///   6. `[]` Sysvar clock
130    ///   7. `[]` Stake program id,
131    RemoveValidatorFromPool,
132
133    /// NOTE: This instruction has been deprecated since version 0.7.0. Please
134    /// use `DecreaseValidatorStakeWithReserve` instead.
135    ///
136    /// (Staker only) Decrease active stake on a validator, eventually moving it
137    /// to the reserve
138    ///
139    /// Internally, this instruction splits a validator stake account into its
140    /// corresponding transient stake account and deactivates it.
141    ///
142    /// In order to rebalance the pool without taking custody, the staker needs
143    /// a way of reducing the stake on a stake account. This instruction splits
144    /// some amount of stake, up to the total activated stake, from the
145    /// canonical validator stake account, into its "transient" stake
146    /// account.
147    ///
148    /// The instruction only succeeds if the transient stake account does not
149    /// exist. The amount of lamports to move must be at least rent-exemption
150    /// plus `max(crate::MINIMUM_ACTIVE_STAKE,
151    /// solana_program::stake::tools::get_minimum_delegation())`.
152    ///
153    ///  0. `[]` Stake pool
154    ///  1. `[s]` Stake pool staker
155    ///  2. `[]` Stake pool withdraw authority
156    ///  3. `[w]` Validator list
157    ///  4. `[w]` Canonical stake account to split from
158    ///  5. `[w]` Transient stake account to receive split
159    ///  6. `[]` Clock sysvar
160    ///  7. `[]` Rent sysvar
161    ///  8. `[]` System program
162    ///  9. `[]` Stake program
163    DecreaseValidatorStake {
164        /// amount of lamports to split into the transient stake account
165        lamports: u64,
166        /// seed used to create transient stake account
167        transient_stake_seed: u64,
168    },
169
170    /// (Staker only) Increase stake on a validator from the reserve account
171    ///
172    /// Internally, this instruction splits reserve stake into a transient stake
173    /// account and delegate to the appropriate validator.
174    /// `UpdateValidatorListBalance` will do the work of merging once it's
175    /// ready.
176    ///
177    /// This instruction only succeeds if the transient stake account does not
178    /// exist. The minimum amount to move is rent-exemption plus
179    /// `max(crate::MINIMUM_ACTIVE_STAKE,
180    /// solana_program::stake::tools::get_minimum_delegation())`.
181    ///
182    ///  0. `[]` Stake pool
183    ///  1. `[s]` Stake pool staker
184    ///  2. `[]` Stake pool withdraw authority
185    ///  3. `[w]` Validator list
186    ///  4. `[w]` Stake pool reserve stake
187    ///  5. `[w]` Transient stake account
188    ///  6. `[]` Validator stake account
189    ///  7. `[]` Validator vote account to delegate to
190    ///  8. '[]' Clock sysvar
191    ///  9. '[]' Rent sysvar
192    /// 10. `[]` Stake History sysvar
193    /// 11. `[]` Stake Config sysvar
194    /// 12. `[]` System program
195    /// 13. `[]` Stake program
196    ///
197    /// User data: amount of lamports to increase on the given validator.
198    ///
199    /// The actual amount split into the transient stake account is:
200    /// `lamports + stake_rent_exemption`.
201    ///
202    /// The rent-exemption of the stake account is withdrawn back to the
203    /// reserve after it is merged.
204    IncreaseValidatorStake {
205        /// amount of lamports to increase on the given validator
206        lamports: u64,
207        /// seed used to create transient stake account
208        transient_stake_seed: u64,
209    },
210
211    /// (Staker only) Set the preferred deposit or withdraw stake account for
212    /// the stake pool
213    ///
214    /// In order to avoid users abusing the stake pool as a free conversion
215    /// between SOL staked on different validators, the staker can force all
216    /// deposits and/or withdraws to go to one chosen account, or unset that
217    /// account.
218    ///
219    /// 0. `[w]` Stake pool
220    /// 1. `[s]` Stake pool staker
221    /// 2. `[]` Validator list
222    ///
223    /// Fails if the validator is not part of the stake pool.
224    SetPreferredValidator {
225        /// Affected operation (deposit or withdraw)
226        validator_type: PreferredValidatorType,
227        /// Validator vote account that deposits or withdraws must go through,
228        /// unset with None
229        validator_vote_address: Option<Pubkey>,
230    },
231
232    ///  Updates balances of validator and transient stake accounts in the pool
233    ///
234    ///  While going through the pairs of validator and transient stake
235    ///  accounts, if the transient stake is inactive, it is merged into the
236    ///  reserve stake account. If the transient stake is active and has
237    ///  matching credits observed, it is merged into the canonical
238    ///  validator stake account. In all other states, nothing is done, and
239    ///  the balance is simply added to the canonical stake account balance.
240    ///
241    ///  0. `[]` Stake pool
242    ///  1. `[]` Stake pool withdraw authority
243    ///  2. `[w]` Validator stake list storage account
244    ///  3. `[w]` Reserve stake account
245    ///  4. `[]` Sysvar clock
246    ///  5. `[]` Sysvar stake history
247    ///  6. `[]` Stake program
248    ///  7. `..7+2N` [] N pairs of validator and transient stake accounts
249    UpdateValidatorListBalance {
250        /// Index to start updating on the validator list
251        start_index: u32,
252        /// If true, don't try merging transient stake accounts into the reserve
253        /// or validator stake account.  Useful for testing or if a
254        /// particular stake account is in a bad state, but we still
255        /// want to update
256        no_merge: bool,
257    },
258
259    ///   Updates total pool balance based on balances in the reserve and
260    ///   validator list
261    ///
262    ///   0. `[w]` Stake pool
263    ///   1. `[]` Stake pool withdraw authority
264    ///   2. `[w]` Validator stake list storage account
265    ///   3. `[]` Reserve stake account
266    ///   4. `[w]` Account to receive pool fee tokens
267    ///   5. `[w]` Pool mint account
268    ///   6. `[]` Pool token program
269    UpdateStakePoolBalance,
270
271    ///   Cleans up validator stake account entries marked as `ReadyForRemoval`
272    ///
273    ///   0. `[]` Stake pool
274    ///   1. `[w]` Validator stake list storage account
275    CleanupRemovedValidatorEntries,
276
277    ///   Deposit some stake into the pool. The output is a "pool" token
278    ///   representing ownership into the pool. Inputs are converted to the
279    ///   current ratio.
280    ///
281    ///   0. `[w]` Stake pool
282    ///   1. `[w]` Validator stake list storage account
283    ///   2. `[s]/[]` Stake pool deposit authority
284    ///   3. `[]` Stake pool withdraw authority
285    ///   4. `[w]` Stake account to join the pool (withdraw authority for the
286    ///      stake account should be first set to the stake pool deposit
287    ///      authority)
288    ///   5. `[w]` Validator stake account for the stake account to be merged
289    ///      with
290    ///   6. `[w]` Reserve stake account, to withdraw rent exempt reserve
291    ///   7. `[w]` User account to receive pool tokens
292    ///   8. `[w]` Account to receive pool fee tokens
293    ///   9. `[w]` Account to receive a portion of pool fee tokens as referral
294    ///      fees
295    ///   10. `[w]` Pool token mint account
296    ///   11. '[]' Sysvar clock account
297    ///   12. '[]' Sysvar stake history account
298    ///   13. `[]` Pool token program id,
299    ///   14. `[]` Stake program id,
300    DepositStake,
301
302    ///   Withdraw the token from the pool at the current ratio.
303    ///
304    ///   Succeeds if the stake account has enough SOL to cover the desired
305    ///   amount of pool tokens, and if the withdrawal keeps the total
306    ///   staked amount above the minimum of rent-exempt amount plus `max(
307    ///     crate::MINIMUM_ACTIVE_STAKE,
308    ///     solana_program::stake::tools::get_minimum_delegation()
309    ///   )`.
310    ///
311    ///   When allowing withdrawals, the order of priority goes:
312    ///
313    ///   * preferred withdraw validator stake account (if set)
314    ///   * validator stake accounts
315    ///   * transient stake accounts
316    ///   * reserve stake account OR totally remove validator stake accounts
317    ///
318    ///   A user can freely withdraw from a validator stake account, and if they
319    ///   are all at the minimum, then they can withdraw from transient stake
320    ///   accounts, and if they are all at minimum, then they can withdraw from
321    ///   the reserve or remove any validator from the pool.
322    ///
323    ///   0. `[w]` Stake pool
324    ///   1. `[w]` Validator stake list storage account
325    ///   2. `[]` Stake pool withdraw authority
326    ///   3. `[w]` Validator or reserve stake account to split
327    ///   4. `[w]` Uninitialized stake account to receive withdrawal
328    ///   5. `[]` User account to set as a new withdraw authority
329    ///   6. `[s]` User transfer authority, for pool token account
330    ///   7. `[w]` User account with pool tokens to burn from
331    ///   8. `[w]` Account to receive pool fee tokens
332    ///   9. `[w]` Pool token mint account
333    ///  10. `[]` Sysvar clock account (required)
334    ///  11. `[]` Pool token program id
335    ///  12. `[]` Stake program id,
336    ///
337    ///  User data: amount of pool tokens to withdraw
338    WithdrawStake(u64),
339
340    ///  (Manager only) Update manager
341    ///
342    ///  0. `[w]` Stake pool
343    ///  1. `[s]` Manager
344    ///  2. `[s]` New manager
345    ///  3. `[]` New manager fee account
346    SetManager,
347
348    ///  (Manager only) Update fee
349    ///
350    ///  0. `[w]` Stake pool
351    ///  1. `[s]` Manager
352    SetFee {
353        /// Type of fee to update and value to update it to
354        fee: FeeType,
355    },
356
357    ///  (Manager or staker only) Update staker
358    ///
359    ///  0. `[w]` Stake pool
360    ///  1. `[s]` Manager or current staker
361    ///  2. '[]` New staker pubkey
362    SetStaker,
363
364    ///   Deposit SOL directly into the pool's reserve account. The output is a
365    ///   "pool" token representing ownership into the pool. Inputs are
366    ///   converted to the current ratio.
367    ///
368    ///   0. `[w]` Stake pool
369    ///   1. `[]` Stake pool withdraw authority
370    ///   2. `[w]` Reserve stake account, to deposit SOL
371    ///   3. `[s]` Account providing the lamports to be deposited into the pool
372    ///   4. `[w]` User account to receive pool tokens
373    ///   5. `[w]` Account to receive fee tokens
374    ///   6. `[w]` Account to receive a portion of fee as referral fees
375    ///   7. `[w]` Pool token mint account
376    ///   8. `[]` System program account
377    ///   9. `[]` Token program id
378    ///  10. `[s]` (Optional) Stake pool sol deposit authority.
379    DepositSol(u64),
380
381    ///  (Manager only) Update SOL deposit, stake deposit, or SOL withdrawal
382    /// authority.
383    ///
384    ///  0. `[w]` Stake pool
385    ///  1. `[s]` Manager
386    ///  2. '[]` New authority pubkey or none
387    SetFundingAuthority(FundingType),
388
389    ///   Withdraw SOL directly from the pool's reserve account. Fails if the
390    ///   reserve does not have enough SOL.
391    ///
392    ///   0. `[w]` Stake pool
393    ///   1. `[]` Stake pool withdraw authority
394    ///   2. `[s]` User transfer authority, for pool token account
395    ///   3. `[w]` User account to burn pool tokens
396    ///   4. `[w]` Reserve stake account, to withdraw SOL
397    ///   5. `[w]` Account receiving the lamports from the reserve, must be a
398    ///      system account
399    ///   6. `[w]` Account to receive pool fee tokens
400    ///   7. `[w]` Pool token mint account
401    ///   8. '[]' Clock sysvar
402    ///   9. '[]' Stake history sysvar
403    ///  10. `[]` Stake program account
404    ///  11. `[]` Token program id
405    ///  12. `[s]` (Optional) Stake pool sol withdraw authority
406    WithdrawSol(u64),
407
408    /// Create token metadata for the stake-pool token in the
409    /// metaplex-token program
410    /// 0. `[]` Stake pool
411    /// 1. `[s]` Manager
412    /// 2. `[]` Stake pool withdraw authority
413    /// 3. `[]` Pool token mint account
414    /// 4. `[s, w]` Payer for creation of token metadata account
415    /// 5. `[w]` Token metadata account
416    /// 6. `[]` Metadata program id
417    /// 7. `[]` System program id
418    CreateTokenMetadata {
419        /// Token name
420        name: String,
421        /// Token symbol e.g. `stkSOL`
422        symbol: String,
423        /// URI of the uploaded metadata of the spl-token
424        uri: String,
425    },
426    /// Update token metadata for the stake-pool token in the
427    /// metaplex-token program
428    ///
429    /// 0. `[]` Stake pool
430    /// 1. `[s]` Manager
431    /// 2. `[]` Stake pool withdraw authority
432    /// 3. `[w]` Token metadata account
433    /// 4. `[]` Metadata program id
434    UpdateTokenMetadata {
435        /// Token name
436        name: String,
437        /// Token symbol e.g. `stkSOL`
438        symbol: String,
439        /// URI of the uploaded metadata of the spl-token
440        uri: String,
441    },
442
443    /// (Staker only) Increase stake on a validator again in an epoch.
444    ///
445    /// Works regardless if the transient stake account exists.
446    ///
447    /// Internally, this instruction splits reserve stake into an ephemeral
448    /// stake account, activates it, then merges or splits it into the
449    /// transient stake account delegated to the appropriate validator.
450    /// `UpdateValidatorListBalance` will do the work of merging once it's
451    /// ready.
452    ///
453    /// The minimum amount to move is rent-exemption plus
454    /// `max(crate::MINIMUM_ACTIVE_STAKE,
455    /// solana_program::stake::tools::get_minimum_delegation())`.
456    ///
457    ///  0. `[]` Stake pool
458    ///  1. `[s]` Stake pool staker
459    ///  2. `[]` Stake pool withdraw authority
460    ///  3. `[w]` Validator list
461    ///  4. `[w]` Stake pool reserve stake
462    ///  5. `[w]` Uninitialized ephemeral stake account to receive stake
463    ///  6. `[w]` Transient stake account
464    ///  7. `[]` Validator stake account
465    ///  8. `[]` Validator vote account to delegate to
466    ///  9. '[]' Clock sysvar
467    /// 10. `[]` Stake History sysvar
468    /// 11. `[]` Stake Config sysvar
469    /// 12. `[]` System program
470    /// 13. `[]` Stake program
471    ///
472    /// User data: amount of lamports to increase on the given validator.
473    ///
474    /// The actual amount split into the transient stake account is:
475    /// `lamports + stake_rent_exemption`.
476    ///
477    /// The rent-exemption of the stake account is withdrawn back to the
478    /// reserve after it is merged.
479    IncreaseAdditionalValidatorStake {
480        /// amount of lamports to increase on the given validator
481        lamports: u64,
482        /// seed used to create transient stake account
483        transient_stake_seed: u64,
484        /// seed used to create ephemeral account.
485        ephemeral_stake_seed: u64,
486    },
487
488    /// (Staker only) Decrease active stake again from a validator, eventually
489    /// moving it to the reserve
490    ///
491    /// Works regardless if the transient stake account already exists.
492    ///
493    /// Internally, this instruction:
494    ///  * withdraws rent-exempt reserve lamports from the reserve into the
495    ///    ephemeral stake
496    ///  * splits a validator stake account into an ephemeral stake account
497    ///  * deactivates the ephemeral account
498    ///  * merges or splits the ephemeral account into the transient stake
499    ///    account delegated to the appropriate validator
500    ///
501    ///  The amount of lamports to move must be at least
502    /// `max(crate::MINIMUM_ACTIVE_STAKE,
503    /// solana_program::stake::tools::get_minimum_delegation())`.
504    ///
505    ///  0. `[]` Stake pool
506    ///  1. `[s]` Stake pool staker
507    ///  2. `[]` Stake pool withdraw authority
508    ///  3. `[w]` Validator list
509    ///  4. `[w]` Reserve stake account, to fund rent exempt reserve
510    ///  5. `[w]` Canonical stake account to split from
511    ///  6. `[w]` Uninitialized ephemeral stake account to receive stake
512    ///  7. `[w]` Transient stake account
513    ///  8. `[]` Clock sysvar
514    ///  9. '[]' Stake history sysvar
515    /// 10. `[]` System program
516    /// 11. `[]` Stake program
517    DecreaseAdditionalValidatorStake {
518        /// amount of lamports to split into the transient stake account
519        lamports: u64,
520        /// seed used to create transient stake account
521        transient_stake_seed: u64,
522        /// seed used to create ephemeral account.
523        ephemeral_stake_seed: u64,
524    },
525
526    /// (Staker only) Decrease active stake on a validator, eventually moving it
527    /// to the reserve
528    ///
529    /// Internally, this instruction:
530    /// * withdraws enough lamports to make the transient account rent-exempt
531    /// * splits from a validator stake account into a transient stake account
532    /// * deactivates the transient stake account
533    ///
534    /// In order to rebalance the pool without taking custody, the staker needs
535    /// a way of reducing the stake on a stake account. This instruction splits
536    /// some amount of stake, up to the total activated stake, from the
537    /// canonical validator stake account, into its "transient" stake
538    /// account.
539    ///
540    /// The instruction only succeeds if the transient stake account does not
541    /// exist. The amount of lamports to move must be at least rent-exemption
542    /// plus `max(crate::MINIMUM_ACTIVE_STAKE,
543    /// solana_program::stake::tools::get_minimum_delegation())`.
544    ///
545    ///  0. `[]` Stake pool
546    ///  1. `[s]` Stake pool staker
547    ///  2. `[]` Stake pool withdraw authority
548    ///  3. `[w]` Validator list
549    ///  4. `[w]` Reserve stake account, to fund rent exempt reserve
550    ///  5. `[w]` Canonical stake account to split from
551    ///  6. `[w]` Transient stake account to receive split
552    ///  7. `[]` Clock sysvar
553    ///  8. '[]' Stake history sysvar
554    ///  9. `[]` System program
555    /// 10. `[]` Stake program
556    DecreaseValidatorStakeWithReserve {
557        /// amount of lamports to split into the transient stake account
558        lamports: u64,
559        /// seed used to create transient stake account
560        transient_stake_seed: u64,
561    },
562
563    /// (Staker only) Redelegate active stake on a validator, eventually moving
564    /// it to another
565    ///
566    /// Internally, this instruction splits a validator stake account into its
567    /// corresponding transient stake account, redelegates it to an ephemeral
568    /// stake account, then merges that stake into the destination transient
569    /// stake account.
570    ///
571    /// In order to rebalance the pool without taking custody, the staker needs
572    /// a way of reducing the stake on a stake account. This instruction splits
573    /// some amount of stake, up to the total activated stake, from the
574    /// canonical validator stake account, into its "transient" stake
575    /// account.
576    ///
577    /// The instruction only succeeds if the source transient stake account and
578    /// ephemeral stake account do not exist.
579    ///
580    /// The amount of lamports to move must be at least rent-exemption plus the
581    /// minimum delegation amount. Rent-exemption plus minimum delegation
582    /// is required for the destination ephemeral stake account.
583    ///
584    /// The rent-exemption for the source transient account comes from the stake
585    /// pool reserve, if needed.
586    ///
587    /// The amount that arrives at the destination validator in the end is
588    /// `redelegate_lamports - rent_exemption` if the destination transient
589    /// account does *not* exist, and `redelegate_lamports` if the destination
590    /// transient account already exists. The `rent_exemption` is not activated
591    /// when creating the destination transient stake account, but if it already
592    /// exists, then the full amount is delegated.
593    ///
594    ///  0. `[]` Stake pool
595    ///  1. `[s]` Stake pool staker
596    ///  2. `[]` Stake pool withdraw authority
597    ///  3. `[w]` Validator list
598    ///  4. `[w]` Reserve stake account, to withdraw rent exempt reserve
599    ///  5. `[w]` Source canonical stake account to split from
600    ///  6. `[w]` Source transient stake account to receive split and be
601    ///     redelegated
602    ///  7. `[w]` Uninitialized ephemeral stake account to receive redelegation
603    ///  8. `[w]` Destination transient stake account to receive ephemeral stake
604    ///     by merge
605    ///  9. `[]` Destination stake account to receive transient stake after
606    ///     activation
607    /// 10. `[]` Destination validator vote account
608    /// 11. `[]` Clock sysvar
609    /// 12. `[]` Stake History sysvar
610    /// 13. `[]` Stake Config sysvar
611    /// 14. `[]` System program
612    /// 15. `[]` Stake program
613    #[deprecated(
614        since = "2.0.0",
615        note = "The stake redelegate instruction used in this will not be enabled."
616    )]
617    Redelegate {
618        /// Amount of lamports to redelegate
619        #[allow(dead_code)] // but it's not
620        lamports: u64,
621        /// Seed used to create source transient stake account
622        #[allow(dead_code)] // but it's not
623        source_transient_stake_seed: u64,
624        /// Seed used to create destination ephemeral account.
625        #[allow(dead_code)] // but it's not
626        ephemeral_stake_seed: u64,
627        /// Seed used to create destination transient stake account. If there is
628        /// already transient stake, this must match the current seed, otherwise
629        /// it can be anything
630        #[allow(dead_code)] // but it's not
631        destination_transient_stake_seed: u64,
632    },
633
634    ///   Deposit some stake into the pool, with a specified slippage
635    ///   constraint. The output is a "pool" token representing ownership
636    ///   into the pool. Inputs are converted at the current ratio.
637    ///
638    ///   0. `[w]` Stake pool
639    ///   1. `[w]` Validator stake list storage account
640    ///   2. `[s]/[]` Stake pool deposit authority
641    ///   3. `[]` Stake pool withdraw authority
642    ///   4. `[w]` Stake account to join the pool (withdraw authority for the
643    ///      stake account should be first set to the stake pool deposit
644    ///      authority)
645    ///   5. `[w]` Validator stake account for the stake account to be merged
646    ///      with
647    ///   6. `[w]` Reserve stake account, to withdraw rent exempt reserve
648    ///   7. `[w]` User account to receive pool tokens
649    ///   8. `[w]` Account to receive pool fee tokens
650    ///   9. `[w]` Account to receive a portion of pool fee tokens as referral
651    ///      fees
652    ///   10. `[w]` Pool token mint account
653    ///   11. '[]' Sysvar clock account
654    ///   12. '[]' Sysvar stake history account
655    ///   13. `[]` Pool token program id,
656    ///   14. `[]` Stake program id,
657    DepositStakeWithSlippage {
658        /// Minimum amount of pool tokens that must be received
659        minimum_pool_tokens_out: u64,
660    },
661
662    ///   Withdraw the token from the pool at the current ratio, specifying a
663    ///   minimum expected output lamport amount.
664    ///
665    ///   Succeeds if the stake account has enough SOL to cover the desired
666    ///   amount of pool tokens, and if the withdrawal keeps the total
667    ///   staked amount above the minimum of rent-exempt amount plus `max(
668    ///     crate::MINIMUM_ACTIVE_STAKE,
669    ///     solana_program::stake::tools::get_minimum_delegation()
670    ///   )`.
671    ///
672    ///   0. `[w]` Stake pool
673    ///   1. `[w]` Validator stake list storage account
674    ///   2. `[]` Stake pool withdraw authority
675    ///   3. `[w]` Validator or reserve stake account to split
676    ///   4. `[w]` Uninitialized stake account to receive withdrawal
677    ///   5. `[]` User account to set as a new withdraw authority
678    ///   6. `[s]` User transfer authority, for pool token account
679    ///   7. `[w]` User account with pool tokens to burn from
680    ///   8. `[w]` Account to receive pool fee tokens
681    ///   9. `[w]` Pool token mint account
682    ///  10. `[]` Sysvar clock account (required)
683    ///  11. `[]` Pool token program id
684    ///  12. `[]` Stake program id,
685    ///
686    ///  User data: amount of pool tokens to withdraw
687    WithdrawStakeWithSlippage {
688        /// Pool tokens to burn in exchange for lamports
689        pool_tokens_in: u64,
690        /// Minimum amount of lamports that must be received
691        minimum_lamports_out: u64,
692    },
693
694    ///   Deposit SOL directly into the pool's reserve account, with a
695    ///   specified slippage constraint. The output is a "pool" token
696    ///   representing ownership into the pool. Inputs are converted at the
697    ///   current ratio.
698    ///
699    ///   0. `[w]` Stake pool
700    ///   1. `[]` Stake pool withdraw authority
701    ///   2. `[w]` Reserve stake account, to deposit SOL
702    ///   3. `[s]` Account providing the lamports to be deposited into the pool
703    ///   4. `[w]` User account to receive pool tokens
704    ///   5. `[w]` Account to receive fee tokens
705    ///   6. `[w]` Account to receive a portion of fee as referral fees
706    ///   7. `[w]` Pool token mint account
707    ///   8. `[]` System program account
708    ///   9. `[]` Token program id
709    ///  10. `[s]` (Optional) Stake pool sol deposit authority.
710    DepositSolWithSlippage {
711        /// Amount of lamports to deposit into the reserve
712        lamports_in: u64,
713        /// Minimum amount of pool tokens that must be received
714        minimum_pool_tokens_out: u64,
715    },
716
717    ///   Withdraw SOL directly from the pool's reserve account. Fails if the
718    ///   reserve does not have enough SOL or if the slippage constraint is not
719    ///   met.
720    ///
721    ///   0. `[w]` Stake pool
722    ///   1. `[]` Stake pool withdraw authority
723    ///   2. `[s]` User transfer authority, for pool token account
724    ///   3. `[w]` User account to burn pool tokens
725    ///   4. `[w]` Reserve stake account, to withdraw SOL
726    ///   5. `[w]` Account receiving the lamports from the reserve, must be a
727    ///      system account
728    ///   6. `[w]` Account to receive pool fee tokens
729    ///   7. `[w]` Pool token mint account
730    ///   8. '[]' Clock sysvar
731    ///   9. '[]' Stake history sysvar
732    ///  10. `[]` Stake program account
733    ///  11. `[]` Token program id
734    ///  12. `[s]` (Optional) Stake pool sol withdraw authority
735    WithdrawSolWithSlippage {
736        /// Pool tokens to burn in exchange for lamports
737        pool_tokens_in: u64,
738        /// Minimum amount of lamports that must be received
739        minimum_lamports_out: u64,
740    },
741
742    ///   Deposit wrapped SOL via a Fogo session.
743    ///
744    ///   0. `[w]` Stake pool
745    ///   1. `[]` Stake pool withdraw authority
746    ///   2. `[w]` Reserve stake account
747    ///   3. `[s]` Signer or Session wSOL source account
748    ///   4. `[w]` User's destination pool token account (ATA)
749    ///   5. `[w]` Manager fee account
750    ///   6. `[w]` Referrer fee account
751    ///   7. `[w]` Pool token mint
752    ///   8. `[]` System Program
753    ///   9. `[]` Token Program
754    ///  10. `[]` Native mint (wSOL)
755    ///  11. `[w]` wSOL token account owned by the user
756    ///  12. `[w]` Transient wSOL token account
757    ///  13. `[w]` Session Program Signer
758    ///  14. `[s]` Payer (Paymaster)
759    ///  15. `[]` User wallet (owner of the ATA)
760    ///  16. `[s]` (Optional) Stake pool SOL deposit authority
761    ///  17. `[]` Associated Token Program
762    DepositWsolWithSession {
763        /// Amount of lamports to deposit
764        lamports_in: u64,
765        /// Minimum amount of pool tokens that must be received
766        minimum_pool_tokens_out: u64,
767    },
768
769    ///   Withdraw wrapped SOL via a Fogo session.
770    ///
771    ///   0. `[w]` Stake pool
772    ///   1. `[]` Stake pool withdraw authority
773    ///   2. `[s]` Signer or Session wSOL source account
774    ///   3. `[w]` User account to burn pool tokens
775    ///   4. `[w]` Reserve stake account, to withdraw SOL
776    ///   5. `[w]` Account receiving the wSOL from the reserve, must be a token account
777    ///   6. `[w]` Account to receive pool fee tokens
778    ///   7. `[w]` Pool token mint account
779    ///   8. `[]` Clock sysvar
780    ///   9. `[]` Stake history sysvar
781    ///  10. `[]` Stake program account
782    ///  11. `[]` Token program id
783    ///  12. `[]` Native mint (wSOL)
784    ///  13. `[w]` Session Program Signer
785    ///  14. `[]` User wallet (owner of the ATA)
786    ///  15. `[]` System Program
787    ///  16. `[s]` (Optional) Stake pool SOL withdraw authority
788    ///  17. `[]` Associated Token Program
789    WithdrawWsolWithSession {
790        /// Pool tokens to burn in exchange for lamports
791        pool_tokens_in: u64,
792        /// Minimum amount of lamports that must be received
793        minimum_lamports_out: u64,
794    },
795
796    ///   Withdraw stake via a Fogo session.
797    ///
798    ///   0. `[w]` Stake pool
799    ///   1. `[w]` Validator stake list storage account
800    ///   2. `[]` Stake pool withdraw authority
801    ///   3. `[w]` Validator or reserve stake account to split
802    ///   4. `[w]` Uninitialized stake account to receive withdrawal (must be user stake PDA)
803    ///   5. `[s]` Signer or Session (user_stake_authority)
804    ///   6. `[]` User transfer authority (same as account 5 for session path)
805    ///   7. `[w]` User account with pool tokens to burn from
806    ///   8. `[w]` Account to receive pool fee tokens
807    ///   9. `[w]` Pool token mint account
808    ///  10. `[]` Clock sysvar
809    ///  11. `[]` Token program id
810    ///  12. `[]` Stake program id
811    ///  13. `[]` Program signer PDA
812    ///  14. `[]` System program
813    ///  15. `[w]` Reserve stake account (to fund rent)
814    ///  16. `[]` Stake history sysvar
815    WithdrawStakeWithSession {
816        /// Pool tokens to burn in exchange for stake
817        pool_tokens_in: u64,
818        /// Minimum amount of lamports that must be received
819        minimum_lamports_out: u64,
820        /// Seed for the user stake account PDA
821        user_stake_seed: u64,
822    },
823
824    ///   Withdraw lamports from a user stake account via a Fogo session.
825    ///   Used after cooldown period to convert deactivated stake to SOL.
826    ///
827    ///   0. `[w]` User stake account (must be user stake PDA, fully deactivated)
828    ///   1. `[w]` Recipient account for withdrawn lamports (must be session user)
829    ///   2. `[]` Clock sysvar
830    ///   3. `[]` Stake history sysvar
831    ///   4. `[s]` Signer or Session (stake authority)
832    ///   5. `[]` Stake program
833    WithdrawFromStakeAccountWithSession {
834        /// Amount of lamports to withdraw (u64::MAX for full withdrawal)
835        lamports: u64,
836        /// Seed for the user stake account PDA
837        user_stake_seed: u64,
838    },
839}
840
841/// Creates an `Initialize` instruction.
842pub fn initialize(
843    program_id: &Pubkey,
844    stake_pool: &Pubkey,
845    manager: &Pubkey,
846    staker: &Pubkey,
847    stake_pool_withdraw_authority: &Pubkey,
848    validator_list: &Pubkey,
849    reserve_stake: &Pubkey,
850    pool_mint: &Pubkey,
851    manager_pool_account: &Pubkey,
852    token_program_id: &Pubkey,
853    deposit_authority: Option<Pubkey>,
854    fee: Fee,
855    withdrawal_fee: Fee,
856    deposit_fee: Fee,
857    referral_fee: u8,
858    max_validators: u32,
859) -> Instruction {
860    let init_data = StakePoolInstruction::Initialize {
861        fee,
862        withdrawal_fee,
863        deposit_fee,
864        referral_fee,
865        max_validators,
866    };
867    let data = borsh::to_vec(&init_data).unwrap();
868    let mut accounts = vec![
869        AccountMeta::new(*stake_pool, false),
870        AccountMeta::new_readonly(*manager, true),
871        AccountMeta::new_readonly(*staker, false),
872        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
873        AccountMeta::new(*validator_list, false),
874        AccountMeta::new_readonly(*reserve_stake, false),
875        AccountMeta::new(*pool_mint, false),
876        AccountMeta::new(*manager_pool_account, false),
877        AccountMeta::new_readonly(*token_program_id, false),
878    ];
879    if let Some(deposit_authority) = deposit_authority {
880        accounts.push(AccountMeta::new_readonly(deposit_authority, true));
881    }
882    Instruction {
883        program_id: *program_id,
884        accounts,
885        data,
886    }
887}
888
889/// Creates `AddValidatorToPool` instruction (add new validator stake account to
890/// the pool)
891pub fn add_validator_to_pool(
892    program_id: &Pubkey,
893    stake_pool: &Pubkey,
894    staker: &Pubkey,
895    reserve: &Pubkey,
896    stake_pool_withdraw: &Pubkey,
897    validator_list: &Pubkey,
898    stake: &Pubkey,
899    validator: &Pubkey,
900    seed: Option<NonZeroU32>,
901) -> Instruction {
902    let accounts = vec![
903        AccountMeta::new(*stake_pool, false),
904        AccountMeta::new_readonly(*staker, true),
905        AccountMeta::new(*reserve, false),
906        AccountMeta::new_readonly(*stake_pool_withdraw, false),
907        AccountMeta::new(*validator_list, false),
908        AccountMeta::new(*stake, false),
909        AccountMeta::new_readonly(*validator, false),
910        AccountMeta::new_readonly(sysvar::rent::id(), false),
911        AccountMeta::new_readonly(sysvar::clock::id(), false),
912        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
913        #[allow(deprecated)]
914        AccountMeta::new_readonly(stake::config::id(), false),
915        AccountMeta::new_readonly(system_program::id(), false),
916        AccountMeta::new_readonly(stake::program::id(), false),
917    ];
918    let data = borsh::to_vec(&StakePoolInstruction::AddValidatorToPool(
919        seed.map(|s| s.get()).unwrap_or(0),
920    ))
921    .unwrap();
922    Instruction {
923        program_id: *program_id,
924        accounts,
925        data,
926    }
927}
928
929/// Creates `RemoveValidatorFromPool` instruction (remove validator stake
930/// account from the pool)
931pub fn remove_validator_from_pool(
932    program_id: &Pubkey,
933    stake_pool: &Pubkey,
934    staker: &Pubkey,
935    stake_pool_withdraw: &Pubkey,
936    validator_list: &Pubkey,
937    stake_account: &Pubkey,
938    transient_stake_account: &Pubkey,
939) -> Instruction {
940    let accounts = vec![
941        AccountMeta::new(*stake_pool, false),
942        AccountMeta::new_readonly(*staker, true),
943        AccountMeta::new_readonly(*stake_pool_withdraw, false),
944        AccountMeta::new(*validator_list, false),
945        AccountMeta::new(*stake_account, false),
946        AccountMeta::new(*transient_stake_account, false),
947        AccountMeta::new_readonly(sysvar::clock::id(), false),
948        AccountMeta::new_readonly(stake::program::id(), false),
949    ];
950    Instruction {
951        program_id: *program_id,
952        accounts,
953        data: borsh::to_vec(&StakePoolInstruction::RemoveValidatorFromPool).unwrap(),
954    }
955}
956
957/// Creates `DecreaseValidatorStake` instruction (rebalance from validator
958/// account to transient account)
959#[deprecated(
960    since = "0.7.0",
961    note = "please use `decrease_validator_stake_with_reserve`"
962)]
963pub fn decrease_validator_stake(
964    program_id: &Pubkey,
965    stake_pool: &Pubkey,
966    staker: &Pubkey,
967    stake_pool_withdraw_authority: &Pubkey,
968    validator_list: &Pubkey,
969    validator_stake: &Pubkey,
970    transient_stake: &Pubkey,
971    lamports: u64,
972    transient_stake_seed: u64,
973) -> Instruction {
974    let accounts = vec![
975        AccountMeta::new_readonly(*stake_pool, false),
976        AccountMeta::new_readonly(*staker, true),
977        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
978        AccountMeta::new(*validator_list, false),
979        AccountMeta::new(*validator_stake, false),
980        AccountMeta::new(*transient_stake, false),
981        AccountMeta::new_readonly(sysvar::clock::id(), false),
982        AccountMeta::new_readonly(sysvar::rent::id(), false),
983        AccountMeta::new_readonly(system_program::id(), false),
984        AccountMeta::new_readonly(stake::program::id(), false),
985    ];
986    Instruction {
987        program_id: *program_id,
988        accounts,
989        data: borsh::to_vec(&StakePoolInstruction::DecreaseValidatorStake {
990            lamports,
991            transient_stake_seed,
992        })
993        .unwrap(),
994    }
995}
996
997/// Creates `DecreaseAdditionalValidatorStake` instruction (rebalance from
998/// validator account to transient account)
999pub fn decrease_additional_validator_stake(
1000    program_id: &Pubkey,
1001    stake_pool: &Pubkey,
1002    staker: &Pubkey,
1003    stake_pool_withdraw_authority: &Pubkey,
1004    validator_list: &Pubkey,
1005    reserve_stake: &Pubkey,
1006    validator_stake: &Pubkey,
1007    ephemeral_stake: &Pubkey,
1008    transient_stake: &Pubkey,
1009    lamports: u64,
1010    transient_stake_seed: u64,
1011    ephemeral_stake_seed: u64,
1012) -> Instruction {
1013    let accounts = vec![
1014        AccountMeta::new_readonly(*stake_pool, false),
1015        AccountMeta::new_readonly(*staker, true),
1016        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
1017        AccountMeta::new(*validator_list, false),
1018        AccountMeta::new(*reserve_stake, false),
1019        AccountMeta::new(*validator_stake, false),
1020        AccountMeta::new(*ephemeral_stake, false),
1021        AccountMeta::new(*transient_stake, false),
1022        AccountMeta::new_readonly(sysvar::clock::id(), false),
1023        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
1024        AccountMeta::new_readonly(system_program::id(), false),
1025        AccountMeta::new_readonly(stake::program::id(), false),
1026    ];
1027    Instruction {
1028        program_id: *program_id,
1029        accounts,
1030        data: borsh::to_vec(&StakePoolInstruction::DecreaseAdditionalValidatorStake {
1031            lamports,
1032            transient_stake_seed,
1033            ephemeral_stake_seed,
1034        })
1035        .unwrap(),
1036    }
1037}
1038
1039/// Creates `DecreaseValidatorStakeWithReserve` instruction (rebalance from
1040/// validator account to transient account)
1041pub fn decrease_validator_stake_with_reserve(
1042    program_id: &Pubkey,
1043    stake_pool: &Pubkey,
1044    staker: &Pubkey,
1045    stake_pool_withdraw_authority: &Pubkey,
1046    validator_list: &Pubkey,
1047    reserve_stake: &Pubkey,
1048    validator_stake: &Pubkey,
1049    transient_stake: &Pubkey,
1050    lamports: u64,
1051    transient_stake_seed: u64,
1052) -> Instruction {
1053    let accounts = vec![
1054        AccountMeta::new_readonly(*stake_pool, false),
1055        AccountMeta::new_readonly(*staker, true),
1056        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
1057        AccountMeta::new(*validator_list, false),
1058        AccountMeta::new(*reserve_stake, false),
1059        AccountMeta::new(*validator_stake, false),
1060        AccountMeta::new(*transient_stake, false),
1061        AccountMeta::new_readonly(sysvar::clock::id(), false),
1062        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
1063        AccountMeta::new_readonly(system_program::id(), false),
1064        AccountMeta::new_readonly(stake::program::id(), false),
1065    ];
1066    Instruction {
1067        program_id: *program_id,
1068        accounts,
1069        data: borsh::to_vec(&StakePoolInstruction::DecreaseValidatorStakeWithReserve {
1070            lamports,
1071            transient_stake_seed,
1072        })
1073        .unwrap(),
1074    }
1075}
1076
1077/// Creates `IncreaseValidatorStake` instruction (rebalance from reserve account
1078/// to transient account)
1079pub fn increase_validator_stake(
1080    program_id: &Pubkey,
1081    stake_pool: &Pubkey,
1082    staker: &Pubkey,
1083    stake_pool_withdraw_authority: &Pubkey,
1084    validator_list: &Pubkey,
1085    reserve_stake: &Pubkey,
1086    transient_stake: &Pubkey,
1087    validator_stake: &Pubkey,
1088    validator: &Pubkey,
1089    lamports: u64,
1090    transient_stake_seed: u64,
1091) -> Instruction {
1092    let accounts = vec![
1093        AccountMeta::new_readonly(*stake_pool, false),
1094        AccountMeta::new_readonly(*staker, true),
1095        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
1096        AccountMeta::new(*validator_list, false),
1097        AccountMeta::new(*reserve_stake, false),
1098        AccountMeta::new(*transient_stake, false),
1099        AccountMeta::new_readonly(*validator_stake, false),
1100        AccountMeta::new_readonly(*validator, false),
1101        AccountMeta::new_readonly(sysvar::clock::id(), false),
1102        AccountMeta::new_readonly(sysvar::rent::id(), false),
1103        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
1104        #[allow(deprecated)]
1105        AccountMeta::new_readonly(stake::config::id(), false),
1106        AccountMeta::new_readonly(system_program::id(), false),
1107        AccountMeta::new_readonly(stake::program::id(), false),
1108    ];
1109    Instruction {
1110        program_id: *program_id,
1111        accounts,
1112        data: borsh::to_vec(&StakePoolInstruction::IncreaseValidatorStake {
1113            lamports,
1114            transient_stake_seed,
1115        })
1116        .unwrap(),
1117    }
1118}
1119
1120/// Creates `IncreaseAdditionalValidatorStake` instruction (rebalance from
1121/// reserve account to transient account)
1122pub fn increase_additional_validator_stake(
1123    program_id: &Pubkey,
1124    stake_pool: &Pubkey,
1125    staker: &Pubkey,
1126    stake_pool_withdraw_authority: &Pubkey,
1127    validator_list: &Pubkey,
1128    reserve_stake: &Pubkey,
1129    ephemeral_stake: &Pubkey,
1130    transient_stake: &Pubkey,
1131    validator_stake: &Pubkey,
1132    validator: &Pubkey,
1133    lamports: u64,
1134    transient_stake_seed: u64,
1135    ephemeral_stake_seed: u64,
1136) -> Instruction {
1137    let accounts = vec![
1138        AccountMeta::new_readonly(*stake_pool, false),
1139        AccountMeta::new_readonly(*staker, true),
1140        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
1141        AccountMeta::new(*validator_list, false),
1142        AccountMeta::new(*reserve_stake, false),
1143        AccountMeta::new(*ephemeral_stake, false),
1144        AccountMeta::new(*transient_stake, false),
1145        AccountMeta::new_readonly(*validator_stake, false),
1146        AccountMeta::new_readonly(*validator, false),
1147        AccountMeta::new_readonly(sysvar::clock::id(), false),
1148        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
1149        #[allow(deprecated)]
1150        AccountMeta::new_readonly(stake::config::id(), false),
1151        AccountMeta::new_readonly(system_program::id(), false),
1152        AccountMeta::new_readonly(stake::program::id(), false),
1153    ];
1154    Instruction {
1155        program_id: *program_id,
1156        accounts,
1157        data: borsh::to_vec(&StakePoolInstruction::IncreaseAdditionalValidatorStake {
1158            lamports,
1159            transient_stake_seed,
1160            ephemeral_stake_seed,
1161        })
1162        .unwrap(),
1163    }
1164}
1165
1166/// Creates `Redelegate` instruction (rebalance from one validator account to
1167/// another)
1168#[deprecated(
1169    since = "2.0.0",
1170    note = "The stake redelegate instruction used in this will not be enabled."
1171)]
1172pub fn redelegate(
1173    program_id: &Pubkey,
1174    stake_pool: &Pubkey,
1175    staker: &Pubkey,
1176    stake_pool_withdraw_authority: &Pubkey,
1177    validator_list: &Pubkey,
1178    reserve_stake: &Pubkey,
1179    source_validator_stake: &Pubkey,
1180    source_transient_stake: &Pubkey,
1181    ephemeral_stake: &Pubkey,
1182    destination_transient_stake: &Pubkey,
1183    destination_validator_stake: &Pubkey,
1184    validator: &Pubkey,
1185    lamports: u64,
1186    source_transient_stake_seed: u64,
1187    ephemeral_stake_seed: u64,
1188    destination_transient_stake_seed: u64,
1189) -> Instruction {
1190    let accounts = vec![
1191        AccountMeta::new_readonly(*stake_pool, false),
1192        AccountMeta::new_readonly(*staker, true),
1193        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
1194        AccountMeta::new(*validator_list, false),
1195        AccountMeta::new(*reserve_stake, false),
1196        AccountMeta::new(*source_validator_stake, false),
1197        AccountMeta::new(*source_transient_stake, false),
1198        AccountMeta::new(*ephemeral_stake, false),
1199        AccountMeta::new(*destination_transient_stake, false),
1200        AccountMeta::new_readonly(*destination_validator_stake, false),
1201        AccountMeta::new_readonly(*validator, false),
1202        AccountMeta::new_readonly(sysvar::clock::id(), false),
1203        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
1204        #[allow(deprecated)]
1205        AccountMeta::new_readonly(stake::config::id(), false),
1206        AccountMeta::new_readonly(system_program::id(), false),
1207        AccountMeta::new_readonly(stake::program::id(), false),
1208    ];
1209    Instruction {
1210        program_id: *program_id,
1211        accounts,
1212        data: borsh::to_vec(&StakePoolInstruction::Redelegate {
1213            lamports,
1214            source_transient_stake_seed,
1215            ephemeral_stake_seed,
1216            destination_transient_stake_seed,
1217        })
1218        .unwrap(),
1219    }
1220}
1221
1222/// Creates `SetPreferredDepositValidator` instruction
1223pub fn set_preferred_validator(
1224    program_id: &Pubkey,
1225    stake_pool_address: &Pubkey,
1226    staker: &Pubkey,
1227    validator_list_address: &Pubkey,
1228    validator_type: PreferredValidatorType,
1229    validator_vote_address: Option<Pubkey>,
1230) -> Instruction {
1231    Instruction {
1232        program_id: *program_id,
1233        accounts: vec![
1234            AccountMeta::new(*stake_pool_address, false),
1235            AccountMeta::new_readonly(*staker, true),
1236            AccountMeta::new_readonly(*validator_list_address, false),
1237        ],
1238        data: borsh::to_vec(&StakePoolInstruction::SetPreferredValidator {
1239            validator_type,
1240            validator_vote_address,
1241        })
1242        .unwrap(),
1243    }
1244}
1245
1246/// Create an `AddValidatorToPool` instruction given an existing stake pool and
1247/// vote account
1248pub fn add_validator_to_pool_with_vote(
1249    program_id: &Pubkey,
1250    stake_pool: &StakePool,
1251    stake_pool_address: &Pubkey,
1252    vote_account_address: &Pubkey,
1253    seed: Option<NonZeroU32>,
1254) -> Instruction {
1255    let pool_withdraw_authority =
1256        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1257    let (stake_account_address, _) =
1258        find_stake_program_address(program_id, vote_account_address, stake_pool_address, seed);
1259    add_validator_to_pool(
1260        program_id,
1261        stake_pool_address,
1262        &stake_pool.staker,
1263        &stake_pool.reserve_stake,
1264        &pool_withdraw_authority,
1265        &stake_pool.validator_list,
1266        &stake_account_address,
1267        vote_account_address,
1268        seed,
1269    )
1270}
1271
1272/// Create an `RemoveValidatorFromPool` instruction given an existing stake pool
1273/// and vote account
1274pub fn remove_validator_from_pool_with_vote(
1275    program_id: &Pubkey,
1276    stake_pool: &StakePool,
1277    stake_pool_address: &Pubkey,
1278    vote_account_address: &Pubkey,
1279    validator_stake_seed: Option<NonZeroU32>,
1280    transient_stake_seed: u64,
1281) -> Instruction {
1282    let pool_withdraw_authority =
1283        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1284    let (stake_account_address, _) = find_stake_program_address(
1285        program_id,
1286        vote_account_address,
1287        stake_pool_address,
1288        validator_stake_seed,
1289    );
1290    let (transient_stake_account, _) = find_transient_stake_program_address(
1291        program_id,
1292        vote_account_address,
1293        stake_pool_address,
1294        transient_stake_seed,
1295    );
1296    remove_validator_from_pool(
1297        program_id,
1298        stake_pool_address,
1299        &stake_pool.staker,
1300        &pool_withdraw_authority,
1301        &stake_pool.validator_list,
1302        &stake_account_address,
1303        &transient_stake_account,
1304    )
1305}
1306
1307/// Create an `IncreaseValidatorStake` instruction given an existing stake pool
1308/// and vote account
1309pub fn increase_validator_stake_with_vote(
1310    program_id: &Pubkey,
1311    stake_pool: &StakePool,
1312    stake_pool_address: &Pubkey,
1313    vote_account_address: &Pubkey,
1314    lamports: u64,
1315    validator_stake_seed: Option<NonZeroU32>,
1316    transient_stake_seed: u64,
1317) -> Instruction {
1318    let pool_withdraw_authority =
1319        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1320    let (transient_stake_address, _) = find_transient_stake_program_address(
1321        program_id,
1322        vote_account_address,
1323        stake_pool_address,
1324        transient_stake_seed,
1325    );
1326    let (validator_stake_address, _) = find_stake_program_address(
1327        program_id,
1328        vote_account_address,
1329        stake_pool_address,
1330        validator_stake_seed,
1331    );
1332
1333    increase_validator_stake(
1334        program_id,
1335        stake_pool_address,
1336        &stake_pool.staker,
1337        &pool_withdraw_authority,
1338        &stake_pool.validator_list,
1339        &stake_pool.reserve_stake,
1340        &transient_stake_address,
1341        &validator_stake_address,
1342        vote_account_address,
1343        lamports,
1344        transient_stake_seed,
1345    )
1346}
1347
1348/// Create an `IncreaseAdditionalValidatorStake` instruction given an existing
1349/// stake pool and vote account
1350pub fn increase_additional_validator_stake_with_vote(
1351    program_id: &Pubkey,
1352    stake_pool: &StakePool,
1353    stake_pool_address: &Pubkey,
1354    vote_account_address: &Pubkey,
1355    lamports: u64,
1356    validator_stake_seed: Option<NonZeroU32>,
1357    transient_stake_seed: u64,
1358    ephemeral_stake_seed: u64,
1359) -> Instruction {
1360    let pool_withdraw_authority =
1361        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1362    let (ephemeral_stake_address, _) =
1363        find_ephemeral_stake_program_address(program_id, stake_pool_address, ephemeral_stake_seed);
1364    let (transient_stake_address, _) = find_transient_stake_program_address(
1365        program_id,
1366        vote_account_address,
1367        stake_pool_address,
1368        transient_stake_seed,
1369    );
1370    let (validator_stake_address, _) = find_stake_program_address(
1371        program_id,
1372        vote_account_address,
1373        stake_pool_address,
1374        validator_stake_seed,
1375    );
1376
1377    increase_additional_validator_stake(
1378        program_id,
1379        stake_pool_address,
1380        &stake_pool.staker,
1381        &pool_withdraw_authority,
1382        &stake_pool.validator_list,
1383        &stake_pool.reserve_stake,
1384        &ephemeral_stake_address,
1385        &transient_stake_address,
1386        &validator_stake_address,
1387        vote_account_address,
1388        lamports,
1389        transient_stake_seed,
1390        ephemeral_stake_seed,
1391    )
1392}
1393
1394/// Create a `DecreaseValidatorStake` instruction given an existing stake pool
1395/// and vote account
1396pub fn decrease_validator_stake_with_vote(
1397    program_id: &Pubkey,
1398    stake_pool: &StakePool,
1399    stake_pool_address: &Pubkey,
1400    vote_account_address: &Pubkey,
1401    lamports: u64,
1402    validator_stake_seed: Option<NonZeroU32>,
1403    transient_stake_seed: u64,
1404) -> Instruction {
1405    let pool_withdraw_authority =
1406        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1407    let (validator_stake_address, _) = find_stake_program_address(
1408        program_id,
1409        vote_account_address,
1410        stake_pool_address,
1411        validator_stake_seed,
1412    );
1413    let (transient_stake_address, _) = find_transient_stake_program_address(
1414        program_id,
1415        vote_account_address,
1416        stake_pool_address,
1417        transient_stake_seed,
1418    );
1419    decrease_validator_stake_with_reserve(
1420        program_id,
1421        stake_pool_address,
1422        &stake_pool.staker,
1423        &pool_withdraw_authority,
1424        &stake_pool.validator_list,
1425        &stake_pool.reserve_stake,
1426        &validator_stake_address,
1427        &transient_stake_address,
1428        lamports,
1429        transient_stake_seed,
1430    )
1431}
1432
1433/// Create a `IncreaseAdditionalValidatorStake` instruction given an existing
1434/// stake pool, validator list and vote account
1435pub fn increase_additional_validator_stake_with_list(
1436    program_id: &Pubkey,
1437    stake_pool: &StakePool,
1438    validator_list: &ValidatorList,
1439    stake_pool_address: &Pubkey,
1440    vote_account_address: &Pubkey,
1441    lamports: u64,
1442    ephemeral_stake_seed: u64,
1443) -> Result<Instruction, ProgramError> {
1444    let validator_info = validator_list
1445        .find(vote_account_address)
1446        .ok_or(ProgramError::InvalidInstructionData)?;
1447    let transient_stake_seed = u64::from(validator_info.transient_seed_suffix);
1448    let validator_stake_seed = NonZeroU32::new(validator_info.validator_seed_suffix.into());
1449    Ok(increase_additional_validator_stake_with_vote(
1450        program_id,
1451        stake_pool,
1452        stake_pool_address,
1453        vote_account_address,
1454        lamports,
1455        validator_stake_seed,
1456        transient_stake_seed,
1457        ephemeral_stake_seed,
1458    ))
1459}
1460
1461/// Create a `DecreaseAdditionalValidatorStake` instruction given an existing
1462/// stake pool, validator list and vote account
1463pub fn decrease_additional_validator_stake_with_list(
1464    program_id: &Pubkey,
1465    stake_pool: &StakePool,
1466    validator_list: &ValidatorList,
1467    stake_pool_address: &Pubkey,
1468    vote_account_address: &Pubkey,
1469    lamports: u64,
1470    ephemeral_stake_seed: u64,
1471) -> Result<Instruction, ProgramError> {
1472    let validator_info = validator_list
1473        .find(vote_account_address)
1474        .ok_or(ProgramError::InvalidInstructionData)?;
1475    let transient_stake_seed = u64::from(validator_info.transient_seed_suffix);
1476    let validator_stake_seed = NonZeroU32::new(validator_info.validator_seed_suffix.into());
1477    Ok(decrease_additional_validator_stake_with_vote(
1478        program_id,
1479        stake_pool,
1480        stake_pool_address,
1481        vote_account_address,
1482        lamports,
1483        validator_stake_seed,
1484        transient_stake_seed,
1485        ephemeral_stake_seed,
1486    ))
1487}
1488
1489/// Create a `DecreaseAdditionalValidatorStake` instruction given an existing
1490/// stake pool and vote account
1491pub fn decrease_additional_validator_stake_with_vote(
1492    program_id: &Pubkey,
1493    stake_pool: &StakePool,
1494    stake_pool_address: &Pubkey,
1495    vote_account_address: &Pubkey,
1496    lamports: u64,
1497    validator_stake_seed: Option<NonZeroU32>,
1498    transient_stake_seed: u64,
1499    ephemeral_stake_seed: u64,
1500) -> Instruction {
1501    let pool_withdraw_authority =
1502        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1503    let (validator_stake_address, _) = find_stake_program_address(
1504        program_id,
1505        vote_account_address,
1506        stake_pool_address,
1507        validator_stake_seed,
1508    );
1509    let (ephemeral_stake_address, _) =
1510        find_ephemeral_stake_program_address(program_id, stake_pool_address, ephemeral_stake_seed);
1511    let (transient_stake_address, _) = find_transient_stake_program_address(
1512        program_id,
1513        vote_account_address,
1514        stake_pool_address,
1515        transient_stake_seed,
1516    );
1517    decrease_additional_validator_stake(
1518        program_id,
1519        stake_pool_address,
1520        &stake_pool.staker,
1521        &pool_withdraw_authority,
1522        &stake_pool.validator_list,
1523        &stake_pool.reserve_stake,
1524        &validator_stake_address,
1525        &ephemeral_stake_address,
1526        &transient_stake_address,
1527        lamports,
1528        transient_stake_seed,
1529        ephemeral_stake_seed,
1530    )
1531}
1532
1533/// Creates `UpdateValidatorListBalance` instruction (update validator stake
1534/// account balances)
1535#[deprecated(
1536    since = "1.1.0",
1537    note = "please use `update_validator_list_balance_chunk`"
1538)]
1539pub fn update_validator_list_balance(
1540    program_id: &Pubkey,
1541    stake_pool: &Pubkey,
1542    stake_pool_withdraw_authority: &Pubkey,
1543    validator_list_address: &Pubkey,
1544    reserve_stake: &Pubkey,
1545    validator_list: &ValidatorList,
1546    validator_vote_accounts: &[Pubkey],
1547    start_index: u32,
1548    no_merge: bool,
1549) -> Instruction {
1550    let mut accounts = vec![
1551        AccountMeta::new_readonly(*stake_pool, false),
1552        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
1553        AccountMeta::new(*validator_list_address, false),
1554        AccountMeta::new(*reserve_stake, false),
1555        AccountMeta::new_readonly(sysvar::clock::id(), false),
1556        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
1557        AccountMeta::new_readonly(stake::program::id(), false),
1558    ];
1559    accounts.append(
1560        &mut validator_vote_accounts
1561            .iter()
1562            .flat_map(|vote_account_address| {
1563                let validator_stake_info = validator_list.find(vote_account_address);
1564                if let Some(validator_stake_info) = validator_stake_info {
1565                    let (validator_stake_account, _) = find_stake_program_address(
1566                        program_id,
1567                        vote_account_address,
1568                        stake_pool,
1569                        NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()),
1570                    );
1571                    let (transient_stake_account, _) = find_transient_stake_program_address(
1572                        program_id,
1573                        vote_account_address,
1574                        stake_pool,
1575                        validator_stake_info.transient_seed_suffix.into(),
1576                    );
1577                    vec![
1578                        AccountMeta::new(validator_stake_account, false),
1579                        AccountMeta::new(transient_stake_account, false),
1580                    ]
1581                } else {
1582                    vec![]
1583                }
1584            })
1585            .collect::<Vec<AccountMeta>>(),
1586    );
1587    Instruction {
1588        program_id: *program_id,
1589        accounts,
1590        data: borsh::to_vec(&StakePoolInstruction::UpdateValidatorListBalance {
1591            start_index,
1592            no_merge,
1593        })
1594        .unwrap(),
1595    }
1596}
1597
1598/// Creates an `UpdateValidatorListBalance` instruction (update validator stake
1599/// account balances) to update `validator_list[start_index..start_index +
1600/// len]`.
1601///
1602/// Returns `Err(ProgramError::InvalidInstructionData)` if:
1603/// - `start_index..start_index + len` is out of bounds for
1604///   `validator_list.validators`
1605pub fn update_validator_list_balance_chunk(
1606    program_id: &Pubkey,
1607    stake_pool: &Pubkey,
1608    stake_pool_withdraw_authority: &Pubkey,
1609    validator_list_address: &Pubkey,
1610    reserve_stake: &Pubkey,
1611    validator_list: &ValidatorList,
1612    len: usize,
1613    start_index: usize,
1614    no_merge: bool,
1615) -> Result<Instruction, ProgramError> {
1616    let mut accounts = vec![
1617        AccountMeta::new_readonly(*stake_pool, false),
1618        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
1619        AccountMeta::new(*validator_list_address, false),
1620        AccountMeta::new(*reserve_stake, false),
1621        AccountMeta::new_readonly(sysvar::clock::id(), false),
1622        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
1623        AccountMeta::new_readonly(stake::program::id(), false),
1624    ];
1625    let validator_list_subslice = validator_list
1626        .validators
1627        .get(start_index..start_index.saturating_add(len))
1628        .ok_or(ProgramError::InvalidInstructionData)?;
1629    accounts.extend(validator_list_subslice.iter().flat_map(
1630        |ValidatorStakeInfo {
1631             vote_account_address,
1632             validator_seed_suffix,
1633             transient_seed_suffix,
1634             ..
1635         }| {
1636            let (validator_stake_account, _) = find_stake_program_address(
1637                program_id,
1638                vote_account_address,
1639                stake_pool,
1640                NonZeroU32::new((*validator_seed_suffix).into()),
1641            );
1642            let (transient_stake_account, _) = find_transient_stake_program_address(
1643                program_id,
1644                vote_account_address,
1645                stake_pool,
1646                (*transient_seed_suffix).into(),
1647            );
1648            [
1649                AccountMeta::new(validator_stake_account, false),
1650                AccountMeta::new(transient_stake_account, false),
1651            ]
1652        },
1653    ));
1654    Ok(Instruction {
1655        program_id: *program_id,
1656        accounts,
1657        data: borsh::to_vec(&StakePoolInstruction::UpdateValidatorListBalance {
1658            start_index: start_index.try_into().unwrap(),
1659            no_merge,
1660        })
1661        .unwrap(),
1662    })
1663}
1664
1665/// Creates `UpdateValidatorListBalance` instruction (update validator stake
1666/// account balances)
1667///
1668/// Returns `None` if all validators in the given chunk has already been updated
1669/// for this epoch, returns the required instruction otherwise.
1670pub fn update_stale_validator_list_balance_chunk(
1671    program_id: &Pubkey,
1672    stake_pool: &Pubkey,
1673    stake_pool_withdraw_authority: &Pubkey,
1674    validator_list_address: &Pubkey,
1675    reserve_stake: &Pubkey,
1676    validator_list: &ValidatorList,
1677    len: usize,
1678    start_index: usize,
1679    no_merge: bool,
1680    current_epoch: Epoch,
1681) -> Result<Option<Instruction>, ProgramError> {
1682    let validator_list_subslice = validator_list
1683        .validators
1684        .get(start_index..start_index.saturating_add(len))
1685        .ok_or(ProgramError::InvalidInstructionData)?;
1686    if validator_list_subslice.iter().all(|info| {
1687        let last_update_epoch: u64 = info.last_update_epoch.into();
1688        last_update_epoch >= current_epoch
1689    }) {
1690        return Ok(None);
1691    }
1692    update_validator_list_balance_chunk(
1693        program_id,
1694        stake_pool,
1695        stake_pool_withdraw_authority,
1696        validator_list_address,
1697        reserve_stake,
1698        validator_list,
1699        len,
1700        start_index,
1701        no_merge,
1702    )
1703    .map(Some)
1704}
1705
1706/// Creates `UpdateStakePoolBalance` instruction (pool balance from the stake
1707/// account list balances)
1708pub fn update_stake_pool_balance(
1709    program_id: &Pubkey,
1710    stake_pool: &Pubkey,
1711    withdraw_authority: &Pubkey,
1712    validator_list_storage: &Pubkey,
1713    reserve_stake: &Pubkey,
1714    manager_fee_account: &Pubkey,
1715    stake_pool_mint: &Pubkey,
1716    token_program_id: &Pubkey,
1717) -> Instruction {
1718    let accounts = vec![
1719        AccountMeta::new(*stake_pool, false),
1720        AccountMeta::new_readonly(*withdraw_authority, false),
1721        AccountMeta::new(*validator_list_storage, false),
1722        AccountMeta::new_readonly(*reserve_stake, false),
1723        AccountMeta::new(*manager_fee_account, false),
1724        AccountMeta::new(*stake_pool_mint, false),
1725        AccountMeta::new_readonly(*token_program_id, false),
1726    ];
1727    Instruction {
1728        program_id: *program_id,
1729        accounts,
1730        data: borsh::to_vec(&StakePoolInstruction::UpdateStakePoolBalance).unwrap(),
1731    }
1732}
1733
1734/// Creates `CleanupRemovedValidatorEntries` instruction (removes entries from
1735/// the validator list)
1736pub fn cleanup_removed_validator_entries(
1737    program_id: &Pubkey,
1738    stake_pool: &Pubkey,
1739    validator_list_storage: &Pubkey,
1740) -> Instruction {
1741    let accounts = vec![
1742        AccountMeta::new(*stake_pool, false),
1743        AccountMeta::new(*validator_list_storage, false),
1744    ];
1745    Instruction {
1746        program_id: *program_id,
1747        accounts,
1748        data: borsh::to_vec(&StakePoolInstruction::CleanupRemovedValidatorEntries).unwrap(),
1749    }
1750}
1751
1752/// Creates all `UpdateValidatorListBalance` and `UpdateStakePoolBalance`
1753/// instructions for fully updating a stake pool each epoch
1754pub fn update_stake_pool(
1755    program_id: &Pubkey,
1756    stake_pool: &StakePool,
1757    validator_list: &ValidatorList,
1758    stake_pool_address: &Pubkey,
1759    no_merge: bool,
1760) -> (Vec<Instruction>, Vec<Instruction>) {
1761    let (withdraw_authority, _) =
1762        find_withdraw_authority_program_address(program_id, stake_pool_address);
1763
1764    let update_list_instructions = validator_list
1765        .validators
1766        .chunks(MAX_VALIDATORS_TO_UPDATE)
1767        .enumerate()
1768        .map(|(i, chunk)| {
1769            // unwrap-safety: chunk len and offset are derived
1770            update_validator_list_balance_chunk(
1771                program_id,
1772                stake_pool_address,
1773                &withdraw_authority,
1774                &stake_pool.validator_list,
1775                &stake_pool.reserve_stake,
1776                validator_list,
1777                chunk.len(),
1778                i.saturating_mul(MAX_VALIDATORS_TO_UPDATE),
1779                no_merge,
1780            )
1781            .unwrap()
1782        })
1783        .collect();
1784
1785    let final_instructions = vec![
1786        update_stake_pool_balance(
1787            program_id,
1788            stake_pool_address,
1789            &withdraw_authority,
1790            &stake_pool.validator_list,
1791            &stake_pool.reserve_stake,
1792            &stake_pool.manager_fee_account,
1793            &stake_pool.pool_mint,
1794            &stake_pool.token_program_id,
1795        ),
1796        cleanup_removed_validator_entries(
1797            program_id,
1798            stake_pool_address,
1799            &stake_pool.validator_list,
1800        ),
1801    ];
1802    (update_list_instructions, final_instructions)
1803}
1804
1805/// Creates the `UpdateValidatorListBalance` instructions only for validators on
1806/// `validator_list` that have not been updated for this epoch, and the
1807/// `UpdateStakePoolBalance` instruction for fully updating the stake pool.
1808///
1809/// Basically same as [`update_stake_pool`], but skips validators that are
1810/// already updated for this epoch
1811pub fn update_stale_stake_pool(
1812    program_id: &Pubkey,
1813    stake_pool: &StakePool,
1814    validator_list: &ValidatorList,
1815    stake_pool_address: &Pubkey,
1816    no_merge: bool,
1817    current_epoch: Epoch,
1818) -> (Vec<Instruction>, Vec<Instruction>) {
1819    let (withdraw_authority, _) =
1820        find_withdraw_authority_program_address(program_id, stake_pool_address);
1821
1822    let update_list_instructions = validator_list
1823        .validators
1824        .chunks(MAX_VALIDATORS_TO_UPDATE)
1825        .enumerate()
1826        .filter_map(|(i, chunk)| {
1827            // unwrap-safety: chunk len and offset are derived
1828            update_stale_validator_list_balance_chunk(
1829                program_id,
1830                stake_pool_address,
1831                &withdraw_authority,
1832                &stake_pool.validator_list,
1833                &stake_pool.reserve_stake,
1834                validator_list,
1835                chunk.len(),
1836                i.saturating_mul(MAX_VALIDATORS_TO_UPDATE),
1837                no_merge,
1838                current_epoch,
1839            )
1840            .unwrap()
1841        })
1842        .collect();
1843
1844    let final_instructions = vec![
1845        update_stake_pool_balance(
1846            program_id,
1847            stake_pool_address,
1848            &withdraw_authority,
1849            &stake_pool.validator_list,
1850            &stake_pool.reserve_stake,
1851            &stake_pool.manager_fee_account,
1852            &stake_pool.pool_mint,
1853            &stake_pool.token_program_id,
1854        ),
1855        cleanup_removed_validator_entries(
1856            program_id,
1857            stake_pool_address,
1858            &stake_pool.validator_list,
1859        ),
1860    ];
1861    (update_list_instructions, final_instructions)
1862}
1863
1864fn deposit_stake_internal(
1865    program_id: &Pubkey,
1866    stake_pool: &Pubkey,
1867    validator_list_storage: &Pubkey,
1868    stake_pool_deposit_authority: Option<&Pubkey>,
1869    stake_pool_withdraw_authority: &Pubkey,
1870    deposit_stake_address: &Pubkey,
1871    deposit_stake_withdraw_authority: &Pubkey,
1872    validator_stake_account: &Pubkey,
1873    reserve_stake_account: &Pubkey,
1874    pool_tokens_to: &Pubkey,
1875    manager_fee_account: &Pubkey,
1876    referrer_pool_tokens_account: &Pubkey,
1877    pool_mint: &Pubkey,
1878    token_program_id: &Pubkey,
1879    minimum_pool_tokens_out: Option<u64>,
1880) -> Vec<Instruction> {
1881    let mut instructions = vec![];
1882    let mut accounts = vec![
1883        AccountMeta::new(*stake_pool, false),
1884        AccountMeta::new(*validator_list_storage, false),
1885    ];
1886    if let Some(stake_pool_deposit_authority) = stake_pool_deposit_authority {
1887        accounts.push(AccountMeta::new_readonly(
1888            *stake_pool_deposit_authority,
1889            true,
1890        ));
1891        instructions.extend_from_slice(&[
1892            stake::instruction::authorize(
1893                deposit_stake_address,
1894                deposit_stake_withdraw_authority,
1895                stake_pool_deposit_authority,
1896                stake::state::StakeAuthorize::Staker,
1897                None,
1898            ),
1899            stake::instruction::authorize(
1900                deposit_stake_address,
1901                deposit_stake_withdraw_authority,
1902                stake_pool_deposit_authority,
1903                stake::state::StakeAuthorize::Withdrawer,
1904                None,
1905            ),
1906        ]);
1907    } else {
1908        let stake_pool_deposit_authority =
1909            find_deposit_authority_program_address(program_id, stake_pool).0;
1910        accounts.push(AccountMeta::new_readonly(
1911            stake_pool_deposit_authority,
1912            false,
1913        ));
1914        instructions.extend_from_slice(&[
1915            stake::instruction::authorize(
1916                deposit_stake_address,
1917                deposit_stake_withdraw_authority,
1918                &stake_pool_deposit_authority,
1919                stake::state::StakeAuthorize::Staker,
1920                None,
1921            ),
1922            stake::instruction::authorize(
1923                deposit_stake_address,
1924                deposit_stake_withdraw_authority,
1925                &stake_pool_deposit_authority,
1926                stake::state::StakeAuthorize::Withdrawer,
1927                None,
1928            ),
1929        ]);
1930    };
1931
1932    accounts.extend_from_slice(&[
1933        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
1934        AccountMeta::new(*deposit_stake_address, false),
1935        AccountMeta::new(*validator_stake_account, false),
1936        AccountMeta::new(*reserve_stake_account, false),
1937        AccountMeta::new(*pool_tokens_to, false),
1938        AccountMeta::new(*manager_fee_account, false),
1939        AccountMeta::new(*referrer_pool_tokens_account, false),
1940        AccountMeta::new(*pool_mint, false),
1941        AccountMeta::new_readonly(sysvar::clock::id(), false),
1942        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
1943        AccountMeta::new_readonly(*token_program_id, false),
1944        AccountMeta::new_readonly(stake::program::id(), false),
1945    ]);
1946    instructions.push(
1947        if let Some(minimum_pool_tokens_out) = minimum_pool_tokens_out {
1948            Instruction {
1949                program_id: *program_id,
1950                accounts,
1951                data: borsh::to_vec(&StakePoolInstruction::DepositStakeWithSlippage {
1952                    minimum_pool_tokens_out,
1953                })
1954                .unwrap(),
1955            }
1956        } else {
1957            Instruction {
1958                program_id: *program_id,
1959                accounts,
1960                data: borsh::to_vec(&StakePoolInstruction::DepositStake).unwrap(),
1961            }
1962        },
1963    );
1964    instructions
1965}
1966
1967/// Creates instructions required to deposit into a stake pool, given a stake
1968/// account owned by the user.
1969pub fn deposit_stake(
1970    program_id: &Pubkey,
1971    stake_pool: &Pubkey,
1972    validator_list_storage: &Pubkey,
1973    stake_pool_withdraw_authority: &Pubkey,
1974    deposit_stake_address: &Pubkey,
1975    deposit_stake_withdraw_authority: &Pubkey,
1976    validator_stake_account: &Pubkey,
1977    reserve_stake_account: &Pubkey,
1978    pool_tokens_to: &Pubkey,
1979    manager_fee_account: &Pubkey,
1980    referrer_pool_tokens_account: &Pubkey,
1981    pool_mint: &Pubkey,
1982    token_program_id: &Pubkey,
1983) -> Vec<Instruction> {
1984    deposit_stake_internal(
1985        program_id,
1986        stake_pool,
1987        validator_list_storage,
1988        None,
1989        stake_pool_withdraw_authority,
1990        deposit_stake_address,
1991        deposit_stake_withdraw_authority,
1992        validator_stake_account,
1993        reserve_stake_account,
1994        pool_tokens_to,
1995        manager_fee_account,
1996        referrer_pool_tokens_account,
1997        pool_mint,
1998        token_program_id,
1999        None,
2000    )
2001}
2002
2003/// Creates instructions to deposit into a stake pool with slippage
2004pub fn deposit_stake_with_slippage(
2005    program_id: &Pubkey,
2006    stake_pool: &Pubkey,
2007    validator_list_storage: &Pubkey,
2008    stake_pool_withdraw_authority: &Pubkey,
2009    deposit_stake_address: &Pubkey,
2010    deposit_stake_withdraw_authority: &Pubkey,
2011    validator_stake_account: &Pubkey,
2012    reserve_stake_account: &Pubkey,
2013    pool_tokens_to: &Pubkey,
2014    manager_fee_account: &Pubkey,
2015    referrer_pool_tokens_account: &Pubkey,
2016    pool_mint: &Pubkey,
2017    token_program_id: &Pubkey,
2018    minimum_pool_tokens_out: u64,
2019) -> Vec<Instruction> {
2020    deposit_stake_internal(
2021        program_id,
2022        stake_pool,
2023        validator_list_storage,
2024        None,
2025        stake_pool_withdraw_authority,
2026        deposit_stake_address,
2027        deposit_stake_withdraw_authority,
2028        validator_stake_account,
2029        reserve_stake_account,
2030        pool_tokens_to,
2031        manager_fee_account,
2032        referrer_pool_tokens_account,
2033        pool_mint,
2034        token_program_id,
2035        Some(minimum_pool_tokens_out),
2036    )
2037}
2038
2039/// Creates instructions required to deposit into a stake pool, given a stake
2040/// account owned by the user. The difference with `deposit()` is that a deposit
2041/// authority must sign this instruction, which is required for private pools.
2042pub fn deposit_stake_with_authority(
2043    program_id: &Pubkey,
2044    stake_pool: &Pubkey,
2045    validator_list_storage: &Pubkey,
2046    stake_pool_deposit_authority: &Pubkey,
2047    stake_pool_withdraw_authority: &Pubkey,
2048    deposit_stake_address: &Pubkey,
2049    deposit_stake_withdraw_authority: &Pubkey,
2050    validator_stake_account: &Pubkey,
2051    reserve_stake_account: &Pubkey,
2052    pool_tokens_to: &Pubkey,
2053    manager_fee_account: &Pubkey,
2054    referrer_pool_tokens_account: &Pubkey,
2055    pool_mint: &Pubkey,
2056    token_program_id: &Pubkey,
2057) -> Vec<Instruction> {
2058    deposit_stake_internal(
2059        program_id,
2060        stake_pool,
2061        validator_list_storage,
2062        Some(stake_pool_deposit_authority),
2063        stake_pool_withdraw_authority,
2064        deposit_stake_address,
2065        deposit_stake_withdraw_authority,
2066        validator_stake_account,
2067        reserve_stake_account,
2068        pool_tokens_to,
2069        manager_fee_account,
2070        referrer_pool_tokens_account,
2071        pool_mint,
2072        token_program_id,
2073        None,
2074    )
2075}
2076
2077/// Creates instructions required to deposit into a stake pool with slippage,
2078/// given a stake account owned by the user. The difference with `deposit()` is
2079/// that a deposit authority must sign this instruction, which is required for
2080/// private pools.
2081pub fn deposit_stake_with_authority_and_slippage(
2082    program_id: &Pubkey,
2083    stake_pool: &Pubkey,
2084    validator_list_storage: &Pubkey,
2085    stake_pool_deposit_authority: &Pubkey,
2086    stake_pool_withdraw_authority: &Pubkey,
2087    deposit_stake_address: &Pubkey,
2088    deposit_stake_withdraw_authority: &Pubkey,
2089    validator_stake_account: &Pubkey,
2090    reserve_stake_account: &Pubkey,
2091    pool_tokens_to: &Pubkey,
2092    manager_fee_account: &Pubkey,
2093    referrer_pool_tokens_account: &Pubkey,
2094    pool_mint: &Pubkey,
2095    token_program_id: &Pubkey,
2096    minimum_pool_tokens_out: u64,
2097) -> Vec<Instruction> {
2098    deposit_stake_internal(
2099        program_id,
2100        stake_pool,
2101        validator_list_storage,
2102        Some(stake_pool_deposit_authority),
2103        stake_pool_withdraw_authority,
2104        deposit_stake_address,
2105        deposit_stake_withdraw_authority,
2106        validator_stake_account,
2107        reserve_stake_account,
2108        pool_tokens_to,
2109        manager_fee_account,
2110        referrer_pool_tokens_account,
2111        pool_mint,
2112        token_program_id,
2113        Some(minimum_pool_tokens_out),
2114    )
2115}
2116
2117/// Creates instructions required to deposit SOL directly into a stake pool.
2118fn deposit_sol_internal(
2119    program_id: &Pubkey,
2120    stake_pool: &Pubkey,
2121    stake_pool_withdraw_authority: &Pubkey,
2122    reserve_stake_account: &Pubkey,
2123    lamports_from: &Pubkey,
2124    pool_tokens_to: &Pubkey,
2125    manager_fee_account: &Pubkey,
2126    referrer_pool_tokens_account: &Pubkey,
2127    pool_mint: &Pubkey,
2128    token_program_id: &Pubkey,
2129    sol_deposit_authority: Option<&Pubkey>,
2130    lamports_in: u64,
2131    minimum_pool_tokens_out: Option<u64>,
2132) -> Instruction {
2133    let mut accounts = vec![
2134        AccountMeta::new(*stake_pool, false),
2135        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
2136        AccountMeta::new(*reserve_stake_account, false),
2137        AccountMeta::new(*lamports_from, true),
2138        AccountMeta::new(*pool_tokens_to, false),
2139        AccountMeta::new(*manager_fee_account, false),
2140        AccountMeta::new(*referrer_pool_tokens_account, false),
2141        AccountMeta::new(*pool_mint, false),
2142        AccountMeta::new_readonly(system_program::id(), false),
2143        AccountMeta::new_readonly(*token_program_id, false),
2144    ];
2145    if let Some(sol_deposit_authority) = sol_deposit_authority {
2146        accounts.push(AccountMeta::new_readonly(*sol_deposit_authority, true));
2147    }
2148    if let Some(minimum_pool_tokens_out) = minimum_pool_tokens_out {
2149        Instruction {
2150            program_id: *program_id,
2151            accounts,
2152            data: borsh::to_vec(&StakePoolInstruction::DepositSolWithSlippage {
2153                lamports_in,
2154                minimum_pool_tokens_out,
2155            })
2156            .unwrap(),
2157        }
2158    } else {
2159        Instruction {
2160            program_id: *program_id,
2161            accounts,
2162            data: borsh::to_vec(&StakePoolInstruction::DepositSol(lamports_in)).unwrap(),
2163        }
2164    }
2165}
2166
2167/// Creates instruction to deposit SOL directly into a stake pool.
2168pub fn deposit_sol(
2169    program_id: &Pubkey,
2170    stake_pool: &Pubkey,
2171    stake_pool_withdraw_authority: &Pubkey,
2172    reserve_stake_account: &Pubkey,
2173    lamports_from: &Pubkey,
2174    pool_tokens_to: &Pubkey,
2175    manager_fee_account: &Pubkey,
2176    referrer_pool_tokens_account: &Pubkey,
2177    pool_mint: &Pubkey,
2178    token_program_id: &Pubkey,
2179    lamports_in: u64,
2180) -> Instruction {
2181    deposit_sol_internal(
2182        program_id,
2183        stake_pool,
2184        stake_pool_withdraw_authority,
2185        reserve_stake_account,
2186        lamports_from,
2187        pool_tokens_to,
2188        manager_fee_account,
2189        referrer_pool_tokens_account,
2190        pool_mint,
2191        token_program_id,
2192        None,
2193        lamports_in,
2194        None,
2195    )
2196}
2197
2198/// Creates instruction to deposit SOL directly into a stake pool with slippage
2199/// constraint.
2200pub fn deposit_sol_with_slippage(
2201    program_id: &Pubkey,
2202    stake_pool: &Pubkey,
2203    stake_pool_withdraw_authority: &Pubkey,
2204    reserve_stake_account: &Pubkey,
2205    lamports_from: &Pubkey,
2206    pool_tokens_to: &Pubkey,
2207    manager_fee_account: &Pubkey,
2208    referrer_pool_tokens_account: &Pubkey,
2209    pool_mint: &Pubkey,
2210    token_program_id: &Pubkey,
2211    lamports_in: u64,
2212    minimum_pool_tokens_out: u64,
2213) -> Instruction {
2214    deposit_sol_internal(
2215        program_id,
2216        stake_pool,
2217        stake_pool_withdraw_authority,
2218        reserve_stake_account,
2219        lamports_from,
2220        pool_tokens_to,
2221        manager_fee_account,
2222        referrer_pool_tokens_account,
2223        pool_mint,
2224        token_program_id,
2225        None,
2226        lamports_in,
2227        Some(minimum_pool_tokens_out),
2228    )
2229}
2230
2231/// Creates instruction required to deposit SOL directly into a stake pool.
2232/// The difference with `deposit_sol()` is that a deposit
2233/// authority must sign this instruction.
2234pub fn deposit_sol_with_authority(
2235    program_id: &Pubkey,
2236    stake_pool: &Pubkey,
2237    sol_deposit_authority: &Pubkey,
2238    stake_pool_withdraw_authority: &Pubkey,
2239    reserve_stake_account: &Pubkey,
2240    lamports_from: &Pubkey,
2241    pool_tokens_to: &Pubkey,
2242    manager_fee_account: &Pubkey,
2243    referrer_pool_tokens_account: &Pubkey,
2244    pool_mint: &Pubkey,
2245    token_program_id: &Pubkey,
2246    lamports_in: u64,
2247) -> Instruction {
2248    deposit_sol_internal(
2249        program_id,
2250        stake_pool,
2251        stake_pool_withdraw_authority,
2252        reserve_stake_account,
2253        lamports_from,
2254        pool_tokens_to,
2255        manager_fee_account,
2256        referrer_pool_tokens_account,
2257        pool_mint,
2258        token_program_id,
2259        Some(sol_deposit_authority),
2260        lamports_in,
2261        None,
2262    )
2263}
2264
2265/// Creates instruction to deposit SOL directly into a stake pool with slippage
2266/// constraint.
2267pub fn deposit_sol_with_authority_and_slippage(
2268    program_id: &Pubkey,
2269    stake_pool: &Pubkey,
2270    sol_deposit_authority: &Pubkey,
2271    stake_pool_withdraw_authority: &Pubkey,
2272    reserve_stake_account: &Pubkey,
2273    lamports_from: &Pubkey,
2274    pool_tokens_to: &Pubkey,
2275    manager_fee_account: &Pubkey,
2276    referrer_pool_tokens_account: &Pubkey,
2277    pool_mint: &Pubkey,
2278    token_program_id: &Pubkey,
2279    lamports_in: u64,
2280    minimum_pool_tokens_out: u64,
2281) -> Instruction {
2282    deposit_sol_internal(
2283        program_id,
2284        stake_pool,
2285        stake_pool_withdraw_authority,
2286        reserve_stake_account,
2287        lamports_from,
2288        pool_tokens_to,
2289        manager_fee_account,
2290        referrer_pool_tokens_account,
2291        pool_mint,
2292        token_program_id,
2293        Some(sol_deposit_authority),
2294        lamports_in,
2295        Some(minimum_pool_tokens_out),
2296    )
2297}
2298
2299fn withdraw_stake_internal(
2300    program_id: &Pubkey,
2301    stake_pool: &Pubkey,
2302    validator_list_storage: &Pubkey,
2303    stake_pool_withdraw: &Pubkey,
2304    stake_to_split: &Pubkey,
2305    stake_to_receive: &Pubkey,
2306    user_stake_authority: &Pubkey,
2307    user_transfer_authority: &Pubkey,
2308    user_pool_token_account: &Pubkey,
2309    manager_fee_account: &Pubkey,
2310    pool_mint: &Pubkey,
2311    token_program_id: &Pubkey,
2312    pool_tokens_in: u64,
2313    minimum_lamports_out: Option<u64>,
2314) -> Instruction {
2315    let accounts = vec![
2316        AccountMeta::new(*stake_pool, false),
2317        AccountMeta::new(*validator_list_storage, false),
2318        AccountMeta::new_readonly(*stake_pool_withdraw, false),
2319        AccountMeta::new(*stake_to_split, false),
2320        AccountMeta::new(*stake_to_receive, false),
2321        AccountMeta::new_readonly(*user_stake_authority, false),
2322        AccountMeta::new_readonly(*user_transfer_authority, true),
2323        AccountMeta::new(*user_pool_token_account, false),
2324        AccountMeta::new(*manager_fee_account, false),
2325        AccountMeta::new(*pool_mint, false),
2326        AccountMeta::new_readonly(sysvar::clock::id(), false),
2327        AccountMeta::new_readonly(*token_program_id, false),
2328        AccountMeta::new_readonly(stake::program::id(), false),
2329    ];
2330    if let Some(minimum_lamports_out) = minimum_lamports_out {
2331        Instruction {
2332            program_id: *program_id,
2333            accounts,
2334            data: borsh::to_vec(&StakePoolInstruction::WithdrawStakeWithSlippage {
2335                pool_tokens_in,
2336                minimum_lamports_out,
2337            })
2338            .unwrap(),
2339        }
2340    } else {
2341        Instruction {
2342            program_id: *program_id,
2343            accounts,
2344            data: borsh::to_vec(&StakePoolInstruction::WithdrawStake(pool_tokens_in)).unwrap(),
2345        }
2346    }
2347}
2348
2349/// Creates a `WithdrawStake` instruction.
2350pub fn withdraw_stake(
2351    program_id: &Pubkey,
2352    stake_pool: &Pubkey,
2353    validator_list_storage: &Pubkey,
2354    stake_pool_withdraw: &Pubkey,
2355    stake_to_split: &Pubkey,
2356    stake_to_receive: &Pubkey,
2357    user_stake_authority: &Pubkey,
2358    user_transfer_authority: &Pubkey,
2359    user_pool_token_account: &Pubkey,
2360    manager_fee_account: &Pubkey,
2361    pool_mint: &Pubkey,
2362    token_program_id: &Pubkey,
2363    pool_tokens_in: u64,
2364) -> Instruction {
2365    withdraw_stake_internal(
2366        program_id,
2367        stake_pool,
2368        validator_list_storage,
2369        stake_pool_withdraw,
2370        stake_to_split,
2371        stake_to_receive,
2372        user_stake_authority,
2373        user_transfer_authority,
2374        user_pool_token_account,
2375        manager_fee_account,
2376        pool_mint,
2377        token_program_id,
2378        pool_tokens_in,
2379        None,
2380    )
2381}
2382
2383/// Creates a `WithdrawStakeWithSlippage` instruction.
2384pub fn withdraw_stake_with_slippage(
2385    program_id: &Pubkey,
2386    stake_pool: &Pubkey,
2387    validator_list_storage: &Pubkey,
2388    stake_pool_withdraw: &Pubkey,
2389    stake_to_split: &Pubkey,
2390    stake_to_receive: &Pubkey,
2391    user_stake_authority: &Pubkey,
2392    user_transfer_authority: &Pubkey,
2393    user_pool_token_account: &Pubkey,
2394    manager_fee_account: &Pubkey,
2395    pool_mint: &Pubkey,
2396    token_program_id: &Pubkey,
2397    pool_tokens_in: u64,
2398    minimum_lamports_out: u64,
2399) -> Instruction {
2400    withdraw_stake_internal(
2401        program_id,
2402        stake_pool,
2403        validator_list_storage,
2404        stake_pool_withdraw,
2405        stake_to_split,
2406        stake_to_receive,
2407        user_stake_authority,
2408        user_transfer_authority,
2409        user_pool_token_account,
2410        manager_fee_account,
2411        pool_mint,
2412        token_program_id,
2413        pool_tokens_in,
2414        Some(minimum_lamports_out),
2415    )
2416}
2417
2418fn withdraw_sol_internal(
2419    program_id: &Pubkey,
2420    stake_pool: &Pubkey,
2421    stake_pool_withdraw_authority: &Pubkey,
2422    user_transfer_authority: &Pubkey,
2423    pool_tokens_from: &Pubkey,
2424    reserve_stake_account: &Pubkey,
2425    lamports_to: &Pubkey,
2426    manager_fee_account: &Pubkey,
2427    pool_mint: &Pubkey,
2428    token_program_id: &Pubkey,
2429    sol_withdraw_authority: Option<&Pubkey>,
2430    pool_tokens_in: u64,
2431    minimum_lamports_out: Option<u64>,
2432) -> Instruction {
2433    let mut accounts = vec![
2434        AccountMeta::new(*stake_pool, false),
2435        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
2436        AccountMeta::new_readonly(*user_transfer_authority, true),
2437        AccountMeta::new(*pool_tokens_from, false),
2438        AccountMeta::new(*reserve_stake_account, false),
2439        AccountMeta::new(*lamports_to, false),
2440        AccountMeta::new(*manager_fee_account, false),
2441        AccountMeta::new(*pool_mint, false),
2442        AccountMeta::new_readonly(sysvar::clock::id(), false),
2443        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
2444        AccountMeta::new_readonly(stake::program::id(), false),
2445        AccountMeta::new_readonly(*token_program_id, false),
2446    ];
2447    if let Some(sol_withdraw_authority) = sol_withdraw_authority {
2448        accounts.push(AccountMeta::new_readonly(*sol_withdraw_authority, true));
2449    }
2450    if let Some(minimum_lamports_out) = minimum_lamports_out {
2451        Instruction {
2452            program_id: *program_id,
2453            accounts,
2454            data: borsh::to_vec(&StakePoolInstruction::WithdrawSolWithSlippage {
2455                pool_tokens_in,
2456                minimum_lamports_out,
2457            })
2458            .unwrap(),
2459        }
2460    } else {
2461        Instruction {
2462            program_id: *program_id,
2463            accounts,
2464            data: borsh::to_vec(&StakePoolInstruction::WithdrawSol(pool_tokens_in)).unwrap(),
2465        }
2466    }
2467}
2468
2469/// Creates instruction required to withdraw SOL directly from a stake pool.
2470pub fn withdraw_sol(
2471    program_id: &Pubkey,
2472    stake_pool: &Pubkey,
2473    stake_pool_withdraw_authority: &Pubkey,
2474    user_transfer_authority: &Pubkey,
2475    pool_tokens_from: &Pubkey,
2476    reserve_stake_account: &Pubkey,
2477    lamports_to: &Pubkey,
2478    manager_fee_account: &Pubkey,
2479    pool_mint: &Pubkey,
2480    token_program_id: &Pubkey,
2481    pool_tokens_in: u64,
2482) -> Instruction {
2483    withdraw_sol_internal(
2484        program_id,
2485        stake_pool,
2486        stake_pool_withdraw_authority,
2487        user_transfer_authority,
2488        pool_tokens_from,
2489        reserve_stake_account,
2490        lamports_to,
2491        manager_fee_account,
2492        pool_mint,
2493        token_program_id,
2494        None,
2495        pool_tokens_in,
2496        None,
2497    )
2498}
2499
2500/// Creates instruction required to withdraw SOL directly from a stake pool with
2501/// slippage constraints.
2502pub fn withdraw_sol_with_slippage(
2503    program_id: &Pubkey,
2504    stake_pool: &Pubkey,
2505    stake_pool_withdraw_authority: &Pubkey,
2506    user_transfer_authority: &Pubkey,
2507    pool_tokens_from: &Pubkey,
2508    reserve_stake_account: &Pubkey,
2509    lamports_to: &Pubkey,
2510    manager_fee_account: &Pubkey,
2511    pool_mint: &Pubkey,
2512    token_program_id: &Pubkey,
2513    pool_tokens_in: u64,
2514    minimum_lamports_out: u64,
2515) -> Instruction {
2516    withdraw_sol_internal(
2517        program_id,
2518        stake_pool,
2519        stake_pool_withdraw_authority,
2520        user_transfer_authority,
2521        pool_tokens_from,
2522        reserve_stake_account,
2523        lamports_to,
2524        manager_fee_account,
2525        pool_mint,
2526        token_program_id,
2527        None,
2528        pool_tokens_in,
2529        Some(minimum_lamports_out),
2530    )
2531}
2532
2533/// Creates instruction required to withdraw SOL directly from a stake pool.
2534/// The difference with `withdraw_sol()` is that the sol withdraw authority
2535/// must sign this instruction.
2536pub fn withdraw_sol_with_authority(
2537    program_id: &Pubkey,
2538    stake_pool: &Pubkey,
2539    sol_withdraw_authority: &Pubkey,
2540    stake_pool_withdraw_authority: &Pubkey,
2541    user_transfer_authority: &Pubkey,
2542    pool_tokens_from: &Pubkey,
2543    reserve_stake_account: &Pubkey,
2544    lamports_to: &Pubkey,
2545    manager_fee_account: &Pubkey,
2546    pool_mint: &Pubkey,
2547    token_program_id: &Pubkey,
2548    pool_tokens_in: u64,
2549) -> Instruction {
2550    withdraw_sol_internal(
2551        program_id,
2552        stake_pool,
2553        stake_pool_withdraw_authority,
2554        user_transfer_authority,
2555        pool_tokens_from,
2556        reserve_stake_account,
2557        lamports_to,
2558        manager_fee_account,
2559        pool_mint,
2560        token_program_id,
2561        Some(sol_withdraw_authority),
2562        pool_tokens_in,
2563        None,
2564    )
2565}
2566
2567/// Creates instruction required to withdraw SOL directly from a stake pool with
2568/// a slippage constraint.
2569/// The difference with `withdraw_sol()` is that the sol withdraw authority
2570/// must sign this instruction.
2571pub fn withdraw_sol_with_authority_and_slippage(
2572    program_id: &Pubkey,
2573    stake_pool: &Pubkey,
2574    sol_withdraw_authority: &Pubkey,
2575    stake_pool_withdraw_authority: &Pubkey,
2576    user_transfer_authority: &Pubkey,
2577    pool_tokens_from: &Pubkey,
2578    reserve_stake_account: &Pubkey,
2579    lamports_to: &Pubkey,
2580    manager_fee_account: &Pubkey,
2581    pool_mint: &Pubkey,
2582    token_program_id: &Pubkey,
2583    pool_tokens_in: u64,
2584    minimum_lamports_out: u64,
2585) -> Instruction {
2586    withdraw_sol_internal(
2587        program_id,
2588        stake_pool,
2589        stake_pool_withdraw_authority,
2590        user_transfer_authority,
2591        pool_tokens_from,
2592        reserve_stake_account,
2593        lamports_to,
2594        manager_fee_account,
2595        pool_mint,
2596        token_program_id,
2597        Some(sol_withdraw_authority),
2598        pool_tokens_in,
2599        Some(minimum_lamports_out),
2600    )
2601}
2602
2603/// Creates a `SetManager` instruction.
2604pub fn set_manager(
2605    program_id: &Pubkey,
2606    stake_pool: &Pubkey,
2607    manager: &Pubkey,
2608    new_manager: &Pubkey,
2609    new_fee_receiver: &Pubkey,
2610) -> Instruction {
2611    let accounts = vec![
2612        AccountMeta::new(*stake_pool, false),
2613        AccountMeta::new_readonly(*manager, true),
2614        AccountMeta::new_readonly(*new_manager, true),
2615        AccountMeta::new_readonly(*new_fee_receiver, false),
2616    ];
2617    Instruction {
2618        program_id: *program_id,
2619        accounts,
2620        data: borsh::to_vec(&StakePoolInstruction::SetManager).unwrap(),
2621    }
2622}
2623
2624/// Creates a `SetFee` instruction.
2625pub fn set_fee(
2626    program_id: &Pubkey,
2627    stake_pool: &Pubkey,
2628    manager: &Pubkey,
2629    fee: FeeType,
2630) -> Instruction {
2631    let accounts = vec![
2632        AccountMeta::new(*stake_pool, false),
2633        AccountMeta::new_readonly(*manager, true),
2634    ];
2635    Instruction {
2636        program_id: *program_id,
2637        accounts,
2638        data: borsh::to_vec(&StakePoolInstruction::SetFee { fee }).unwrap(),
2639    }
2640}
2641
2642/// Creates a `SetStaker` instruction.
2643pub fn set_staker(
2644    program_id: &Pubkey,
2645    stake_pool: &Pubkey,
2646    set_staker_authority: &Pubkey,
2647    new_staker: &Pubkey,
2648) -> Instruction {
2649    let accounts = vec![
2650        AccountMeta::new(*stake_pool, false),
2651        AccountMeta::new_readonly(*set_staker_authority, true),
2652        AccountMeta::new_readonly(*new_staker, false),
2653    ];
2654    Instruction {
2655        program_id: *program_id,
2656        accounts,
2657        data: borsh::to_vec(&StakePoolInstruction::SetStaker).unwrap(),
2658    }
2659}
2660
2661/// Creates a `SetFundingAuthority` instruction.
2662pub fn set_funding_authority(
2663    program_id: &Pubkey,
2664    stake_pool: &Pubkey,
2665    manager: &Pubkey,
2666    new_sol_deposit_authority: Option<&Pubkey>,
2667    funding_type: FundingType,
2668) -> Instruction {
2669    let mut accounts = vec![
2670        AccountMeta::new(*stake_pool, false),
2671        AccountMeta::new_readonly(*manager, true),
2672    ];
2673    if let Some(auth) = new_sol_deposit_authority {
2674        accounts.push(AccountMeta::new_readonly(*auth, false))
2675    }
2676    Instruction {
2677        program_id: *program_id,
2678        accounts,
2679        data: borsh::to_vec(&StakePoolInstruction::SetFundingAuthority(funding_type)).unwrap(),
2680    }
2681}
2682
2683/// Creates an instruction to update metadata in the mpl token metadata program
2684/// account for the pool token
2685pub fn update_token_metadata(
2686    program_id: &Pubkey,
2687    stake_pool: &Pubkey,
2688    manager: &Pubkey,
2689    pool_mint: &Pubkey,
2690    name: String,
2691    symbol: String,
2692    uri: String,
2693) -> Instruction {
2694    let (stake_pool_withdraw_authority, _) =
2695        find_withdraw_authority_program_address(program_id, stake_pool);
2696    let (token_metadata, _) = find_metadata_account(pool_mint);
2697
2698    let accounts = vec![
2699        AccountMeta::new_readonly(*stake_pool, false),
2700        AccountMeta::new_readonly(*manager, true),
2701        AccountMeta::new_readonly(stake_pool_withdraw_authority, false),
2702        AccountMeta::new(token_metadata, false),
2703        AccountMeta::new_readonly(inline_mpl_token_metadata::id(), false),
2704    ];
2705
2706    Instruction {
2707        program_id: *program_id,
2708        accounts,
2709        data: borsh::to_vec(&StakePoolInstruction::UpdateTokenMetadata { name, symbol, uri })
2710            .unwrap(),
2711    }
2712}
2713
2714/// Creates an instruction to create metadata using the mpl token metadata
2715/// program for the pool token
2716pub fn create_token_metadata(
2717    program_id: &Pubkey,
2718    stake_pool: &Pubkey,
2719    manager: &Pubkey,
2720    pool_mint: &Pubkey,
2721    payer: &Pubkey,
2722    name: String,
2723    symbol: String,
2724    uri: String,
2725) -> Instruction {
2726    let (stake_pool_withdraw_authority, _) =
2727        find_withdraw_authority_program_address(program_id, stake_pool);
2728    let (token_metadata, _) = find_metadata_account(pool_mint);
2729
2730    let accounts = vec![
2731        AccountMeta::new_readonly(*stake_pool, false),
2732        AccountMeta::new_readonly(*manager, true),
2733        AccountMeta::new_readonly(stake_pool_withdraw_authority, false),
2734        AccountMeta::new_readonly(*pool_mint, false),
2735        AccountMeta::new(*payer, true),
2736        AccountMeta::new(token_metadata, false),
2737        AccountMeta::new_readonly(inline_mpl_token_metadata::id(), false),
2738        AccountMeta::new_readonly(system_program::id(), false),
2739    ];
2740
2741    Instruction {
2742        program_id: *program_id,
2743        accounts,
2744        data: borsh::to_vec(&StakePoolInstruction::CreateTokenMetadata { name, symbol, uri })
2745            .unwrap(),
2746    }
2747}
2748
2749/// Creates instruction required to deposit wSOL directly
2750/// into a stake pool using a session signer.
2751pub fn deposit_wsol_with_session(
2752    program_id: &Pubkey,
2753    stake_pool: &Pubkey,
2754    withdraw_authority: &Pubkey,
2755    reserve_stake: &Pubkey,
2756    session_signer: &Pubkey,
2757    pool_token_account: &Pubkey,
2758    manager_fee_account: &Pubkey,
2759    referrer_pool_account: &Pubkey,
2760    pool_mint: &Pubkey,
2761    token_program_id: &Pubkey,
2762    wsol_token_account: &Pubkey,
2763    transient_wsol_account: &Pubkey,
2764    program_signer: &Pubkey,
2765    payer: &Pubkey,
2766    user_wallet: &Pubkey,
2767    sol_deposit_authority: Option<&Pubkey>,
2768    lamports_in: u64,
2769    minimum_pool_tokens_out: u64,
2770) -> Instruction {
2771    let mut accounts = vec![
2772        AccountMeta::new(*stake_pool, false),
2773        AccountMeta::new_readonly(*withdraw_authority, false),
2774        AccountMeta::new(*reserve_stake, false),
2775        AccountMeta::new_readonly(*session_signer, true),
2776        AccountMeta::new(*pool_token_account, false),
2777        AccountMeta::new(*manager_fee_account, false),
2778        AccountMeta::new(*referrer_pool_account, false),
2779        AccountMeta::new(*pool_mint, false),
2780        AccountMeta::new_readonly(system_program::id(), false),
2781        AccountMeta::new_readonly(*token_program_id, false),
2782        AccountMeta::new_readonly(native_mint::id(), false),
2783        AccountMeta::new(*wsol_token_account, false),
2784        AccountMeta::new(*transient_wsol_account, false),
2785        AccountMeta::new(*program_signer, false),
2786        AccountMeta::new(*payer, true),
2787        AccountMeta::new_readonly(*user_wallet, false),
2788    ];
2789
2790    if let Some(sol_deposit_authority) = sol_deposit_authority {
2791        accounts.push(AccountMeta::new_readonly(*sol_deposit_authority, true));
2792    }
2793
2794    accounts.push(AccountMeta::new_readonly(
2795        spl_associated_token_account::id(),
2796        false,
2797    ));
2798
2799    let data = borsh::to_vec(&StakePoolInstruction::DepositWsolWithSession {
2800        lamports_in,
2801        minimum_pool_tokens_out,
2802    })
2803    .unwrap();
2804
2805    Instruction {
2806        program_id: *program_id,
2807        accounts,
2808        data,
2809    }
2810}
2811
2812/// Creates instruction required to withdraw wSOL directly
2813/// from a stake pool using a session signer.
2814pub fn withdraw_wsol_with_session(
2815    program_id: &Pubkey,
2816    stake_pool: &Pubkey,
2817    withdraw_authority: &Pubkey,
2818    session_signer: &Pubkey,
2819    burn_from_pool: &Pubkey,
2820    reserve_stake: &Pubkey,
2821    destination_wsol_account: &Pubkey,
2822    manager_fee_account: &Pubkey,
2823    pool_mint: &Pubkey,
2824    token_program_id: &Pubkey,
2825    program_signer: &Pubkey,
2826    user_wallet: &Pubkey,
2827    sol_withdraw_authority: Option<&Pubkey>,
2828    pool_tokens_in: u64,
2829    minimum_lamports_out: u64,
2830) -> Instruction {
2831    let mut accounts = vec![
2832        AccountMeta::new(*stake_pool, false),
2833        AccountMeta::new_readonly(*withdraw_authority, false),
2834        AccountMeta::new_readonly(*session_signer, true),
2835        AccountMeta::new(*burn_from_pool, false),
2836        AccountMeta::new(*reserve_stake, false),
2837        AccountMeta::new(*destination_wsol_account, false),
2838        AccountMeta::new(*manager_fee_account, false),
2839        AccountMeta::new(*pool_mint, false),
2840        AccountMeta::new_readonly(sysvar::clock::id(), false),
2841        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
2842        AccountMeta::new_readonly(stake::program::id(), false),
2843        AccountMeta::new_readonly(*token_program_id, false),
2844        AccountMeta::new_readonly(native_mint::id(), false),
2845        AccountMeta::new(*program_signer, false),
2846        AccountMeta::new_readonly(*user_wallet, false),
2847        AccountMeta::new_readonly(system_program::id(), false),
2848    ];
2849
2850    if let Some(sol_withdraw_authority) = sol_withdraw_authority {
2851        accounts.push(AccountMeta::new_readonly(*sol_withdraw_authority, true));
2852    }
2853
2854    // Associated Token Program must be last - only needed in transaction for CPI routing
2855    accounts.push(AccountMeta::new_readonly(
2856        spl_associated_token_account::id(),
2857        false,
2858    ));
2859
2860    let data = borsh::to_vec(&StakePoolInstruction::WithdrawWsolWithSession {
2861        pool_tokens_in,
2862        minimum_lamports_out,
2863    })
2864    .unwrap();
2865
2866    Instruction {
2867        program_id: *program_id,
2868        accounts,
2869        data,
2870    }
2871}
2872
2873/// Creates instruction to withdraw stake from a stake pool using a session signer.
2874pub fn withdraw_stake_with_session(
2875    program_id: &Pubkey,
2876    stake_pool: &Pubkey,
2877    validator_list: &Pubkey,
2878    withdraw_authority: &Pubkey,
2879    stake_to_split: &Pubkey,
2880    stake_to_receive: &Pubkey,
2881    session_signer: &Pubkey,
2882    burn_from_pool: &Pubkey,
2883    manager_fee_account: &Pubkey,
2884    pool_mint: &Pubkey,
2885    token_program_id: &Pubkey,
2886    program_signer: &Pubkey,
2887    reserve_stake: &Pubkey,
2888    pool_tokens_in: u64,
2889    minimum_lamports_out: u64,
2890    user_stake_seed: u64,
2891) -> Instruction {
2892    let accounts = vec![
2893        AccountMeta::new(*stake_pool, false),
2894        AccountMeta::new(*validator_list, false),
2895        AccountMeta::new_readonly(*withdraw_authority, false),
2896        AccountMeta::new(*stake_to_split, false),
2897        AccountMeta::new(*stake_to_receive, false),
2898        AccountMeta::new_readonly(*session_signer, true), // user_stake_authority (signer_or_session)
2899        AccountMeta::new_readonly(*session_signer, false), // user_transfer_authority (not used in session path)
2900        AccountMeta::new(*burn_from_pool, false),
2901        AccountMeta::new(*manager_fee_account, false),
2902        AccountMeta::new(*pool_mint, false),
2903        AccountMeta::new_readonly(sysvar::clock::id(), false),
2904        AccountMeta::new_readonly(*token_program_id, false),
2905        AccountMeta::new_readonly(stake::program::id(), false),
2906        AccountMeta::new_readonly(*program_signer, false),
2907        AccountMeta::new_readonly(system_program::id(), false),
2908        AccountMeta::new(*reserve_stake, false),
2909        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
2910    ];
2911
2912    let data = borsh::to_vec(&StakePoolInstruction::WithdrawStakeWithSession {
2913        pool_tokens_in,
2914        minimum_lamports_out,
2915        user_stake_seed,
2916    })
2917    .unwrap();
2918
2919    Instruction {
2920        program_id: *program_id,
2921        accounts,
2922        data,
2923    }
2924}
2925
2926/// Creates instruction to withdraw lamports from a user stake account using a session signer.
2927/// Used after the cooldown period to convert deactivated stake to SOL.
2928pub fn withdraw_from_stake_account_with_session(
2929    program_id: &Pubkey,
2930    user_stake_account: &Pubkey,
2931    recipient: &Pubkey,
2932    session_signer: &Pubkey,
2933    lamports: u64,
2934    user_stake_seed: u64,
2935) -> Instruction {
2936    let accounts = vec![
2937        AccountMeta::new(*user_stake_account, false),
2938        AccountMeta::new(*recipient, false),
2939        AccountMeta::new_readonly(sysvar::clock::id(), false),
2940        AccountMeta::new_readonly(sysvar::stake_history::id(), false),
2941        AccountMeta::new_readonly(*session_signer, true),
2942        AccountMeta::new_readonly(stake::program::id(), false),
2943    ];
2944
2945    let data = borsh::to_vec(&StakePoolInstruction::WithdrawFromStakeAccountWithSession {
2946        lamports,
2947        user_stake_seed,
2948    })
2949    .unwrap();
2950
2951    Instruction {
2952        program_id: *program_id,
2953        accounts,
2954        data,
2955    }
2956}