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 {
9    crate::{
10        find_deposit_authority_program_address, find_ephemeral_stake_program_address,
11        find_stake_program_address, find_transient_stake_program_address,
12        find_withdraw_authority_program_address,
13        inline_mpl_token_metadata::{self, pda::find_metadata_account},
14        state::{Fee, FeeType, StakePool, ValidatorList, ValidatorStakeInfo},
15        MAX_VALIDATORS_TO_UPDATE,
16    },
17    borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
18    solana_instruction::{AccountMeta, Instruction},
19    solana_program_error::ProgramError,
20    solana_pubkey::Pubkey,
21    solana_stake_interface::stake_history::Epoch,
22    std::num::NonZeroU32,
23};
24
25/// Defines which validator vote account is set during the
26/// `SetPreferredValidator` instruction
27#[repr(C)]
28#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, BorshSchema)]
29pub enum PreferredValidatorType {
30    /// Set preferred validator for deposits
31    Deposit,
32    /// Set preferred validator for withdraws
33    Withdraw,
34}
35
36/// Defines which authority to update in the `SetFundingAuthority`
37/// instruction
38#[repr(C)]
39#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, BorshSchema)]
40pub enum FundingType {
41    /// Sets the stake deposit authority
42    StakeDeposit,
43    /// Sets the SOL deposit authority
44    SolDeposit,
45    /// Sets the SOL withdraw authority
46    SolWithdraw,
47}
48
49/// Instructions supported by the `StakePool` program.
50#[repr(C)]
51#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
52pub enum StakePoolInstruction {
53    ///   Initializes a new `StakePool`.
54    ///
55    ///   0. `[w]` New `StakePool` to create.
56    ///   1. `[s]` Manager
57    ///   2. `[]` Staker
58    ///   3. `[]` Stake pool withdraw authority
59    ///   4. `[w]` Uninitialized validator stake list storage account
60    ///   5. `[]` Reserve stake account must be initialized, have zero balance,
61    ///      and staker / withdrawer authority set to pool withdraw authority.
62    ///   6. `[]` Pool token mint. Must have zero supply, owned by withdraw
63    ///      authority.
64    ///   7. `[]` Pool account to deposit the generated fee for manager.
65    ///   8. `[]` Token program id
66    ///   9. `[]` (Optional) Deposit authority that must sign all deposits.
67    ///      Defaults to the program address generated using
68    ///      `find_deposit_authority_program_address`, making deposits
69    ///      permissionless.
70    Initialize {
71        /// Fee assessed as percentage of perceived rewards
72        fee: Fee,
73        /// Fee charged per withdrawal as percentage of withdrawal
74        withdrawal_fee: Fee,
75        /// Fee charged per deposit as percentage of deposit
76        deposit_fee: Fee,
77        /// Percentage [0-100] of `deposit_fee` that goes to referrer
78        referral_fee: u8,
79        /// Maximum expected number of validators
80        max_validators: u32,
81    },
82
83    ///   (Staker only) Adds stake account delegated to validator to the pool's
84    ///   list of managed validators.
85    ///
86    ///   The stake account will have the rent-exempt amount plus
87    ///   `max(
88    ///     crate::MINIMUM_ACTIVE_STAKE,
89    ///     solana_stake_interface::tools::get_minimum_delegation()
90    ///   )`.
91    ///   It is funded from the stake pool reserve.
92    ///
93    ///   0. `[w]` Stake pool
94    ///   1. `[s]` Staker
95    ///   2. `[w]` Reserve stake account
96    ///   3. `[]` Stake pool withdraw authority
97    ///   4. `[w]` Validator stake list storage account
98    ///   5. `[w]` Stake account to add to the pool
99    ///   6. `[]` Validator this stake account will be delegated to
100    ///   7. `[]` Rent sysvar
101    ///   8. `[]` Clock sysvar
102    ///   9. '[]' Stake history sysvar
103    ///  10. '[]' Stake config sysvar
104    ///  11. `[]` System program
105    ///  12. `[]` Stake program
106    ///
107    ///  User data: optional non-zero `u32` seed used for generating the
108    ///  validator stake address
109    AddValidatorToPool(u32),
110
111    ///   (Staker only) Removes validator from the pool, deactivating its stake
112    ///
113    ///   Only succeeds if the validator stake account has the minimum of
114    ///   `max(crate::MINIMUM_ACTIVE_STAKE,
115    /// solana_stake_interface::tools::get_minimum_delegation())`.   plus the
116    /// rent-exempt amount.
117    ///
118    ///   0. `[w]` Stake pool
119    ///   1. `[s]` Staker
120    ///   2. `[]` Stake pool withdraw authority
121    ///   3. `[w]` Validator stake list storage account
122    ///   4. `[w]` Stake account to remove from the pool
123    ///   5. `[w]` Transient stake account, to deactivate if necessary
124    ///   6. `[]` Sysvar clock
125    ///   7. `[]` Stake program id,
126    RemoveValidatorFromPool,
127
128    /// NOTE: This instruction has been deprecated since version 0.7.0. Please
129    /// use `DecreaseValidatorStakeWithReserve` instead.
130    ///
131    /// (Staker only) Decrease active stake on a validator, eventually moving it
132    /// to the reserve
133    ///
134    /// Internally, this instruction splits a validator stake account into its
135    /// corresponding transient stake account and deactivates it.
136    ///
137    /// In order to rebalance the pool without taking custody, the staker needs
138    /// a way of reducing the stake on a stake account. This instruction splits
139    /// some amount of stake, up to the total activated stake, from the
140    /// canonical validator stake account, into its "transient" stake
141    /// account.
142    ///
143    /// The instruction only succeeds if the transient stake account does not
144    /// exist. The amount of lamports to move must be at least rent-exemption
145    /// plus `max(crate::MINIMUM_ACTIVE_STAKE,
146    /// solana_stake_interface::tools::get_minimum_delegation())`.
147    ///
148    ///  0. `[]` Stake pool
149    ///  1. `[s]` Stake pool staker
150    ///  2. `[]` Stake pool withdraw authority
151    ///  3. `[w]` Validator list
152    ///  4. `[w]` Canonical stake account to split from
153    ///  5. `[w]` Transient stake account to receive split
154    ///  6. `[]` Clock sysvar
155    ///  7. `[]` Rent sysvar
156    ///  8. `[]` System program
157    ///  9. `[]` Stake program
158    DecreaseValidatorStake {
159        /// amount of lamports to split into the transient stake account
160        lamports: u64,
161        /// seed used to create transient stake account
162        transient_stake_seed: u64,
163    },
164
165    /// (Staker only) Increase stake on a validator from the reserve account
166    ///
167    /// Internally, this instruction splits reserve stake into a transient stake
168    /// account and delegate to the appropriate validator.
169    /// `UpdateValidatorListBalance` will do the work of merging once it's
170    /// ready.
171    ///
172    /// This instruction only succeeds if the transient stake account does not
173    /// exist. The minimum amount to move is rent-exemption plus
174    /// `max(crate::MINIMUM_ACTIVE_STAKE,
175    /// solana_stake_interface::tools::get_minimum_delegation())`.
176    ///
177    ///  0. `[]` Stake pool
178    ///  1. `[s]` Stake pool staker
179    ///  2. `[]` Stake pool withdraw authority
180    ///  3. `[w]` Validator list
181    ///  4. `[w]` Stake pool reserve stake
182    ///  5. `[w]` Transient stake account
183    ///  6. `[]` Validator stake account
184    ///  7. `[]` Validator vote account to delegate to
185    ///  8. '[]' Clock sysvar
186    ///  9. '[]' Rent sysvar
187    /// 10. `[]` Stake History sysvar
188    /// 11. `[]` Stake Config sysvar
189    /// 12. `[]` System program
190    /// 13. `[]` Stake program
191    ///
192    /// User data: amount of lamports to increase on the given validator.
193    ///
194    /// The actual amount split into the transient stake account is:
195    /// `lamports + stake_rent_exemption`.
196    ///
197    /// The rent-exemption of the stake account is withdrawn back to the
198    /// reserve after it is merged.
199    IncreaseValidatorStake {
200        /// amount of lamports to increase on the given validator
201        lamports: u64,
202        /// seed used to create transient stake account
203        transient_stake_seed: u64,
204    },
205
206    /// (Staker only) Set the preferred deposit or withdraw stake account for
207    /// the stake pool
208    ///
209    /// In order to avoid users abusing the stake pool as a free conversion
210    /// between SOL staked on different validators, the staker can force all
211    /// deposits and/or withdraws to go to one chosen account, or unset that
212    /// account.
213    ///
214    /// 0. `[w]` Stake pool
215    /// 1. `[s]` Stake pool staker
216    /// 2. `[]` Validator list
217    ///
218    /// Fails if the validator is not part of the stake pool.
219    SetPreferredValidator {
220        /// Affected operation (deposit or withdraw)
221        validator_type: PreferredValidatorType,
222        /// Validator vote account that deposits or withdraws must go through,
223        /// unset with None
224        validator_vote_address: Option<Pubkey>,
225    },
226
227    ///  Updates balances of validator and transient stake accounts in the pool
228    ///
229    ///  While going through the pairs of validator and transient stake
230    ///  accounts, if the transient stake is inactive, it is merged into the
231    ///  reserve stake account. If the transient stake is active and has
232    ///  matching credits observed, it is merged into the canonical
233    ///  validator stake account. In all other states, nothing is done, and
234    ///  the balance is simply added to the canonical stake account balance.
235    ///
236    ///  0. `[]` Stake pool
237    ///  1. `[]` Stake pool withdraw authority
238    ///  2. `[w]` Validator stake list storage account
239    ///  3. `[w]` Reserve stake account
240    ///  4. `[]` Sysvar clock
241    ///  5. `[]` Sysvar stake history
242    ///  6. `[]` Stake program
243    ///  7. `..7+2N` [] N pairs of validator and transient stake accounts
244    UpdateValidatorListBalance {
245        /// Index to start updating on the validator list
246        start_index: u32,
247        /// If true, don't try merging transient stake accounts into the reserve
248        /// or validator stake account.  Useful for testing or if a
249        /// particular stake account is in a bad state, but we still
250        /// want to update
251        no_merge: bool,
252    },
253
254    ///   Updates total pool balance based on balances in the reserve and
255    ///   validator list
256    ///
257    ///   0. `[w]` Stake pool
258    ///   1. `[]` Stake pool withdraw authority
259    ///   2. `[w]` Validator stake list storage account
260    ///   3. `[]` Reserve stake account
261    ///   4. `[w]` Account to receive pool fee tokens
262    ///   5. `[w]` Pool mint account
263    ///   6. `[]` Pool token program
264    UpdateStakePoolBalance,
265
266    ///   Cleans up validator stake account entries marked as `ReadyForRemoval`
267    ///
268    ///   0. `[]` Stake pool
269    ///   1. `[w]` Validator stake list storage account
270    CleanupRemovedValidatorEntries,
271
272    ///   Deposit some stake into the pool. The output is a "pool" token
273    ///   representing ownership into the pool. Inputs are converted to the
274    ///   current ratio.
275    ///
276    ///   This instruction MUST be included within the same Transaction as the
277    ///   stake program's `Authorize` instruction that assigns ownership of the
278    ///   stake account to the stake pool. Otherwise another party can deposit
279    ///   the stake account and receive pool tokens destined for someone else.
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_stake_interface::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_stake_interface::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_stake_interface::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_stake_interface::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_stake_interface::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
743/// Creates an `Initialize` instruction.
744pub fn initialize(
745    program_id: &Pubkey,
746    stake_pool: &Pubkey,
747    manager: &Pubkey,
748    staker: &Pubkey,
749    stake_pool_withdraw_authority: &Pubkey,
750    validator_list: &Pubkey,
751    reserve_stake: &Pubkey,
752    pool_mint: &Pubkey,
753    manager_pool_account: &Pubkey,
754    token_program_id: &Pubkey,
755    deposit_authority: Option<Pubkey>,
756    fee: Fee,
757    withdrawal_fee: Fee,
758    deposit_fee: Fee,
759    referral_fee: u8,
760    max_validators: u32,
761) -> Instruction {
762    let init_data = StakePoolInstruction::Initialize {
763        fee,
764        withdrawal_fee,
765        deposit_fee,
766        referral_fee,
767        max_validators,
768    };
769    let data = borsh::to_vec(&init_data).unwrap();
770    let mut accounts = vec![
771        AccountMeta::new(*stake_pool, false),
772        AccountMeta::new_readonly(*manager, true),
773        AccountMeta::new_readonly(*staker, false),
774        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
775        AccountMeta::new(*validator_list, false),
776        AccountMeta::new_readonly(*reserve_stake, false),
777        AccountMeta::new(*pool_mint, false),
778        AccountMeta::new(*manager_pool_account, false),
779        AccountMeta::new_readonly(*token_program_id, false),
780    ];
781    if let Some(deposit_authority) = deposit_authority {
782        accounts.push(AccountMeta::new_readonly(deposit_authority, true));
783    }
784    Instruction {
785        program_id: *program_id,
786        accounts,
787        data,
788    }
789}
790
791/// Creates `AddValidatorToPool` instruction (add new validator stake account to
792/// the pool)
793pub fn add_validator_to_pool(
794    program_id: &Pubkey,
795    stake_pool: &Pubkey,
796    staker: &Pubkey,
797    reserve: &Pubkey,
798    stake_pool_withdraw: &Pubkey,
799    validator_list: &Pubkey,
800    stake: &Pubkey,
801    validator: &Pubkey,
802    seed: Option<NonZeroU32>,
803) -> Instruction {
804    let accounts = vec![
805        AccountMeta::new(*stake_pool, false),
806        AccountMeta::new_readonly(*staker, true),
807        AccountMeta::new(*reserve, false),
808        AccountMeta::new_readonly(*stake_pool_withdraw, false),
809        AccountMeta::new(*validator_list, false),
810        AccountMeta::new(*stake, false),
811        AccountMeta::new_readonly(*validator, false),
812        AccountMeta::new_readonly(solana_sdk_ids::sysvar::rent::id(), false),
813        AccountMeta::new_readonly(solana_sdk_ids::sysvar::clock::id(), false),
814        AccountMeta::new_readonly(solana_sdk_ids::sysvar::stake_history::id(), false),
815        #[allow(deprecated)]
816        AccountMeta::new_readonly(solana_stake_interface::config::id(), false),
817        AccountMeta::new_readonly(solana_system_interface::program::id(), false),
818        AccountMeta::new_readonly(solana_stake_interface::program::id(), false),
819    ];
820    let data = borsh::to_vec(&StakePoolInstruction::AddValidatorToPool(
821        seed.map(|s| s.get()).unwrap_or(0),
822    ))
823    .unwrap();
824    Instruction {
825        program_id: *program_id,
826        accounts,
827        data,
828    }
829}
830
831/// Creates `RemoveValidatorFromPool` instruction (remove validator stake
832/// account from the pool)
833pub fn remove_validator_from_pool(
834    program_id: &Pubkey,
835    stake_pool: &Pubkey,
836    staker: &Pubkey,
837    stake_pool_withdraw: &Pubkey,
838    validator_list: &Pubkey,
839    stake_account: &Pubkey,
840    transient_stake_account: &Pubkey,
841) -> Instruction {
842    let accounts = vec![
843        AccountMeta::new(*stake_pool, false),
844        AccountMeta::new_readonly(*staker, true),
845        AccountMeta::new_readonly(*stake_pool_withdraw, false),
846        AccountMeta::new(*validator_list, false),
847        AccountMeta::new(*stake_account, false),
848        AccountMeta::new(*transient_stake_account, false),
849        AccountMeta::new_readonly(solana_sdk_ids::sysvar::clock::id(), false),
850        AccountMeta::new_readonly(solana_stake_interface::program::id(), false),
851    ];
852    Instruction {
853        program_id: *program_id,
854        accounts,
855        data: borsh::to_vec(&StakePoolInstruction::RemoveValidatorFromPool).unwrap(),
856    }
857}
858
859/// Creates `DecreaseValidatorStake` instruction (rebalance from validator
860/// account to transient account)
861#[deprecated(
862    since = "0.7.0",
863    note = "please use `decrease_validator_stake_with_reserve`"
864)]
865pub fn decrease_validator_stake(
866    program_id: &Pubkey,
867    stake_pool: &Pubkey,
868    staker: &Pubkey,
869    stake_pool_withdraw_authority: &Pubkey,
870    validator_list: &Pubkey,
871    validator_stake: &Pubkey,
872    transient_stake: &Pubkey,
873    lamports: u64,
874    transient_stake_seed: u64,
875) -> Instruction {
876    let accounts = vec![
877        AccountMeta::new_readonly(*stake_pool, false),
878        AccountMeta::new_readonly(*staker, true),
879        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
880        AccountMeta::new(*validator_list, false),
881        AccountMeta::new(*validator_stake, false),
882        AccountMeta::new(*transient_stake, false),
883        AccountMeta::new_readonly(solana_sdk_ids::sysvar::clock::id(), false),
884        AccountMeta::new_readonly(solana_sdk_ids::sysvar::rent::id(), false),
885        AccountMeta::new_readonly(solana_system_interface::program::id(), false),
886        AccountMeta::new_readonly(solana_stake_interface::program::id(), false),
887    ];
888    Instruction {
889        program_id: *program_id,
890        accounts,
891        data: borsh::to_vec(&StakePoolInstruction::DecreaseValidatorStake {
892            lamports,
893            transient_stake_seed,
894        })
895        .unwrap(),
896    }
897}
898
899/// Creates `DecreaseAdditionalValidatorStake` instruction (rebalance from
900/// validator account to transient account)
901pub fn decrease_additional_validator_stake(
902    program_id: &Pubkey,
903    stake_pool: &Pubkey,
904    staker: &Pubkey,
905    stake_pool_withdraw_authority: &Pubkey,
906    validator_list: &Pubkey,
907    reserve_stake: &Pubkey,
908    validator_stake: &Pubkey,
909    ephemeral_stake: &Pubkey,
910    transient_stake: &Pubkey,
911    lamports: u64,
912    transient_stake_seed: u64,
913    ephemeral_stake_seed: u64,
914) -> Instruction {
915    let accounts = vec![
916        AccountMeta::new_readonly(*stake_pool, false),
917        AccountMeta::new_readonly(*staker, true),
918        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
919        AccountMeta::new(*validator_list, false),
920        AccountMeta::new(*reserve_stake, false),
921        AccountMeta::new(*validator_stake, false),
922        AccountMeta::new(*ephemeral_stake, false),
923        AccountMeta::new(*transient_stake, false),
924        AccountMeta::new_readonly(solana_sdk_ids::sysvar::clock::id(), false),
925        AccountMeta::new_readonly(solana_sdk_ids::sysvar::stake_history::id(), false),
926        AccountMeta::new_readonly(solana_system_interface::program::id(), false),
927        AccountMeta::new_readonly(solana_stake_interface::program::id(), false),
928    ];
929    Instruction {
930        program_id: *program_id,
931        accounts,
932        data: borsh::to_vec(&StakePoolInstruction::DecreaseAdditionalValidatorStake {
933            lamports,
934            transient_stake_seed,
935            ephemeral_stake_seed,
936        })
937        .unwrap(),
938    }
939}
940
941/// Creates `DecreaseValidatorStakeWithReserve` instruction (rebalance from
942/// validator account to transient account)
943pub fn decrease_validator_stake_with_reserve(
944    program_id: &Pubkey,
945    stake_pool: &Pubkey,
946    staker: &Pubkey,
947    stake_pool_withdraw_authority: &Pubkey,
948    validator_list: &Pubkey,
949    reserve_stake: &Pubkey,
950    validator_stake: &Pubkey,
951    transient_stake: &Pubkey,
952    lamports: u64,
953    transient_stake_seed: u64,
954) -> Instruction {
955    let accounts = vec![
956        AccountMeta::new_readonly(*stake_pool, false),
957        AccountMeta::new_readonly(*staker, true),
958        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
959        AccountMeta::new(*validator_list, false),
960        AccountMeta::new(*reserve_stake, false),
961        AccountMeta::new(*validator_stake, false),
962        AccountMeta::new(*transient_stake, false),
963        AccountMeta::new_readonly(solana_sdk_ids::sysvar::clock::id(), false),
964        AccountMeta::new_readonly(solana_sdk_ids::sysvar::stake_history::id(), false),
965        AccountMeta::new_readonly(solana_system_interface::program::id(), false),
966        AccountMeta::new_readonly(solana_stake_interface::program::id(), false),
967    ];
968    Instruction {
969        program_id: *program_id,
970        accounts,
971        data: borsh::to_vec(&StakePoolInstruction::DecreaseValidatorStakeWithReserve {
972            lamports,
973            transient_stake_seed,
974        })
975        .unwrap(),
976    }
977}
978
979/// Creates `IncreaseValidatorStake` instruction (rebalance from reserve account
980/// to transient account)
981pub fn increase_validator_stake(
982    program_id: &Pubkey,
983    stake_pool: &Pubkey,
984    staker: &Pubkey,
985    stake_pool_withdraw_authority: &Pubkey,
986    validator_list: &Pubkey,
987    reserve_stake: &Pubkey,
988    transient_stake: &Pubkey,
989    validator_stake: &Pubkey,
990    validator: &Pubkey,
991    lamports: u64,
992    transient_stake_seed: u64,
993) -> Instruction {
994    let accounts = vec![
995        AccountMeta::new_readonly(*stake_pool, false),
996        AccountMeta::new_readonly(*staker, true),
997        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
998        AccountMeta::new(*validator_list, false),
999        AccountMeta::new(*reserve_stake, false),
1000        AccountMeta::new(*transient_stake, false),
1001        AccountMeta::new_readonly(*validator_stake, false),
1002        AccountMeta::new_readonly(*validator, false),
1003        AccountMeta::new_readonly(solana_sdk_ids::sysvar::clock::id(), false),
1004        AccountMeta::new_readonly(solana_sdk_ids::sysvar::rent::id(), false),
1005        AccountMeta::new_readonly(solana_sdk_ids::sysvar::stake_history::id(), false),
1006        #[allow(deprecated)]
1007        AccountMeta::new_readonly(solana_stake_interface::config::id(), false),
1008        AccountMeta::new_readonly(solana_system_interface::program::id(), false),
1009        AccountMeta::new_readonly(solana_stake_interface::program::id(), false),
1010    ];
1011    Instruction {
1012        program_id: *program_id,
1013        accounts,
1014        data: borsh::to_vec(&StakePoolInstruction::IncreaseValidatorStake {
1015            lamports,
1016            transient_stake_seed,
1017        })
1018        .unwrap(),
1019    }
1020}
1021
1022/// Creates `IncreaseAdditionalValidatorStake` instruction (rebalance from
1023/// reserve account to transient account)
1024pub fn increase_additional_validator_stake(
1025    program_id: &Pubkey,
1026    stake_pool: &Pubkey,
1027    staker: &Pubkey,
1028    stake_pool_withdraw_authority: &Pubkey,
1029    validator_list: &Pubkey,
1030    reserve_stake: &Pubkey,
1031    ephemeral_stake: &Pubkey,
1032    transient_stake: &Pubkey,
1033    validator_stake: &Pubkey,
1034    validator: &Pubkey,
1035    lamports: u64,
1036    transient_stake_seed: u64,
1037    ephemeral_stake_seed: u64,
1038) -> Instruction {
1039    let accounts = vec![
1040        AccountMeta::new_readonly(*stake_pool, false),
1041        AccountMeta::new_readonly(*staker, true),
1042        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
1043        AccountMeta::new(*validator_list, false),
1044        AccountMeta::new(*reserve_stake, false),
1045        AccountMeta::new(*ephemeral_stake, false),
1046        AccountMeta::new(*transient_stake, false),
1047        AccountMeta::new_readonly(*validator_stake, false),
1048        AccountMeta::new_readonly(*validator, false),
1049        AccountMeta::new_readonly(solana_sdk_ids::sysvar::clock::id(), false),
1050        AccountMeta::new_readonly(solana_sdk_ids::sysvar::stake_history::id(), false),
1051        #[allow(deprecated)]
1052        AccountMeta::new_readonly(solana_stake_interface::config::id(), false),
1053        AccountMeta::new_readonly(solana_system_interface::program::id(), false),
1054        AccountMeta::new_readonly(solana_stake_interface::program::id(), false),
1055    ];
1056    Instruction {
1057        program_id: *program_id,
1058        accounts,
1059        data: borsh::to_vec(&StakePoolInstruction::IncreaseAdditionalValidatorStake {
1060            lamports,
1061            transient_stake_seed,
1062            ephemeral_stake_seed,
1063        })
1064        .unwrap(),
1065    }
1066}
1067
1068/// Creates `Redelegate` instruction (rebalance from one validator account to
1069/// another)
1070#[deprecated(
1071    since = "2.0.0",
1072    note = "The stake redelegate instruction used in this will not be enabled."
1073)]
1074pub fn redelegate(
1075    program_id: &Pubkey,
1076    stake_pool: &Pubkey,
1077    staker: &Pubkey,
1078    stake_pool_withdraw_authority: &Pubkey,
1079    validator_list: &Pubkey,
1080    reserve_stake: &Pubkey,
1081    source_validator_stake: &Pubkey,
1082    source_transient_stake: &Pubkey,
1083    ephemeral_stake: &Pubkey,
1084    destination_transient_stake: &Pubkey,
1085    destination_validator_stake: &Pubkey,
1086    validator: &Pubkey,
1087    lamports: u64,
1088    source_transient_stake_seed: u64,
1089    ephemeral_stake_seed: u64,
1090    destination_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(*source_validator_stake, false),
1099        AccountMeta::new(*source_transient_stake, false),
1100        AccountMeta::new(*ephemeral_stake, false),
1101        AccountMeta::new(*destination_transient_stake, false),
1102        AccountMeta::new_readonly(*destination_validator_stake, false),
1103        AccountMeta::new_readonly(*validator, false),
1104        AccountMeta::new_readonly(solana_sdk_ids::sysvar::clock::id(), false),
1105        AccountMeta::new_readonly(solana_sdk_ids::sysvar::stake_history::id(), false),
1106        #[allow(deprecated)]
1107        AccountMeta::new_readonly(solana_stake_interface::config::id(), false),
1108        AccountMeta::new_readonly(solana_system_interface::program::id(), false),
1109        AccountMeta::new_readonly(solana_stake_interface::program::id(), false),
1110    ];
1111    Instruction {
1112        program_id: *program_id,
1113        accounts,
1114        data: borsh::to_vec(&StakePoolInstruction::Redelegate {
1115            lamports,
1116            source_transient_stake_seed,
1117            ephemeral_stake_seed,
1118            destination_transient_stake_seed,
1119        })
1120        .unwrap(),
1121    }
1122}
1123
1124/// Creates `SetPreferredDepositValidator` instruction
1125pub fn set_preferred_validator(
1126    program_id: &Pubkey,
1127    stake_pool_address: &Pubkey,
1128    staker: &Pubkey,
1129    validator_list_address: &Pubkey,
1130    validator_type: PreferredValidatorType,
1131    validator_vote_address: Option<Pubkey>,
1132) -> Instruction {
1133    Instruction {
1134        program_id: *program_id,
1135        accounts: vec![
1136            AccountMeta::new(*stake_pool_address, false),
1137            AccountMeta::new_readonly(*staker, true),
1138            AccountMeta::new_readonly(*validator_list_address, false),
1139        ],
1140        data: borsh::to_vec(&StakePoolInstruction::SetPreferredValidator {
1141            validator_type,
1142            validator_vote_address,
1143        })
1144        .unwrap(),
1145    }
1146}
1147
1148/// Create an `AddValidatorToPool` instruction given an existing stake pool and
1149/// vote account
1150pub fn add_validator_to_pool_with_vote(
1151    program_id: &Pubkey,
1152    stake_pool: &StakePool,
1153    stake_pool_address: &Pubkey,
1154    vote_account_address: &Pubkey,
1155    seed: Option<NonZeroU32>,
1156) -> Instruction {
1157    let pool_withdraw_authority =
1158        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1159    let (stake_account_address, _) =
1160        find_stake_program_address(program_id, vote_account_address, stake_pool_address, seed);
1161    add_validator_to_pool(
1162        program_id,
1163        stake_pool_address,
1164        &stake_pool.staker,
1165        &stake_pool.reserve_stake,
1166        &pool_withdraw_authority,
1167        &stake_pool.validator_list,
1168        &stake_account_address,
1169        vote_account_address,
1170        seed,
1171    )
1172}
1173
1174/// Create an `RemoveValidatorFromPool` instruction given an existing stake pool
1175/// and vote account
1176pub fn remove_validator_from_pool_with_vote(
1177    program_id: &Pubkey,
1178    stake_pool: &StakePool,
1179    stake_pool_address: &Pubkey,
1180    vote_account_address: &Pubkey,
1181    validator_stake_seed: Option<NonZeroU32>,
1182    transient_stake_seed: u64,
1183) -> Instruction {
1184    let pool_withdraw_authority =
1185        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1186    let (stake_account_address, _) = find_stake_program_address(
1187        program_id,
1188        vote_account_address,
1189        stake_pool_address,
1190        validator_stake_seed,
1191    );
1192    let (transient_stake_account, _) = find_transient_stake_program_address(
1193        program_id,
1194        vote_account_address,
1195        stake_pool_address,
1196        transient_stake_seed,
1197    );
1198    remove_validator_from_pool(
1199        program_id,
1200        stake_pool_address,
1201        &stake_pool.staker,
1202        &pool_withdraw_authority,
1203        &stake_pool.validator_list,
1204        &stake_account_address,
1205        &transient_stake_account,
1206    )
1207}
1208
1209/// Create an `IncreaseValidatorStake` instruction given an existing stake pool
1210/// and vote account
1211pub fn increase_validator_stake_with_vote(
1212    program_id: &Pubkey,
1213    stake_pool: &StakePool,
1214    stake_pool_address: &Pubkey,
1215    vote_account_address: &Pubkey,
1216    lamports: u64,
1217    validator_stake_seed: Option<NonZeroU32>,
1218    transient_stake_seed: u64,
1219) -> Instruction {
1220    let pool_withdraw_authority =
1221        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1222    let (transient_stake_address, _) = find_transient_stake_program_address(
1223        program_id,
1224        vote_account_address,
1225        stake_pool_address,
1226        transient_stake_seed,
1227    );
1228    let (validator_stake_address, _) = find_stake_program_address(
1229        program_id,
1230        vote_account_address,
1231        stake_pool_address,
1232        validator_stake_seed,
1233    );
1234
1235    increase_validator_stake(
1236        program_id,
1237        stake_pool_address,
1238        &stake_pool.staker,
1239        &pool_withdraw_authority,
1240        &stake_pool.validator_list,
1241        &stake_pool.reserve_stake,
1242        &transient_stake_address,
1243        &validator_stake_address,
1244        vote_account_address,
1245        lamports,
1246        transient_stake_seed,
1247    )
1248}
1249
1250/// Create an `IncreaseAdditionalValidatorStake` instruction given an existing
1251/// stake pool and vote account
1252pub fn increase_additional_validator_stake_with_vote(
1253    program_id: &Pubkey,
1254    stake_pool: &StakePool,
1255    stake_pool_address: &Pubkey,
1256    vote_account_address: &Pubkey,
1257    lamports: u64,
1258    validator_stake_seed: Option<NonZeroU32>,
1259    transient_stake_seed: u64,
1260    ephemeral_stake_seed: u64,
1261) -> Instruction {
1262    let pool_withdraw_authority =
1263        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1264    let (ephemeral_stake_address, _) =
1265        find_ephemeral_stake_program_address(program_id, stake_pool_address, ephemeral_stake_seed);
1266    let (transient_stake_address, _) = find_transient_stake_program_address(
1267        program_id,
1268        vote_account_address,
1269        stake_pool_address,
1270        transient_stake_seed,
1271    );
1272    let (validator_stake_address, _) = find_stake_program_address(
1273        program_id,
1274        vote_account_address,
1275        stake_pool_address,
1276        validator_stake_seed,
1277    );
1278
1279    increase_additional_validator_stake(
1280        program_id,
1281        stake_pool_address,
1282        &stake_pool.staker,
1283        &pool_withdraw_authority,
1284        &stake_pool.validator_list,
1285        &stake_pool.reserve_stake,
1286        &ephemeral_stake_address,
1287        &transient_stake_address,
1288        &validator_stake_address,
1289        vote_account_address,
1290        lamports,
1291        transient_stake_seed,
1292        ephemeral_stake_seed,
1293    )
1294}
1295
1296/// Create a `DecreaseValidatorStake` instruction given an existing stake pool
1297/// and vote account
1298pub fn decrease_validator_stake_with_vote(
1299    program_id: &Pubkey,
1300    stake_pool: &StakePool,
1301    stake_pool_address: &Pubkey,
1302    vote_account_address: &Pubkey,
1303    lamports: u64,
1304    validator_stake_seed: Option<NonZeroU32>,
1305    transient_stake_seed: u64,
1306) -> Instruction {
1307    let pool_withdraw_authority =
1308        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1309    let (validator_stake_address, _) = find_stake_program_address(
1310        program_id,
1311        vote_account_address,
1312        stake_pool_address,
1313        validator_stake_seed,
1314    );
1315    let (transient_stake_address, _) = find_transient_stake_program_address(
1316        program_id,
1317        vote_account_address,
1318        stake_pool_address,
1319        transient_stake_seed,
1320    );
1321    decrease_validator_stake_with_reserve(
1322        program_id,
1323        stake_pool_address,
1324        &stake_pool.staker,
1325        &pool_withdraw_authority,
1326        &stake_pool.validator_list,
1327        &stake_pool.reserve_stake,
1328        &validator_stake_address,
1329        &transient_stake_address,
1330        lamports,
1331        transient_stake_seed,
1332    )
1333}
1334
1335/// Create a `IncreaseAdditionalValidatorStake` instruction given an existing
1336/// stake pool, validator list and vote account
1337pub fn increase_additional_validator_stake_with_list(
1338    program_id: &Pubkey,
1339    stake_pool: &StakePool,
1340    validator_list: &ValidatorList,
1341    stake_pool_address: &Pubkey,
1342    vote_account_address: &Pubkey,
1343    lamports: u64,
1344    ephemeral_stake_seed: u64,
1345) -> Result<Instruction, ProgramError> {
1346    let validator_info = validator_list
1347        .find(vote_account_address)
1348        .ok_or(ProgramError::InvalidInstructionData)?;
1349    let transient_stake_seed = u64::from(validator_info.transient_seed_suffix);
1350    let validator_stake_seed = NonZeroU32::new(validator_info.validator_seed_suffix.into());
1351    Ok(increase_additional_validator_stake_with_vote(
1352        program_id,
1353        stake_pool,
1354        stake_pool_address,
1355        vote_account_address,
1356        lamports,
1357        validator_stake_seed,
1358        transient_stake_seed,
1359        ephemeral_stake_seed,
1360    ))
1361}
1362
1363/// Create a `DecreaseAdditionalValidatorStake` instruction given an existing
1364/// stake pool, validator list and vote account
1365pub fn decrease_additional_validator_stake_with_list(
1366    program_id: &Pubkey,
1367    stake_pool: &StakePool,
1368    validator_list: &ValidatorList,
1369    stake_pool_address: &Pubkey,
1370    vote_account_address: &Pubkey,
1371    lamports: u64,
1372    ephemeral_stake_seed: u64,
1373) -> Result<Instruction, ProgramError> {
1374    let validator_info = validator_list
1375        .find(vote_account_address)
1376        .ok_or(ProgramError::InvalidInstructionData)?;
1377    let transient_stake_seed = u64::from(validator_info.transient_seed_suffix);
1378    let validator_stake_seed = NonZeroU32::new(validator_info.validator_seed_suffix.into());
1379    Ok(decrease_additional_validator_stake_with_vote(
1380        program_id,
1381        stake_pool,
1382        stake_pool_address,
1383        vote_account_address,
1384        lamports,
1385        validator_stake_seed,
1386        transient_stake_seed,
1387        ephemeral_stake_seed,
1388    ))
1389}
1390
1391/// Create a `DecreaseAdditionalValidatorStake` instruction given an existing
1392/// stake pool and vote account
1393pub fn decrease_additional_validator_stake_with_vote(
1394    program_id: &Pubkey,
1395    stake_pool: &StakePool,
1396    stake_pool_address: &Pubkey,
1397    vote_account_address: &Pubkey,
1398    lamports: u64,
1399    validator_stake_seed: Option<NonZeroU32>,
1400    transient_stake_seed: u64,
1401    ephemeral_stake_seed: u64,
1402) -> Instruction {
1403    let pool_withdraw_authority =
1404        find_withdraw_authority_program_address(program_id, stake_pool_address).0;
1405    let (validator_stake_address, _) = find_stake_program_address(
1406        program_id,
1407        vote_account_address,
1408        stake_pool_address,
1409        validator_stake_seed,
1410    );
1411    let (ephemeral_stake_address, _) =
1412        find_ephemeral_stake_program_address(program_id, stake_pool_address, ephemeral_stake_seed);
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_additional_validator_stake(
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        &ephemeral_stake_address,
1428        &transient_stake_address,
1429        lamports,
1430        transient_stake_seed,
1431        ephemeral_stake_seed,
1432    )
1433}
1434
1435/// Creates `UpdateValidatorListBalance` instruction (update validator stake
1436/// account balances)
1437#[deprecated(
1438    since = "1.1.0",
1439    note = "please use `update_validator_list_balance_chunk`"
1440)]
1441pub fn update_validator_list_balance(
1442    program_id: &Pubkey,
1443    stake_pool: &Pubkey,
1444    stake_pool_withdraw_authority: &Pubkey,
1445    validator_list_address: &Pubkey,
1446    reserve_stake: &Pubkey,
1447    validator_list: &ValidatorList,
1448    validator_vote_accounts: &[Pubkey],
1449    start_index: u32,
1450    no_merge: bool,
1451) -> Instruction {
1452    let mut accounts = vec![
1453        AccountMeta::new_readonly(*stake_pool, false),
1454        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
1455        AccountMeta::new(*validator_list_address, false),
1456        AccountMeta::new(*reserve_stake, false),
1457        AccountMeta::new_readonly(solana_sdk_ids::sysvar::clock::id(), false),
1458        AccountMeta::new_readonly(solana_sdk_ids::sysvar::stake_history::id(), false),
1459        AccountMeta::new_readonly(solana_stake_interface::program::id(), false),
1460    ];
1461    accounts.append(
1462        &mut validator_vote_accounts
1463            .iter()
1464            .flat_map(|vote_account_address| {
1465                let validator_stake_info = validator_list.find(vote_account_address);
1466                if let Some(validator_stake_info) = validator_stake_info {
1467                    let (validator_stake_account, _) = find_stake_program_address(
1468                        program_id,
1469                        vote_account_address,
1470                        stake_pool,
1471                        NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()),
1472                    );
1473                    let (transient_stake_account, _) = find_transient_stake_program_address(
1474                        program_id,
1475                        vote_account_address,
1476                        stake_pool,
1477                        validator_stake_info.transient_seed_suffix.into(),
1478                    );
1479                    vec![
1480                        AccountMeta::new(validator_stake_account, false),
1481                        AccountMeta::new(transient_stake_account, false),
1482                    ]
1483                } else {
1484                    vec![]
1485                }
1486            })
1487            .collect::<Vec<AccountMeta>>(),
1488    );
1489    Instruction {
1490        program_id: *program_id,
1491        accounts,
1492        data: borsh::to_vec(&StakePoolInstruction::UpdateValidatorListBalance {
1493            start_index,
1494            no_merge,
1495        })
1496        .unwrap(),
1497    }
1498}
1499
1500/// Creates an `UpdateValidatorListBalance` instruction (update validator stake
1501/// account balances) to update `validator_list[start_index..start_index +
1502/// len]`.
1503///
1504/// Returns `Err(ProgramError::InvalidInstructionData)` if:
1505/// - `start_index..start_index + len` is out of bounds for
1506///   `validator_list.validators`
1507pub fn update_validator_list_balance_chunk(
1508    program_id: &Pubkey,
1509    stake_pool: &Pubkey,
1510    stake_pool_withdraw_authority: &Pubkey,
1511    validator_list_address: &Pubkey,
1512    reserve_stake: &Pubkey,
1513    validator_list: &ValidatorList,
1514    len: usize,
1515    start_index: usize,
1516    no_merge: bool,
1517) -> Result<Instruction, ProgramError> {
1518    let mut accounts = vec![
1519        AccountMeta::new_readonly(*stake_pool, false),
1520        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
1521        AccountMeta::new(*validator_list_address, false),
1522        AccountMeta::new(*reserve_stake, false),
1523        AccountMeta::new_readonly(solana_sdk_ids::sysvar::clock::id(), false),
1524        AccountMeta::new_readonly(solana_sdk_ids::sysvar::stake_history::id(), false),
1525        AccountMeta::new_readonly(solana_stake_interface::program::id(), false),
1526    ];
1527    let validator_list_subslice = validator_list
1528        .validators
1529        .get(start_index..start_index.saturating_add(len))
1530        .ok_or(ProgramError::InvalidInstructionData)?;
1531    accounts.extend(validator_list_subslice.iter().flat_map(
1532        |ValidatorStakeInfo {
1533             vote_account_address,
1534             validator_seed_suffix,
1535             transient_seed_suffix,
1536             ..
1537         }| {
1538            let (validator_stake_account, _) = find_stake_program_address(
1539                program_id,
1540                vote_account_address,
1541                stake_pool,
1542                NonZeroU32::new((*validator_seed_suffix).into()),
1543            );
1544            let (transient_stake_account, _) = find_transient_stake_program_address(
1545                program_id,
1546                vote_account_address,
1547                stake_pool,
1548                (*transient_seed_suffix).into(),
1549            );
1550            [
1551                AccountMeta::new(validator_stake_account, false),
1552                AccountMeta::new(transient_stake_account, false),
1553            ]
1554        },
1555    ));
1556    Ok(Instruction {
1557        program_id: *program_id,
1558        accounts,
1559        data: borsh::to_vec(&StakePoolInstruction::UpdateValidatorListBalance {
1560            start_index: start_index.try_into().unwrap(),
1561            no_merge,
1562        })
1563        .unwrap(),
1564    })
1565}
1566
1567/// Creates `UpdateValidatorListBalance` instruction (update validator stake
1568/// account balances)
1569///
1570/// Returns `None` if all validators in the given chunk has already been updated
1571/// for this epoch, returns the required instruction otherwise.
1572pub fn update_stale_validator_list_balance_chunk(
1573    program_id: &Pubkey,
1574    stake_pool: &Pubkey,
1575    stake_pool_withdraw_authority: &Pubkey,
1576    validator_list_address: &Pubkey,
1577    reserve_stake: &Pubkey,
1578    validator_list: &ValidatorList,
1579    len: usize,
1580    start_index: usize,
1581    no_merge: bool,
1582    current_epoch: Epoch,
1583) -> Result<Option<Instruction>, ProgramError> {
1584    let validator_list_subslice = validator_list
1585        .validators
1586        .get(start_index..start_index.saturating_add(len))
1587        .ok_or(ProgramError::InvalidInstructionData)?;
1588    if validator_list_subslice.iter().all(|info| {
1589        let last_update_epoch: u64 = info.last_update_epoch.into();
1590        last_update_epoch >= current_epoch
1591    }) {
1592        return Ok(None);
1593    }
1594    update_validator_list_balance_chunk(
1595        program_id,
1596        stake_pool,
1597        stake_pool_withdraw_authority,
1598        validator_list_address,
1599        reserve_stake,
1600        validator_list,
1601        len,
1602        start_index,
1603        no_merge,
1604    )
1605    .map(Some)
1606}
1607
1608/// Creates `UpdateStakePoolBalance` instruction (pool balance from the stake
1609/// account list balances)
1610pub fn update_stake_pool_balance(
1611    program_id: &Pubkey,
1612    stake_pool: &Pubkey,
1613    withdraw_authority: &Pubkey,
1614    validator_list_storage: &Pubkey,
1615    reserve_stake: &Pubkey,
1616    manager_fee_account: &Pubkey,
1617    stake_pool_mint: &Pubkey,
1618    token_program_id: &Pubkey,
1619) -> Instruction {
1620    let accounts = vec![
1621        AccountMeta::new(*stake_pool, false),
1622        AccountMeta::new_readonly(*withdraw_authority, false),
1623        AccountMeta::new(*validator_list_storage, false),
1624        AccountMeta::new_readonly(*reserve_stake, false),
1625        AccountMeta::new(*manager_fee_account, false),
1626        AccountMeta::new(*stake_pool_mint, false),
1627        AccountMeta::new_readonly(*token_program_id, false),
1628    ];
1629    Instruction {
1630        program_id: *program_id,
1631        accounts,
1632        data: borsh::to_vec(&StakePoolInstruction::UpdateStakePoolBalance).unwrap(),
1633    }
1634}
1635
1636/// Creates `CleanupRemovedValidatorEntries` instruction (removes entries from
1637/// the validator list)
1638pub fn cleanup_removed_validator_entries(
1639    program_id: &Pubkey,
1640    stake_pool: &Pubkey,
1641    validator_list_storage: &Pubkey,
1642) -> Instruction {
1643    let accounts = vec![
1644        AccountMeta::new(*stake_pool, false),
1645        AccountMeta::new(*validator_list_storage, false),
1646    ];
1647    Instruction {
1648        program_id: *program_id,
1649        accounts,
1650        data: borsh::to_vec(&StakePoolInstruction::CleanupRemovedValidatorEntries).unwrap(),
1651    }
1652}
1653
1654/// Creates all `UpdateValidatorListBalance` and `UpdateStakePoolBalance`
1655/// instructions for fully updating a stake pool each epoch
1656pub fn update_stake_pool(
1657    program_id: &Pubkey,
1658    stake_pool: &StakePool,
1659    validator_list: &ValidatorList,
1660    stake_pool_address: &Pubkey,
1661    no_merge: bool,
1662) -> (Vec<Instruction>, Vec<Instruction>) {
1663    let (withdraw_authority, _) =
1664        find_withdraw_authority_program_address(program_id, stake_pool_address);
1665
1666    let update_list_instructions = validator_list
1667        .validators
1668        .chunks(MAX_VALIDATORS_TO_UPDATE)
1669        .enumerate()
1670        .map(|(i, chunk)| {
1671            // unwrap-safety: chunk len and offset are derived
1672            update_validator_list_balance_chunk(
1673                program_id,
1674                stake_pool_address,
1675                &withdraw_authority,
1676                &stake_pool.validator_list,
1677                &stake_pool.reserve_stake,
1678                validator_list,
1679                chunk.len(),
1680                i.saturating_mul(MAX_VALIDATORS_TO_UPDATE),
1681                no_merge,
1682            )
1683            .unwrap()
1684        })
1685        .collect();
1686
1687    let final_instructions = vec![
1688        update_stake_pool_balance(
1689            program_id,
1690            stake_pool_address,
1691            &withdraw_authority,
1692            &stake_pool.validator_list,
1693            &stake_pool.reserve_stake,
1694            &stake_pool.manager_fee_account,
1695            &stake_pool.pool_mint,
1696            &stake_pool.token_program_id,
1697        ),
1698        cleanup_removed_validator_entries(
1699            program_id,
1700            stake_pool_address,
1701            &stake_pool.validator_list,
1702        ),
1703    ];
1704    (update_list_instructions, final_instructions)
1705}
1706
1707/// Creates the `UpdateValidatorListBalance` instructions only for validators on
1708/// `validator_list` that have not been updated for this epoch, and the
1709/// `UpdateStakePoolBalance` instruction for fully updating the stake pool.
1710///
1711/// Basically same as [`update_stake_pool`], but skips validators that are
1712/// already updated for this epoch
1713pub fn update_stale_stake_pool(
1714    program_id: &Pubkey,
1715    stake_pool: &StakePool,
1716    validator_list: &ValidatorList,
1717    stake_pool_address: &Pubkey,
1718    no_merge: bool,
1719    current_epoch: Epoch,
1720) -> (Vec<Instruction>, Vec<Instruction>) {
1721    let (withdraw_authority, _) =
1722        find_withdraw_authority_program_address(program_id, stake_pool_address);
1723
1724    let update_list_instructions = validator_list
1725        .validators
1726        .chunks(MAX_VALIDATORS_TO_UPDATE)
1727        .enumerate()
1728        .filter_map(|(i, chunk)| {
1729            // unwrap-safety: chunk len and offset are derived
1730            update_stale_validator_list_balance_chunk(
1731                program_id,
1732                stake_pool_address,
1733                &withdraw_authority,
1734                &stake_pool.validator_list,
1735                &stake_pool.reserve_stake,
1736                validator_list,
1737                chunk.len(),
1738                i.saturating_mul(MAX_VALIDATORS_TO_UPDATE),
1739                no_merge,
1740                current_epoch,
1741            )
1742            .unwrap()
1743        })
1744        .collect();
1745
1746    let final_instructions = vec![
1747        update_stake_pool_balance(
1748            program_id,
1749            stake_pool_address,
1750            &withdraw_authority,
1751            &stake_pool.validator_list,
1752            &stake_pool.reserve_stake,
1753            &stake_pool.manager_fee_account,
1754            &stake_pool.pool_mint,
1755            &stake_pool.token_program_id,
1756        ),
1757        cleanup_removed_validator_entries(
1758            program_id,
1759            stake_pool_address,
1760            &stake_pool.validator_list,
1761        ),
1762    ];
1763    (update_list_instructions, final_instructions)
1764}
1765
1766fn deposit_stake_internal(
1767    program_id: &Pubkey,
1768    stake_pool: &Pubkey,
1769    validator_list_storage: &Pubkey,
1770    stake_pool_deposit_authority: Option<&Pubkey>,
1771    stake_pool_withdraw_authority: &Pubkey,
1772    deposit_stake_address: &Pubkey,
1773    deposit_stake_withdraw_authority: &Pubkey,
1774    validator_stake_account: &Pubkey,
1775    reserve_stake_account: &Pubkey,
1776    pool_tokens_to: &Pubkey,
1777    manager_fee_account: &Pubkey,
1778    referrer_pool_tokens_account: &Pubkey,
1779    pool_mint: &Pubkey,
1780    token_program_id: &Pubkey,
1781    minimum_pool_tokens_out: Option<u64>,
1782) -> Vec<Instruction> {
1783    let mut instructions = vec![];
1784    let mut accounts = vec![
1785        AccountMeta::new(*stake_pool, false),
1786        AccountMeta::new(*validator_list_storage, false),
1787    ];
1788    if let Some(stake_pool_deposit_authority) = stake_pool_deposit_authority {
1789        accounts.push(AccountMeta::new_readonly(
1790            *stake_pool_deposit_authority,
1791            true,
1792        ));
1793        instructions.extend_from_slice(&[
1794            solana_stake_interface::instruction::authorize(
1795                deposit_stake_address,
1796                deposit_stake_withdraw_authority,
1797                stake_pool_deposit_authority,
1798                solana_stake_interface::state::StakeAuthorize::Staker,
1799                None,
1800            ),
1801            solana_stake_interface::instruction::authorize(
1802                deposit_stake_address,
1803                deposit_stake_withdraw_authority,
1804                stake_pool_deposit_authority,
1805                solana_stake_interface::state::StakeAuthorize::Withdrawer,
1806                None,
1807            ),
1808        ]);
1809    } else {
1810        let stake_pool_deposit_authority =
1811            find_deposit_authority_program_address(program_id, stake_pool).0;
1812        accounts.push(AccountMeta::new_readonly(
1813            stake_pool_deposit_authority,
1814            false,
1815        ));
1816        instructions.extend_from_slice(&[
1817            solana_stake_interface::instruction::authorize(
1818                deposit_stake_address,
1819                deposit_stake_withdraw_authority,
1820                &stake_pool_deposit_authority,
1821                solana_stake_interface::state::StakeAuthorize::Staker,
1822                None,
1823            ),
1824            solana_stake_interface::instruction::authorize(
1825                deposit_stake_address,
1826                deposit_stake_withdraw_authority,
1827                &stake_pool_deposit_authority,
1828                solana_stake_interface::state::StakeAuthorize::Withdrawer,
1829                None,
1830            ),
1831        ]);
1832    };
1833
1834    accounts.extend_from_slice(&[
1835        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
1836        AccountMeta::new(*deposit_stake_address, false),
1837        AccountMeta::new(*validator_stake_account, false),
1838        AccountMeta::new(*reserve_stake_account, false),
1839        AccountMeta::new(*pool_tokens_to, false),
1840        AccountMeta::new(*manager_fee_account, false),
1841        AccountMeta::new(*referrer_pool_tokens_account, false),
1842        AccountMeta::new(*pool_mint, false),
1843        AccountMeta::new_readonly(solana_sdk_ids::sysvar::clock::id(), false),
1844        AccountMeta::new_readonly(solana_sdk_ids::sysvar::stake_history::id(), false),
1845        AccountMeta::new_readonly(*token_program_id, false),
1846        AccountMeta::new_readonly(solana_stake_interface::program::id(), false),
1847    ]);
1848    instructions.push(
1849        if let Some(minimum_pool_tokens_out) = minimum_pool_tokens_out {
1850            Instruction {
1851                program_id: *program_id,
1852                accounts,
1853                data: borsh::to_vec(&StakePoolInstruction::DepositStakeWithSlippage {
1854                    minimum_pool_tokens_out,
1855                })
1856                .unwrap(),
1857            }
1858        } else {
1859            Instruction {
1860                program_id: *program_id,
1861                accounts,
1862                data: borsh::to_vec(&StakePoolInstruction::DepositStake).unwrap(),
1863            }
1864        },
1865    );
1866    instructions
1867}
1868
1869/// Creates instructions required to deposit into a stake pool, given a stake
1870/// account owned by the user.
1871pub fn deposit_stake(
1872    program_id: &Pubkey,
1873    stake_pool: &Pubkey,
1874    validator_list_storage: &Pubkey,
1875    stake_pool_withdraw_authority: &Pubkey,
1876    deposit_stake_address: &Pubkey,
1877    deposit_stake_withdraw_authority: &Pubkey,
1878    validator_stake_account: &Pubkey,
1879    reserve_stake_account: &Pubkey,
1880    pool_tokens_to: &Pubkey,
1881    manager_fee_account: &Pubkey,
1882    referrer_pool_tokens_account: &Pubkey,
1883    pool_mint: &Pubkey,
1884    token_program_id: &Pubkey,
1885) -> Vec<Instruction> {
1886    deposit_stake_internal(
1887        program_id,
1888        stake_pool,
1889        validator_list_storage,
1890        None,
1891        stake_pool_withdraw_authority,
1892        deposit_stake_address,
1893        deposit_stake_withdraw_authority,
1894        validator_stake_account,
1895        reserve_stake_account,
1896        pool_tokens_to,
1897        manager_fee_account,
1898        referrer_pool_tokens_account,
1899        pool_mint,
1900        token_program_id,
1901        None,
1902    )
1903}
1904
1905/// Creates instructions to deposit into a stake pool with slippage
1906pub fn deposit_stake_with_slippage(
1907    program_id: &Pubkey,
1908    stake_pool: &Pubkey,
1909    validator_list_storage: &Pubkey,
1910    stake_pool_withdraw_authority: &Pubkey,
1911    deposit_stake_address: &Pubkey,
1912    deposit_stake_withdraw_authority: &Pubkey,
1913    validator_stake_account: &Pubkey,
1914    reserve_stake_account: &Pubkey,
1915    pool_tokens_to: &Pubkey,
1916    manager_fee_account: &Pubkey,
1917    referrer_pool_tokens_account: &Pubkey,
1918    pool_mint: &Pubkey,
1919    token_program_id: &Pubkey,
1920    minimum_pool_tokens_out: u64,
1921) -> Vec<Instruction> {
1922    deposit_stake_internal(
1923        program_id,
1924        stake_pool,
1925        validator_list_storage,
1926        None,
1927        stake_pool_withdraw_authority,
1928        deposit_stake_address,
1929        deposit_stake_withdraw_authority,
1930        validator_stake_account,
1931        reserve_stake_account,
1932        pool_tokens_to,
1933        manager_fee_account,
1934        referrer_pool_tokens_account,
1935        pool_mint,
1936        token_program_id,
1937        Some(minimum_pool_tokens_out),
1938    )
1939}
1940
1941/// Creates instructions required to deposit into a stake pool, given a stake
1942/// account owned by the user. The difference with `deposit()` is that a deposit
1943/// authority must sign this instruction, which is required for private pools.
1944pub fn deposit_stake_with_authority(
1945    program_id: &Pubkey,
1946    stake_pool: &Pubkey,
1947    validator_list_storage: &Pubkey,
1948    stake_pool_deposit_authority: &Pubkey,
1949    stake_pool_withdraw_authority: &Pubkey,
1950    deposit_stake_address: &Pubkey,
1951    deposit_stake_withdraw_authority: &Pubkey,
1952    validator_stake_account: &Pubkey,
1953    reserve_stake_account: &Pubkey,
1954    pool_tokens_to: &Pubkey,
1955    manager_fee_account: &Pubkey,
1956    referrer_pool_tokens_account: &Pubkey,
1957    pool_mint: &Pubkey,
1958    token_program_id: &Pubkey,
1959) -> Vec<Instruction> {
1960    deposit_stake_internal(
1961        program_id,
1962        stake_pool,
1963        validator_list_storage,
1964        Some(stake_pool_deposit_authority),
1965        stake_pool_withdraw_authority,
1966        deposit_stake_address,
1967        deposit_stake_withdraw_authority,
1968        validator_stake_account,
1969        reserve_stake_account,
1970        pool_tokens_to,
1971        manager_fee_account,
1972        referrer_pool_tokens_account,
1973        pool_mint,
1974        token_program_id,
1975        None,
1976    )
1977}
1978
1979/// Creates instructions required to deposit into a stake pool with slippage,
1980/// given a stake account owned by the user. The difference with `deposit()` is
1981/// that a deposit authority must sign this instruction, which is required for
1982/// private pools.
1983pub fn deposit_stake_with_authority_and_slippage(
1984    program_id: &Pubkey,
1985    stake_pool: &Pubkey,
1986    validator_list_storage: &Pubkey,
1987    stake_pool_deposit_authority: &Pubkey,
1988    stake_pool_withdraw_authority: &Pubkey,
1989    deposit_stake_address: &Pubkey,
1990    deposit_stake_withdraw_authority: &Pubkey,
1991    validator_stake_account: &Pubkey,
1992    reserve_stake_account: &Pubkey,
1993    pool_tokens_to: &Pubkey,
1994    manager_fee_account: &Pubkey,
1995    referrer_pool_tokens_account: &Pubkey,
1996    pool_mint: &Pubkey,
1997    token_program_id: &Pubkey,
1998    minimum_pool_tokens_out: u64,
1999) -> Vec<Instruction> {
2000    deposit_stake_internal(
2001        program_id,
2002        stake_pool,
2003        validator_list_storage,
2004        Some(stake_pool_deposit_authority),
2005        stake_pool_withdraw_authority,
2006        deposit_stake_address,
2007        deposit_stake_withdraw_authority,
2008        validator_stake_account,
2009        reserve_stake_account,
2010        pool_tokens_to,
2011        manager_fee_account,
2012        referrer_pool_tokens_account,
2013        pool_mint,
2014        token_program_id,
2015        Some(minimum_pool_tokens_out),
2016    )
2017}
2018
2019/// Creates instructions required to deposit SOL directly into a stake pool.
2020fn deposit_sol_internal(
2021    program_id: &Pubkey,
2022    stake_pool: &Pubkey,
2023    stake_pool_withdraw_authority: &Pubkey,
2024    reserve_stake_account: &Pubkey,
2025    lamports_from: &Pubkey,
2026    pool_tokens_to: &Pubkey,
2027    manager_fee_account: &Pubkey,
2028    referrer_pool_tokens_account: &Pubkey,
2029    pool_mint: &Pubkey,
2030    token_program_id: &Pubkey,
2031    sol_deposit_authority: Option<&Pubkey>,
2032    lamports_in: u64,
2033    minimum_pool_tokens_out: Option<u64>,
2034) -> Instruction {
2035    let mut accounts = vec![
2036        AccountMeta::new(*stake_pool, false),
2037        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
2038        AccountMeta::new(*reserve_stake_account, false),
2039        AccountMeta::new(*lamports_from, true),
2040        AccountMeta::new(*pool_tokens_to, false),
2041        AccountMeta::new(*manager_fee_account, false),
2042        AccountMeta::new(*referrer_pool_tokens_account, false),
2043        AccountMeta::new(*pool_mint, false),
2044        AccountMeta::new_readonly(solana_system_interface::program::id(), false),
2045        AccountMeta::new_readonly(*token_program_id, false),
2046    ];
2047    if let Some(sol_deposit_authority) = sol_deposit_authority {
2048        accounts.push(AccountMeta::new_readonly(*sol_deposit_authority, true));
2049    }
2050    if let Some(minimum_pool_tokens_out) = minimum_pool_tokens_out {
2051        Instruction {
2052            program_id: *program_id,
2053            accounts,
2054            data: borsh::to_vec(&StakePoolInstruction::DepositSolWithSlippage {
2055                lamports_in,
2056                minimum_pool_tokens_out,
2057            })
2058            .unwrap(),
2059        }
2060    } else {
2061        Instruction {
2062            program_id: *program_id,
2063            accounts,
2064            data: borsh::to_vec(&StakePoolInstruction::DepositSol(lamports_in)).unwrap(),
2065        }
2066    }
2067}
2068
2069/// Creates instruction to deposit SOL directly into a stake pool.
2070pub fn deposit_sol(
2071    program_id: &Pubkey,
2072    stake_pool: &Pubkey,
2073    stake_pool_withdraw_authority: &Pubkey,
2074    reserve_stake_account: &Pubkey,
2075    lamports_from: &Pubkey,
2076    pool_tokens_to: &Pubkey,
2077    manager_fee_account: &Pubkey,
2078    referrer_pool_tokens_account: &Pubkey,
2079    pool_mint: &Pubkey,
2080    token_program_id: &Pubkey,
2081    lamports_in: u64,
2082) -> Instruction {
2083    deposit_sol_internal(
2084        program_id,
2085        stake_pool,
2086        stake_pool_withdraw_authority,
2087        reserve_stake_account,
2088        lamports_from,
2089        pool_tokens_to,
2090        manager_fee_account,
2091        referrer_pool_tokens_account,
2092        pool_mint,
2093        token_program_id,
2094        None,
2095        lamports_in,
2096        None,
2097    )
2098}
2099
2100/// Creates instruction to deposit SOL directly into a stake pool with slippage
2101/// constraint.
2102pub fn deposit_sol_with_slippage(
2103    program_id: &Pubkey,
2104    stake_pool: &Pubkey,
2105    stake_pool_withdraw_authority: &Pubkey,
2106    reserve_stake_account: &Pubkey,
2107    lamports_from: &Pubkey,
2108    pool_tokens_to: &Pubkey,
2109    manager_fee_account: &Pubkey,
2110    referrer_pool_tokens_account: &Pubkey,
2111    pool_mint: &Pubkey,
2112    token_program_id: &Pubkey,
2113    lamports_in: u64,
2114    minimum_pool_tokens_out: u64,
2115) -> Instruction {
2116    deposit_sol_internal(
2117        program_id,
2118        stake_pool,
2119        stake_pool_withdraw_authority,
2120        reserve_stake_account,
2121        lamports_from,
2122        pool_tokens_to,
2123        manager_fee_account,
2124        referrer_pool_tokens_account,
2125        pool_mint,
2126        token_program_id,
2127        None,
2128        lamports_in,
2129        Some(minimum_pool_tokens_out),
2130    )
2131}
2132
2133/// Creates instruction required to deposit SOL directly into a stake pool.
2134/// The difference with `deposit_sol()` is that a deposit
2135/// authority must sign this instruction.
2136pub fn deposit_sol_with_authority(
2137    program_id: &Pubkey,
2138    stake_pool: &Pubkey,
2139    sol_deposit_authority: &Pubkey,
2140    stake_pool_withdraw_authority: &Pubkey,
2141    reserve_stake_account: &Pubkey,
2142    lamports_from: &Pubkey,
2143    pool_tokens_to: &Pubkey,
2144    manager_fee_account: &Pubkey,
2145    referrer_pool_tokens_account: &Pubkey,
2146    pool_mint: &Pubkey,
2147    token_program_id: &Pubkey,
2148    lamports_in: u64,
2149) -> Instruction {
2150    deposit_sol_internal(
2151        program_id,
2152        stake_pool,
2153        stake_pool_withdraw_authority,
2154        reserve_stake_account,
2155        lamports_from,
2156        pool_tokens_to,
2157        manager_fee_account,
2158        referrer_pool_tokens_account,
2159        pool_mint,
2160        token_program_id,
2161        Some(sol_deposit_authority),
2162        lamports_in,
2163        None,
2164    )
2165}
2166
2167/// Creates instruction to deposit SOL directly into a stake pool with slippage
2168/// constraint.
2169pub fn deposit_sol_with_authority_and_slippage(
2170    program_id: &Pubkey,
2171    stake_pool: &Pubkey,
2172    sol_deposit_authority: &Pubkey,
2173    stake_pool_withdraw_authority: &Pubkey,
2174    reserve_stake_account: &Pubkey,
2175    lamports_from: &Pubkey,
2176    pool_tokens_to: &Pubkey,
2177    manager_fee_account: &Pubkey,
2178    referrer_pool_tokens_account: &Pubkey,
2179    pool_mint: &Pubkey,
2180    token_program_id: &Pubkey,
2181    lamports_in: u64,
2182    minimum_pool_tokens_out: u64,
2183) -> Instruction {
2184    deposit_sol_internal(
2185        program_id,
2186        stake_pool,
2187        stake_pool_withdraw_authority,
2188        reserve_stake_account,
2189        lamports_from,
2190        pool_tokens_to,
2191        manager_fee_account,
2192        referrer_pool_tokens_account,
2193        pool_mint,
2194        token_program_id,
2195        Some(sol_deposit_authority),
2196        lamports_in,
2197        Some(minimum_pool_tokens_out),
2198    )
2199}
2200
2201fn withdraw_stake_internal(
2202    program_id: &Pubkey,
2203    stake_pool: &Pubkey,
2204    validator_list_storage: &Pubkey,
2205    stake_pool_withdraw: &Pubkey,
2206    stake_to_split: &Pubkey,
2207    stake_to_receive: &Pubkey,
2208    user_stake_authority: &Pubkey,
2209    user_transfer_authority: &Pubkey,
2210    user_pool_token_account: &Pubkey,
2211    manager_fee_account: &Pubkey,
2212    pool_mint: &Pubkey,
2213    token_program_id: &Pubkey,
2214    pool_tokens_in: u64,
2215    minimum_lamports_out: Option<u64>,
2216) -> Instruction {
2217    let accounts = vec![
2218        AccountMeta::new(*stake_pool, false),
2219        AccountMeta::new(*validator_list_storage, false),
2220        AccountMeta::new_readonly(*stake_pool_withdraw, false),
2221        AccountMeta::new(*stake_to_split, false),
2222        AccountMeta::new(*stake_to_receive, false),
2223        AccountMeta::new_readonly(*user_stake_authority, false),
2224        AccountMeta::new_readonly(*user_transfer_authority, true),
2225        AccountMeta::new(*user_pool_token_account, false),
2226        AccountMeta::new(*manager_fee_account, false),
2227        AccountMeta::new(*pool_mint, false),
2228        AccountMeta::new_readonly(solana_sdk_ids::sysvar::clock::id(), false),
2229        AccountMeta::new_readonly(*token_program_id, false),
2230        AccountMeta::new_readonly(solana_stake_interface::program::id(), false),
2231    ];
2232    if let Some(minimum_lamports_out) = minimum_lamports_out {
2233        Instruction {
2234            program_id: *program_id,
2235            accounts,
2236            data: borsh::to_vec(&StakePoolInstruction::WithdrawStakeWithSlippage {
2237                pool_tokens_in,
2238                minimum_lamports_out,
2239            })
2240            .unwrap(),
2241        }
2242    } else {
2243        Instruction {
2244            program_id: *program_id,
2245            accounts,
2246            data: borsh::to_vec(&StakePoolInstruction::WithdrawStake(pool_tokens_in)).unwrap(),
2247        }
2248    }
2249}
2250
2251/// Creates a `WithdrawStake` instruction.
2252pub fn withdraw_stake(
2253    program_id: &Pubkey,
2254    stake_pool: &Pubkey,
2255    validator_list_storage: &Pubkey,
2256    stake_pool_withdraw: &Pubkey,
2257    stake_to_split: &Pubkey,
2258    stake_to_receive: &Pubkey,
2259    user_stake_authority: &Pubkey,
2260    user_transfer_authority: &Pubkey,
2261    user_pool_token_account: &Pubkey,
2262    manager_fee_account: &Pubkey,
2263    pool_mint: &Pubkey,
2264    token_program_id: &Pubkey,
2265    pool_tokens_in: u64,
2266) -> Instruction {
2267    withdraw_stake_internal(
2268        program_id,
2269        stake_pool,
2270        validator_list_storage,
2271        stake_pool_withdraw,
2272        stake_to_split,
2273        stake_to_receive,
2274        user_stake_authority,
2275        user_transfer_authority,
2276        user_pool_token_account,
2277        manager_fee_account,
2278        pool_mint,
2279        token_program_id,
2280        pool_tokens_in,
2281        None,
2282    )
2283}
2284
2285/// Creates a `WithdrawStakeWithSlippage` instruction.
2286pub fn withdraw_stake_with_slippage(
2287    program_id: &Pubkey,
2288    stake_pool: &Pubkey,
2289    validator_list_storage: &Pubkey,
2290    stake_pool_withdraw: &Pubkey,
2291    stake_to_split: &Pubkey,
2292    stake_to_receive: &Pubkey,
2293    user_stake_authority: &Pubkey,
2294    user_transfer_authority: &Pubkey,
2295    user_pool_token_account: &Pubkey,
2296    manager_fee_account: &Pubkey,
2297    pool_mint: &Pubkey,
2298    token_program_id: &Pubkey,
2299    pool_tokens_in: u64,
2300    minimum_lamports_out: u64,
2301) -> Instruction {
2302    withdraw_stake_internal(
2303        program_id,
2304        stake_pool,
2305        validator_list_storage,
2306        stake_pool_withdraw,
2307        stake_to_split,
2308        stake_to_receive,
2309        user_stake_authority,
2310        user_transfer_authority,
2311        user_pool_token_account,
2312        manager_fee_account,
2313        pool_mint,
2314        token_program_id,
2315        pool_tokens_in,
2316        Some(minimum_lamports_out),
2317    )
2318}
2319
2320fn withdraw_sol_internal(
2321    program_id: &Pubkey,
2322    stake_pool: &Pubkey,
2323    stake_pool_withdraw_authority: &Pubkey,
2324    user_transfer_authority: &Pubkey,
2325    pool_tokens_from: &Pubkey,
2326    reserve_stake_account: &Pubkey,
2327    lamports_to: &Pubkey,
2328    manager_fee_account: &Pubkey,
2329    pool_mint: &Pubkey,
2330    token_program_id: &Pubkey,
2331    sol_withdraw_authority: Option<&Pubkey>,
2332    pool_tokens_in: u64,
2333    minimum_lamports_out: Option<u64>,
2334) -> Instruction {
2335    let mut accounts = vec![
2336        AccountMeta::new(*stake_pool, false),
2337        AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
2338        AccountMeta::new_readonly(*user_transfer_authority, true),
2339        AccountMeta::new(*pool_tokens_from, false),
2340        AccountMeta::new(*reserve_stake_account, false),
2341        AccountMeta::new(*lamports_to, false),
2342        AccountMeta::new(*manager_fee_account, false),
2343        AccountMeta::new(*pool_mint, false),
2344        AccountMeta::new_readonly(solana_sdk_ids::sysvar::clock::id(), false),
2345        AccountMeta::new_readonly(solana_sdk_ids::sysvar::stake_history::id(), false),
2346        AccountMeta::new_readonly(solana_stake_interface::program::id(), false),
2347        AccountMeta::new_readonly(*token_program_id, false),
2348    ];
2349    if let Some(sol_withdraw_authority) = sol_withdraw_authority {
2350        accounts.push(AccountMeta::new_readonly(*sol_withdraw_authority, true));
2351    }
2352    if let Some(minimum_lamports_out) = minimum_lamports_out {
2353        Instruction {
2354            program_id: *program_id,
2355            accounts,
2356            data: borsh::to_vec(&StakePoolInstruction::WithdrawSolWithSlippage {
2357                pool_tokens_in,
2358                minimum_lamports_out,
2359            })
2360            .unwrap(),
2361        }
2362    } else {
2363        Instruction {
2364            program_id: *program_id,
2365            accounts,
2366            data: borsh::to_vec(&StakePoolInstruction::WithdrawSol(pool_tokens_in)).unwrap(),
2367        }
2368    }
2369}
2370
2371/// Creates instruction required to withdraw SOL directly from a stake pool.
2372pub fn withdraw_sol(
2373    program_id: &Pubkey,
2374    stake_pool: &Pubkey,
2375    stake_pool_withdraw_authority: &Pubkey,
2376    user_transfer_authority: &Pubkey,
2377    pool_tokens_from: &Pubkey,
2378    reserve_stake_account: &Pubkey,
2379    lamports_to: &Pubkey,
2380    manager_fee_account: &Pubkey,
2381    pool_mint: &Pubkey,
2382    token_program_id: &Pubkey,
2383    pool_tokens_in: u64,
2384) -> Instruction {
2385    withdraw_sol_internal(
2386        program_id,
2387        stake_pool,
2388        stake_pool_withdraw_authority,
2389        user_transfer_authority,
2390        pool_tokens_from,
2391        reserve_stake_account,
2392        lamports_to,
2393        manager_fee_account,
2394        pool_mint,
2395        token_program_id,
2396        None,
2397        pool_tokens_in,
2398        None,
2399    )
2400}
2401
2402/// Creates instruction required to withdraw SOL directly from a stake pool with
2403/// slippage constraints.
2404pub fn withdraw_sol_with_slippage(
2405    program_id: &Pubkey,
2406    stake_pool: &Pubkey,
2407    stake_pool_withdraw_authority: &Pubkey,
2408    user_transfer_authority: &Pubkey,
2409    pool_tokens_from: &Pubkey,
2410    reserve_stake_account: &Pubkey,
2411    lamports_to: &Pubkey,
2412    manager_fee_account: &Pubkey,
2413    pool_mint: &Pubkey,
2414    token_program_id: &Pubkey,
2415    pool_tokens_in: u64,
2416    minimum_lamports_out: u64,
2417) -> Instruction {
2418    withdraw_sol_internal(
2419        program_id,
2420        stake_pool,
2421        stake_pool_withdraw_authority,
2422        user_transfer_authority,
2423        pool_tokens_from,
2424        reserve_stake_account,
2425        lamports_to,
2426        manager_fee_account,
2427        pool_mint,
2428        token_program_id,
2429        None,
2430        pool_tokens_in,
2431        Some(minimum_lamports_out),
2432    )
2433}
2434
2435/// Creates instruction required to withdraw SOL directly from a stake pool.
2436/// The difference with `withdraw_sol()` is that the sol withdraw authority
2437/// must sign this instruction.
2438pub fn withdraw_sol_with_authority(
2439    program_id: &Pubkey,
2440    stake_pool: &Pubkey,
2441    sol_withdraw_authority: &Pubkey,
2442    stake_pool_withdraw_authority: &Pubkey,
2443    user_transfer_authority: &Pubkey,
2444    pool_tokens_from: &Pubkey,
2445    reserve_stake_account: &Pubkey,
2446    lamports_to: &Pubkey,
2447    manager_fee_account: &Pubkey,
2448    pool_mint: &Pubkey,
2449    token_program_id: &Pubkey,
2450    pool_tokens_in: u64,
2451) -> Instruction {
2452    withdraw_sol_internal(
2453        program_id,
2454        stake_pool,
2455        stake_pool_withdraw_authority,
2456        user_transfer_authority,
2457        pool_tokens_from,
2458        reserve_stake_account,
2459        lamports_to,
2460        manager_fee_account,
2461        pool_mint,
2462        token_program_id,
2463        Some(sol_withdraw_authority),
2464        pool_tokens_in,
2465        None,
2466    )
2467}
2468
2469/// Creates instruction required to withdraw SOL directly from a stake pool with
2470/// a slippage constraint.
2471/// The difference with `withdraw_sol()` is that the sol withdraw authority
2472/// must sign this instruction.
2473pub fn withdraw_sol_with_authority_and_slippage(
2474    program_id: &Pubkey,
2475    stake_pool: &Pubkey,
2476    sol_withdraw_authority: &Pubkey,
2477    stake_pool_withdraw_authority: &Pubkey,
2478    user_transfer_authority: &Pubkey,
2479    pool_tokens_from: &Pubkey,
2480    reserve_stake_account: &Pubkey,
2481    lamports_to: &Pubkey,
2482    manager_fee_account: &Pubkey,
2483    pool_mint: &Pubkey,
2484    token_program_id: &Pubkey,
2485    pool_tokens_in: u64,
2486    minimum_lamports_out: u64,
2487) -> Instruction {
2488    withdraw_sol_internal(
2489        program_id,
2490        stake_pool,
2491        stake_pool_withdraw_authority,
2492        user_transfer_authority,
2493        pool_tokens_from,
2494        reserve_stake_account,
2495        lamports_to,
2496        manager_fee_account,
2497        pool_mint,
2498        token_program_id,
2499        Some(sol_withdraw_authority),
2500        pool_tokens_in,
2501        Some(minimum_lamports_out),
2502    )
2503}
2504
2505/// Creates a `SetManager` instruction.
2506pub fn set_manager(
2507    program_id: &Pubkey,
2508    stake_pool: &Pubkey,
2509    manager: &Pubkey,
2510    new_manager: &Pubkey,
2511    new_fee_receiver: &Pubkey,
2512) -> Instruction {
2513    let accounts = vec![
2514        AccountMeta::new(*stake_pool, false),
2515        AccountMeta::new_readonly(*manager, true),
2516        AccountMeta::new_readonly(*new_manager, true),
2517        AccountMeta::new_readonly(*new_fee_receiver, false),
2518    ];
2519    Instruction {
2520        program_id: *program_id,
2521        accounts,
2522        data: borsh::to_vec(&StakePoolInstruction::SetManager).unwrap(),
2523    }
2524}
2525
2526/// Creates a `SetFee` instruction.
2527pub fn set_fee(
2528    program_id: &Pubkey,
2529    stake_pool: &Pubkey,
2530    manager: &Pubkey,
2531    fee: FeeType,
2532) -> Instruction {
2533    let accounts = vec![
2534        AccountMeta::new(*stake_pool, false),
2535        AccountMeta::new_readonly(*manager, true),
2536    ];
2537    Instruction {
2538        program_id: *program_id,
2539        accounts,
2540        data: borsh::to_vec(&StakePoolInstruction::SetFee { fee }).unwrap(),
2541    }
2542}
2543
2544/// Creates a `SetStaker` instruction.
2545pub fn set_staker(
2546    program_id: &Pubkey,
2547    stake_pool: &Pubkey,
2548    set_staker_authority: &Pubkey,
2549    new_staker: &Pubkey,
2550) -> Instruction {
2551    let accounts = vec![
2552        AccountMeta::new(*stake_pool, false),
2553        AccountMeta::new_readonly(*set_staker_authority, true),
2554        AccountMeta::new_readonly(*new_staker, false),
2555    ];
2556    Instruction {
2557        program_id: *program_id,
2558        accounts,
2559        data: borsh::to_vec(&StakePoolInstruction::SetStaker).unwrap(),
2560    }
2561}
2562
2563/// Creates a `SetFundingAuthority` instruction.
2564pub fn set_funding_authority(
2565    program_id: &Pubkey,
2566    stake_pool: &Pubkey,
2567    manager: &Pubkey,
2568    new_sol_deposit_authority: Option<&Pubkey>,
2569    funding_type: FundingType,
2570) -> Instruction {
2571    let mut accounts = vec![
2572        AccountMeta::new(*stake_pool, false),
2573        AccountMeta::new_readonly(*manager, true),
2574    ];
2575    if let Some(auth) = new_sol_deposit_authority {
2576        accounts.push(AccountMeta::new_readonly(*auth, false))
2577    }
2578    Instruction {
2579        program_id: *program_id,
2580        accounts,
2581        data: borsh::to_vec(&StakePoolInstruction::SetFundingAuthority(funding_type)).unwrap(),
2582    }
2583}
2584
2585/// Creates an instruction to update metadata in the mpl token metadata program
2586/// account for the pool token
2587pub fn update_token_metadata(
2588    program_id: &Pubkey,
2589    stake_pool: &Pubkey,
2590    manager: &Pubkey,
2591    pool_mint: &Pubkey,
2592    name: String,
2593    symbol: String,
2594    uri: String,
2595) -> Instruction {
2596    let (stake_pool_withdraw_authority, _) =
2597        find_withdraw_authority_program_address(program_id, stake_pool);
2598    let (token_metadata, _) = find_metadata_account(pool_mint);
2599
2600    let accounts = vec![
2601        AccountMeta::new_readonly(*stake_pool, false),
2602        AccountMeta::new_readonly(*manager, true),
2603        AccountMeta::new_readonly(stake_pool_withdraw_authority, false),
2604        AccountMeta::new(token_metadata, false),
2605        AccountMeta::new_readonly(inline_mpl_token_metadata::id(), false),
2606    ];
2607
2608    Instruction {
2609        program_id: *program_id,
2610        accounts,
2611        data: borsh::to_vec(&StakePoolInstruction::UpdateTokenMetadata { name, symbol, uri })
2612            .unwrap(),
2613    }
2614}
2615
2616/// Creates an instruction to create metadata using the mpl token metadata
2617/// program for the pool token
2618pub fn create_token_metadata(
2619    program_id: &Pubkey,
2620    stake_pool: &Pubkey,
2621    manager: &Pubkey,
2622    pool_mint: &Pubkey,
2623    payer: &Pubkey,
2624    name: String,
2625    symbol: String,
2626    uri: String,
2627) -> Instruction {
2628    let (stake_pool_withdraw_authority, _) =
2629        find_withdraw_authority_program_address(program_id, stake_pool);
2630    let (token_metadata, _) = find_metadata_account(pool_mint);
2631
2632    let accounts = vec![
2633        AccountMeta::new_readonly(*stake_pool, false),
2634        AccountMeta::new_readonly(*manager, true),
2635        AccountMeta::new_readonly(stake_pool_withdraw_authority, false),
2636        AccountMeta::new_readonly(*pool_mint, false),
2637        AccountMeta::new(*payer, true),
2638        AccountMeta::new(token_metadata, false),
2639        AccountMeta::new_readonly(inline_mpl_token_metadata::id(), false),
2640        AccountMeta::new_readonly(solana_system_interface::program::id(), false),
2641    ];
2642
2643    Instruction {
2644        program_id: *program_id,
2645        accounts,
2646        data: borsh::to_vec(&StakePoolInstruction::CreateTokenMetadata { name, symbol, uri })
2647            .unwrap(),
2648    }
2649}