Skip to main content

spl_stake_pool/
processor.rs

1//! Program state processor
2
3use {
4    crate::{
5        error::StakePoolError,
6        find_deposit_authority_program_address,
7        inline_mpl_token_metadata::{
8            self,
9            instruction::{create_metadata_accounts_v3, update_metadata_accounts_v2},
10            pda::find_metadata_account,
11            state::DataV2,
12        },
13        instruction::{FundingType, PreferredValidatorType, StakePoolInstruction},
14        minimum_delegation, minimum_reserve_lamports, minimum_stake_lamports,
15        state::{
16            is_extension_supported_for_mint, AccountType, Fee, FeeType, FutureEpoch, StakePool,
17            StakeStatus, StakeWithdrawSource, ValidatorList, ValidatorListHeader,
18            ValidatorStakeInfo,
19        },
20        AUTHORITY_DEPOSIT, AUTHORITY_WITHDRAW, EPHEMERAL_STAKE_SEED_PREFIX, MAX_VALIDATORS_IN_POOL,
21        TRANSIENT_STAKE_SEED_PREFIX,
22    },
23    borsh::BorshDeserialize,
24    solana_account_info::{next_account_info, AccountInfo},
25    solana_borsh::v1::try_from_slice_unchecked,
26    solana_clock::{Clock, Epoch},
27    solana_cpi::{invoke, invoke_signed},
28    solana_epoch_rewards::EpochRewards,
29    solana_msg::msg,
30    solana_program_error::{ProgramError, ProgramResult},
31    solana_pubkey::Pubkey,
32    solana_rent::Rent,
33    solana_stake_interface as stake,
34    solana_system_interface::{instruction as system_instruction, program as system_program},
35    solana_sysvar::{Sysvar, SysvarSerialize},
36    spl_token_2022_interface::{
37        check_spl_token_program_account,
38        extension::{BaseStateWithExtensions, StateWithExtensions},
39        native_mint,
40        state::Mint,
41    },
42    std::num::NonZeroU32,
43};
44
45/// Deserialize the stake state from `AccountInfo`
46fn get_stake_state(
47    stake_account_info: &AccountInfo,
48) -> Result<(stake::state::Meta, stake::state::Stake), ProgramError> {
49    let stake_state =
50        try_from_slice_unchecked::<stake::state::StakeStateV2>(&stake_account_info.data.borrow())?;
51    match stake_state {
52        stake::state::StakeStateV2::Stake(meta, stake, _) => Ok((meta, stake)),
53        _ => Err(StakePoolError::WrongStakeStake.into()),
54    }
55}
56
57/// Check validity of vote address for a particular stake account
58fn check_validator_stake_address(
59    program_id: &Pubkey,
60    stake_pool_address: &Pubkey,
61    stake_account_address: &Pubkey,
62    vote_address: &Pubkey,
63    seed: Option<NonZeroU32>,
64) -> Result<(), ProgramError> {
65    // Check stake account address validity
66    let (validator_stake_address, _) =
67        crate::find_stake_program_address(program_id, vote_address, stake_pool_address, seed);
68    if validator_stake_address != *stake_account_address {
69        msg!(
70            "Incorrect stake account address for vote {}, expected {}, received {}",
71            vote_address,
72            validator_stake_address,
73            stake_account_address
74        );
75        Err(StakePoolError::InvalidStakeAccountAddress.into())
76    } else {
77        Ok(())
78    }
79}
80
81/// Check validity of vote address for a particular stake account
82fn check_transient_stake_address(
83    program_id: &Pubkey,
84    stake_pool_address: &Pubkey,
85    stake_account_address: &Pubkey,
86    vote_address: &Pubkey,
87    seed: u64,
88) -> Result<u8, ProgramError> {
89    // Check stake account address validity
90    let (transient_stake_address, bump_seed) = crate::find_transient_stake_program_address(
91        program_id,
92        vote_address,
93        stake_pool_address,
94        seed,
95    );
96    if transient_stake_address != *stake_account_address {
97        Err(StakePoolError::InvalidStakeAccountAddress.into())
98    } else {
99        Ok(bump_seed)
100    }
101}
102
103/// Check address validity for an ephemeral stake account
104fn check_ephemeral_stake_address(
105    program_id: &Pubkey,
106    stake_pool_address: &Pubkey,
107    stake_account_address: &Pubkey,
108    seed: u64,
109) -> Result<u8, ProgramError> {
110    // Check stake account address validity
111    let (ephemeral_stake_address, bump_seed) =
112        crate::find_ephemeral_stake_program_address(program_id, stake_pool_address, seed);
113    if ephemeral_stake_address != *stake_account_address {
114        Err(StakePoolError::InvalidStakeAccountAddress.into())
115    } else {
116        Ok(bump_seed)
117    }
118}
119
120/// Check mpl metadata account address for the pool mint
121fn check_mpl_metadata_account_address(
122    metadata_address: &Pubkey,
123    pool_mint: &Pubkey,
124) -> Result<(), ProgramError> {
125    let (metadata_account_pubkey, _) = find_metadata_account(pool_mint);
126    if metadata_account_pubkey != *metadata_address {
127        Err(StakePoolError::InvalidMetadataAccount.into())
128    } else {
129        Ok(())
130    }
131}
132
133/// Check system program address
134fn check_system_program(program_id: &Pubkey) -> Result<(), ProgramError> {
135    if *program_id != system_program::id() {
136        msg!(
137            "Expected system program {}, received {}",
138            system_program::id(),
139            program_id
140        );
141        Err(ProgramError::IncorrectProgramId)
142    } else {
143        Ok(())
144    }
145}
146
147/// Check stake program address
148fn check_stake_program(program_id: &Pubkey) -> Result<(), ProgramError> {
149    if *program_id != stake::program::id() {
150        msg!(
151            "Expected stake program {}, received {}",
152            stake::program::id(),
153            program_id
154        );
155        Err(ProgramError::IncorrectProgramId)
156    } else {
157        Ok(())
158    }
159}
160
161/// Check mpl metadata program
162fn check_mpl_metadata_program(program_id: &Pubkey) -> Result<(), ProgramError> {
163    if *program_id != inline_mpl_token_metadata::id() {
164        msg!(
165            "Expected mpl metadata program {}, received {}",
166            inline_mpl_token_metadata::id(),
167            program_id
168        );
169        Err(ProgramError::IncorrectProgramId)
170    } else {
171        Ok(())
172    }
173}
174
175/// Check account owner is the given program
176fn check_account_owner(
177    account_info: &AccountInfo,
178    program_id: &Pubkey,
179) -> Result<(), ProgramError> {
180    if *program_id != *account_info.owner {
181        msg!(
182            "Expected account to be owned by program {}, received {}",
183            program_id,
184            account_info.owner
185        );
186        Err(ProgramError::IncorrectProgramId)
187    } else {
188        Ok(())
189    }
190}
191
192/// Checks if a stake account can be managed by the pool
193fn stake_is_usable_by_pool(
194    meta: &stake::state::Meta,
195    expected_authority: &Pubkey,
196    expected_lockup: &stake::state::Lockup,
197) -> bool {
198    meta.authorized.staker == *expected_authority
199        && meta.authorized.withdrawer == *expected_authority
200        && meta.lockup == *expected_lockup
201}
202
203/// Checks if a stake account is active, without taking into account cool down
204fn stake_is_inactive_without_history(stake: &stake::state::Stake, epoch: Epoch) -> bool {
205    stake.delegation.deactivation_epoch < epoch
206        || (stake.delegation.activation_epoch == epoch
207            && stake.delegation.deactivation_epoch == epoch)
208}
209
210/// Roughly checks if a stake account is deactivating
211fn check_if_stake_deactivating(
212    account_info: &AccountInfo,
213    vote_account_address: &Pubkey,
214    epoch: Epoch,
215) -> Result<(), ProgramError> {
216    let (_, stake) = get_stake_state(account_info)?;
217    if stake.delegation.deactivation_epoch != epoch {
218        msg!(
219            "Existing stake {} delegated to {} not deactivated in epoch {}",
220            account_info.key,
221            vote_account_address,
222            epoch,
223        );
224        Err(StakePoolError::WrongStakeStake.into())
225    } else {
226        Ok(())
227    }
228}
229
230/// Roughly checks if a stake account is activating
231fn check_if_stake_activating(
232    account_info: &AccountInfo,
233    vote_account_address: &Pubkey,
234    epoch: Epoch,
235) -> Result<(), ProgramError> {
236    let (_, stake) = get_stake_state(account_info)?;
237    if stake.delegation.deactivation_epoch != Epoch::MAX
238        || stake.delegation.activation_epoch != epoch
239    {
240        msg!(
241            "Existing stake {} delegated to {} not activated in epoch {}",
242            account_info.key,
243            vote_account_address,
244            epoch,
245        );
246        Err(StakePoolError::WrongStakeStake.into())
247    } else {
248        Ok(())
249    }
250}
251
252/// Check that the stake state is correct: usable by the pool and delegated to
253/// the expected validator
254fn check_stake_state(
255    stake_account_info: &AccountInfo,
256    withdraw_authority: &Pubkey,
257    vote_account_address: &Pubkey,
258    lockup: &stake::state::Lockup,
259) -> Result<(), ProgramError> {
260    let (meta, stake) = get_stake_state(stake_account_info)?;
261    if !stake_is_usable_by_pool(&meta, withdraw_authority, lockup) {
262        msg!(
263            "Validator stake for {} not usable by pool, must be owned by withdraw authority",
264            vote_account_address
265        );
266        return Err(StakePoolError::WrongStakeStake.into());
267    }
268    if stake.delegation.voter_pubkey != *vote_account_address {
269        msg!(
270            "Validator stake {} not delegated to {}",
271            stake_account_info.key,
272            vote_account_address
273        );
274        return Err(StakePoolError::WrongStakeStake.into());
275    }
276    Ok(())
277}
278
279/// Checks if a validator stake account is valid, which means that it's usable
280/// by the pool and delegated to the expected validator. These conditions can be
281/// violated if a validator was force destaked during a cluster restart.
282fn check_validator_stake_account(
283    stake_account_info: &AccountInfo,
284    program_id: &Pubkey,
285    stake_pool: &Pubkey,
286    withdraw_authority: &Pubkey,
287    vote_account_address: &Pubkey,
288    seed: u32,
289    lockup: &stake::state::Lockup,
290) -> Result<(), ProgramError> {
291    check_account_owner(stake_account_info, &stake::program::id())?;
292    check_validator_stake_address(
293        program_id,
294        stake_pool,
295        stake_account_info.key,
296        vote_account_address,
297        NonZeroU32::new(seed),
298    )?;
299    check_stake_state(
300        stake_account_info,
301        withdraw_authority,
302        vote_account_address,
303        lockup,
304    )?;
305    Ok(())
306}
307
308/// Create a stake account on a PDA without transferring lamports
309fn create_stake_account(
310    stake_account_info: AccountInfo<'_>,
311    stake_account_signer_seeds: &[&[u8]],
312    stake_space: usize,
313) -> Result<(), ProgramError> {
314    invoke_signed(
315        &system_instruction::allocate(stake_account_info.key, stake_space as u64),
316        core::slice::from_ref(&stake_account_info),
317        &[stake_account_signer_seeds],
318    )?;
319    invoke_signed(
320        &system_instruction::assign(stake_account_info.key, &stake::program::id()),
321        &[stake_account_info],
322        &[stake_account_signer_seeds],
323    )
324}
325
326/// Program state handler.
327pub struct Processor {}
328impl Processor {
329    /// Issue a `delegate_stake` instruction.
330    #[allow(clippy::too_many_arguments)]
331    fn stake_delegate<'a>(
332        stake_info: AccountInfo<'a>,
333        vote_account_info: AccountInfo<'a>,
334        clock_info: AccountInfo<'a>,
335        stake_history_info: AccountInfo<'a>,
336        stake_config_info: AccountInfo<'a>,
337        authority_info: AccountInfo<'a>,
338        stake_pool: &Pubkey,
339        authority_type: &[u8],
340        bump_seed: u8,
341    ) -> Result<(), ProgramError> {
342        let authority_signature_seeds = [stake_pool.as_ref(), authority_type, &[bump_seed]];
343        let signers = &[&authority_signature_seeds[..]];
344
345        let ix = stake::instruction::delegate_stake(
346            stake_info.key,
347            authority_info.key,
348            vote_account_info.key,
349        );
350
351        invoke_signed(
352            &ix,
353            &[
354                stake_info,
355                vote_account_info,
356                clock_info,
357                stake_history_info,
358                stake_config_info,
359                authority_info,
360            ],
361            signers,
362        )
363    }
364
365    /// Issue a `stake_deactivate` instruction.
366    fn stake_deactivate<'a>(
367        stake_info: AccountInfo<'a>,
368        clock_info: AccountInfo<'a>,
369        authority_info: AccountInfo<'a>,
370        stake_pool: &Pubkey,
371        authority_type: &[u8],
372        bump_seed: u8,
373    ) -> Result<(), ProgramError> {
374        let authority_signature_seeds = [stake_pool.as_ref(), authority_type, &[bump_seed]];
375        let signers = &[&authority_signature_seeds[..]];
376
377        let ix = stake::instruction::deactivate_stake(stake_info.key, authority_info.key);
378
379        invoke_signed(&ix, &[stake_info, clock_info, authority_info], signers)
380    }
381
382    /// Issue a `stake_split` instruction.
383    fn stake_split<'a>(
384        stake_pool: &Pubkey,
385        stake_account: AccountInfo<'a>,
386        authority: AccountInfo<'a>,
387        authority_type: &[u8],
388        bump_seed: u8,
389        amount: u64,
390        split_stake: AccountInfo<'a>,
391    ) -> Result<(), ProgramError> {
392        let authority_signature_seeds = [stake_pool.as_ref(), authority_type, &[bump_seed]];
393        let signers = &[&authority_signature_seeds[..]];
394
395        let split_instruction =
396            stake::instruction::split(stake_account.key, authority.key, amount, split_stake.key);
397
398        invoke_signed(
399            split_instruction
400                .last()
401                .ok_or(ProgramError::InvalidInstructionData)?,
402            &[stake_account, split_stake, authority],
403            signers,
404        )
405    }
406
407    /// Issue a `stake_merge` instruction.
408    #[allow(clippy::too_many_arguments)]
409    fn stake_merge<'a>(
410        stake_pool: &Pubkey,
411        source_account: AccountInfo<'a>,
412        authority: AccountInfo<'a>,
413        authority_type: &[u8],
414        bump_seed: u8,
415        destination_account: AccountInfo<'a>,
416        clock: AccountInfo<'a>,
417        stake_history: AccountInfo<'a>,
418    ) -> Result<(), ProgramError> {
419        let authority_signature_seeds = [stake_pool.as_ref(), authority_type, &[bump_seed]];
420        let signers = &[&authority_signature_seeds[..]];
421
422        let merge_instruction =
423            stake::instruction::merge(destination_account.key, source_account.key, authority.key);
424
425        invoke_signed(
426            &merge_instruction[0],
427            &[
428                destination_account,
429                source_account,
430                clock,
431                stake_history,
432                authority,
433            ],
434            signers,
435        )
436    }
437
438    /// Issue stake::instruction::authorize instructions to update both
439    /// authorities
440    fn stake_authorize<'a>(
441        stake_account: AccountInfo<'a>,
442        stake_authority: AccountInfo<'a>,
443        new_stake_authority: &Pubkey,
444        clock: AccountInfo<'a>,
445    ) -> Result<(), ProgramError> {
446        let authorize_instruction = stake::instruction::authorize(
447            stake_account.key,
448            stake_authority.key,
449            new_stake_authority,
450            stake::state::StakeAuthorize::Staker,
451            None,
452        );
453
454        invoke(
455            &authorize_instruction,
456            &[
457                stake_account.clone(),
458                clock.clone(),
459                stake_authority.clone(),
460            ],
461        )?;
462
463        let authorize_instruction = stake::instruction::authorize(
464            stake_account.key,
465            stake_authority.key,
466            new_stake_authority,
467            stake::state::StakeAuthorize::Withdrawer,
468            None,
469        );
470
471        invoke(
472            &authorize_instruction,
473            &[stake_account, clock, stake_authority],
474        )
475    }
476
477    /// Issue stake::instruction::authorize instructions to update both
478    /// authorities
479    #[allow(clippy::too_many_arguments)]
480    fn stake_authorize_signed<'a>(
481        stake_pool: &Pubkey,
482        stake_account: AccountInfo<'a>,
483        stake_authority: AccountInfo<'a>,
484        authority_type: &[u8],
485        bump_seed: u8,
486        new_stake_authority: &Pubkey,
487        clock: AccountInfo<'a>,
488    ) -> Result<(), ProgramError> {
489        let authority_signature_seeds = [stake_pool.as_ref(), authority_type, &[bump_seed]];
490        let signers = &[&authority_signature_seeds[..]];
491
492        let authorize_instruction = stake::instruction::authorize(
493            stake_account.key,
494            stake_authority.key,
495            new_stake_authority,
496            stake::state::StakeAuthorize::Staker,
497            None,
498        );
499
500        invoke_signed(
501            &authorize_instruction,
502            &[
503                stake_account.clone(),
504                clock.clone(),
505                stake_authority.clone(),
506            ],
507            signers,
508        )?;
509
510        let authorize_instruction = stake::instruction::authorize(
511            stake_account.key,
512            stake_authority.key,
513            new_stake_authority,
514            stake::state::StakeAuthorize::Withdrawer,
515            None,
516        );
517        invoke_signed(
518            &authorize_instruction,
519            &[stake_account, clock, stake_authority],
520            signers,
521        )
522    }
523
524    /// Issue stake::instruction::withdraw instruction to move additional
525    /// lamports
526    #[allow(clippy::too_many_arguments)]
527    fn stake_withdraw<'a>(
528        stake_pool: &Pubkey,
529        source_account: AccountInfo<'a>,
530        authority: AccountInfo<'a>,
531        authority_type: &[u8],
532        bump_seed: u8,
533        destination_account: AccountInfo<'a>,
534        clock: AccountInfo<'a>,
535        stake_history: AccountInfo<'a>,
536        lamports: u64,
537    ) -> Result<(), ProgramError> {
538        let authority_signature_seeds = [stake_pool.as_ref(), authority_type, &[bump_seed]];
539        let signers = &[&authority_signature_seeds[..]];
540        let custodian_pubkey = None;
541
542        let withdraw_instruction = stake::instruction::withdraw(
543            source_account.key,
544            authority.key,
545            destination_account.key,
546            lamports,
547            custodian_pubkey,
548        );
549
550        invoke_signed(
551            &withdraw_instruction,
552            &[
553                source_account,
554                destination_account,
555                clock,
556                stake_history,
557                authority,
558            ],
559            signers,
560        )
561    }
562
563    /// Issue a SPL Token `Burn` instruction.
564    #[allow(clippy::too_many_arguments)]
565    fn token_burn<'a>(
566        token_program: AccountInfo<'a>,
567        burn_account: AccountInfo<'a>,
568        mint: AccountInfo<'a>,
569        authority: AccountInfo<'a>,
570        amount: u64,
571    ) -> Result<(), ProgramError> {
572        let ix = spl_token_2022_interface::instruction::burn(
573            token_program.key,
574            burn_account.key,
575            mint.key,
576            authority.key,
577            &[],
578            amount,
579        )?;
580
581        invoke(&ix, &[burn_account, mint, authority])
582    }
583
584    /// Issue a SPL Token `MintTo` instruction.
585    #[allow(clippy::too_many_arguments)]
586    fn token_mint_to<'a>(
587        stake_pool: &Pubkey,
588        token_program: AccountInfo<'a>,
589        mint: AccountInfo<'a>,
590        destination: AccountInfo<'a>,
591        authority: AccountInfo<'a>,
592        authority_type: &[u8],
593        bump_seed: u8,
594        amount: u64,
595    ) -> Result<(), ProgramError> {
596        let authority_signature_seeds = [stake_pool.as_ref(), authority_type, &[bump_seed]];
597        let signers = &[&authority_signature_seeds[..]];
598
599        let ix = spl_token_2022_interface::instruction::mint_to(
600            token_program.key,
601            mint.key,
602            destination.key,
603            authority.key,
604            &[],
605            amount,
606        )?;
607
608        invoke_signed(&ix, &[mint, destination, authority], signers)
609    }
610
611    /// Issue a SPL Token `Transfer` instruction.
612    #[allow(clippy::too_many_arguments)]
613    fn token_transfer<'a>(
614        token_program: AccountInfo<'a>,
615        source: AccountInfo<'a>,
616        mint: AccountInfo<'a>,
617        destination: AccountInfo<'a>,
618        authority: AccountInfo<'a>,
619        amount: u64,
620        decimals: u8,
621    ) -> Result<(), ProgramError> {
622        let ix = spl_token_2022_interface::instruction::transfer_checked(
623            token_program.key,
624            source.key,
625            mint.key,
626            destination.key,
627            authority.key,
628            &[],
629            amount,
630            decimals,
631        )?;
632        invoke(&ix, &[source, mint, destination, authority])
633    }
634
635    fn sol_transfer<'a>(
636        source: AccountInfo<'a>,
637        destination: AccountInfo<'a>,
638        amount: u64,
639    ) -> Result<(), ProgramError> {
640        let ix = system_instruction::transfer(source.key, destination.key, amount);
641        invoke(&ix, &[source, destination])
642    }
643
644    /// Processes `Initialize` instruction.
645    #[inline(never)] // needed due to stack size violation
646    fn process_initialize(
647        program_id: &Pubkey,
648        accounts: &[AccountInfo],
649        epoch_fee: Fee,
650        withdrawal_fee: Fee,
651        deposit_fee: Fee,
652        referral_fee: u8,
653        max_validators: u32,
654    ) -> ProgramResult {
655        let account_info_iter = &mut accounts.iter();
656        let stake_pool_info = next_account_info(account_info_iter)?;
657        let manager_info = next_account_info(account_info_iter)?;
658        let staker_info = next_account_info(account_info_iter)?;
659        let withdraw_authority_info = next_account_info(account_info_iter)?;
660        let validator_list_info = next_account_info(account_info_iter)?;
661        let reserve_stake_info = next_account_info(account_info_iter)?;
662        let pool_mint_info = next_account_info(account_info_iter)?;
663        let manager_fee_info = next_account_info(account_info_iter)?;
664        let token_program_info = next_account_info(account_info_iter)?;
665
666        let rent = Rent::get()?;
667
668        if !manager_info.is_signer {
669            msg!("Manager did not sign initialization");
670            return Err(StakePoolError::SignatureMissing.into());
671        }
672
673        if stake_pool_info.key == validator_list_info.key {
674            msg!("Cannot use same account for stake pool and validator list");
675            return Err(StakePoolError::AlreadyInUse.into());
676        }
677
678        // This check is unnecessary since the runtime will check the ownership,
679        // but provides clarity that the parameter is in fact checked.
680        check_account_owner(stake_pool_info, program_id)?;
681        let mut stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
682        if !stake_pool.is_uninitialized() {
683            msg!("Provided stake pool already in use");
684            return Err(StakePoolError::AlreadyInUse.into());
685        }
686
687        // This check is unnecessary since the runtime will check the ownership,
688        // but provides clarity that the parameter is in fact checked.
689        check_account_owner(validator_list_info, program_id)?;
690        let mut validator_list =
691            try_from_slice_unchecked::<ValidatorList>(&validator_list_info.data.borrow())?;
692        if !validator_list.header.is_uninitialized() {
693            msg!("Provided validator list already in use");
694            return Err(StakePoolError::AlreadyInUse.into());
695        }
696
697        let data_length = validator_list_info.data_len();
698        let expected_max_validators = ValidatorList::calculate_max_validators(data_length);
699        if expected_max_validators != max_validators as usize || max_validators == 0 {
700            msg!(
701                "Incorrect validator list size provided, expected {}, provided {}",
702                expected_max_validators,
703                max_validators
704            );
705            return Err(StakePoolError::UnexpectedValidatorListAccountSize.into());
706        }
707        if max_validators > MAX_VALIDATORS_IN_POOL {
708            return Err(StakePoolError::TooManyValidatorsInPool.into());
709        }
710        validator_list.header.account_type = AccountType::ValidatorList;
711        validator_list.header.max_validators = max_validators;
712        validator_list.validators.clear();
713
714        if !rent.is_exempt(stake_pool_info.lamports(), stake_pool_info.data_len()) {
715            msg!("Stake pool not rent-exempt");
716            return Err(ProgramError::AccountNotRentExempt);
717        }
718
719        if !rent.is_exempt(
720            validator_list_info.lamports(),
721            validator_list_info.data_len(),
722        ) {
723            msg!("Validator stake list not rent-exempt");
724            return Err(ProgramError::AccountNotRentExempt);
725        }
726
727        // Numerator should be smaller than or equal to denominator (fee <= 1)
728        if epoch_fee.numerator > epoch_fee.denominator
729            || withdrawal_fee.numerator > withdrawal_fee.denominator
730            || deposit_fee.numerator > deposit_fee.denominator
731            || referral_fee > 100u8
732        {
733            return Err(StakePoolError::FeeTooHigh.into());
734        }
735
736        check_spl_token_program_account(token_program_info.key)?;
737
738        if pool_mint_info.owner != token_program_info.key {
739            return Err(ProgramError::IncorrectProgramId);
740        }
741
742        stake_pool.token_program_id = *token_program_info.key;
743        stake_pool.pool_mint = *pool_mint_info.key;
744
745        let (stake_deposit_authority, sol_deposit_authority) =
746            match next_account_info(account_info_iter) {
747                Ok(deposit_authority_info) => (
748                    *deposit_authority_info.key,
749                    Some(*deposit_authority_info.key),
750                ),
751                Err(_) => (
752                    find_deposit_authority_program_address(program_id, stake_pool_info.key).0,
753                    None,
754                ),
755            };
756        let (withdraw_authority_key, stake_withdraw_bump_seed) =
757            crate::find_withdraw_authority_program_address(program_id, stake_pool_info.key);
758        if withdraw_authority_key != *withdraw_authority_info.key {
759            msg!(
760                "Incorrect withdraw authority provided, expected {}, received {}",
761                withdraw_authority_key,
762                withdraw_authority_info.key
763            );
764            return Err(StakePoolError::InvalidProgramAddress.into());
765        }
766
767        {
768            let pool_mint_data = pool_mint_info.try_borrow_data()?;
769            let pool_mint = StateWithExtensions::<Mint>::unpack(&pool_mint_data)?;
770
771            if pool_mint.base.supply != 0 {
772                return Err(StakePoolError::NonZeroPoolTokenSupply.into());
773            }
774
775            if pool_mint.base.decimals != native_mint::DECIMALS {
776                return Err(StakePoolError::IncorrectMintDecimals.into());
777            }
778
779            if !pool_mint
780                .base
781                .mint_authority
782                .contains(&withdraw_authority_key)
783            {
784                return Err(StakePoolError::WrongMintingAuthority.into());
785            }
786
787            let extensions = pool_mint.get_extension_types()?;
788            if extensions
789                .iter()
790                .any(|x| !is_extension_supported_for_mint(x))
791            {
792                return Err(StakePoolError::UnsupportedMintExtension.into());
793            }
794        }
795        stake_pool.check_manager_fee_info(manager_fee_info)?;
796
797        if *reserve_stake_info.owner != stake::program::id() {
798            msg!("Reserve stake account not owned by stake program");
799            return Err(ProgramError::IncorrectProgramId);
800        }
801        let reserve_rent = rent.minimum_balance(reserve_stake_info.data_len());
802        let stake_state = try_from_slice_unchecked::<stake::state::StakeStateV2>(
803            &reserve_stake_info.data.borrow(),
804        )?;
805        let total_lamports = if let stake::state::StakeStateV2::Initialized(meta) = stake_state {
806            if meta.lockup != stake::state::Lockup::default() {
807                msg!("Reserve stake account has some lockup");
808                return Err(StakePoolError::WrongStakeStake.into());
809            }
810
811            if meta.authorized.staker != withdraw_authority_key {
812                msg!(
813                    "Reserve stake account has incorrect staker {}, should be {}",
814                    meta.authorized.staker,
815                    withdraw_authority_key
816                );
817                return Err(StakePoolError::WrongStakeStake.into());
818            }
819
820            if meta.authorized.withdrawer != withdraw_authority_key {
821                msg!(
822                    "Reserve stake account has incorrect withdrawer {}, should be {}",
823                    meta.authorized.staker,
824                    withdraw_authority_key
825                );
826                return Err(StakePoolError::WrongStakeStake.into());
827            }
828            reserve_stake_info
829                .lamports()
830                .checked_sub(minimum_reserve_lamports(reserve_rent))
831                .ok_or(StakePoolError::CalculationFailure)?
832        } else {
833            msg!("Reserve stake account not in intialized state");
834            return Err(StakePoolError::WrongStakeStake.into());
835        };
836
837        if total_lamports > 0 {
838            Self::token_mint_to(
839                stake_pool_info.key,
840                token_program_info.clone(),
841                pool_mint_info.clone(),
842                manager_fee_info.clone(),
843                withdraw_authority_info.clone(),
844                AUTHORITY_WITHDRAW,
845                stake_withdraw_bump_seed,
846                total_lamports,
847            )?;
848        }
849
850        borsh::to_writer(
851            &mut validator_list_info.data.borrow_mut()[..],
852            &validator_list,
853        )?;
854
855        stake_pool.account_type = AccountType::StakePool;
856        stake_pool.manager = *manager_info.key;
857        stake_pool.staker = *staker_info.key;
858        stake_pool.stake_deposit_authority = stake_deposit_authority;
859        stake_pool.stake_withdraw_bump_seed = stake_withdraw_bump_seed;
860        stake_pool.validator_list = *validator_list_info.key;
861        stake_pool.reserve_stake = *reserve_stake_info.key;
862        stake_pool.manager_fee_account = *manager_fee_info.key;
863        stake_pool.total_lamports = total_lamports;
864        stake_pool.pool_token_supply = total_lamports;
865        stake_pool.last_update_epoch = Clock::get()?.epoch;
866        stake_pool.lockup = stake::state::Lockup::default();
867        stake_pool.epoch_fee = epoch_fee;
868        stake_pool.next_epoch_fee = FutureEpoch::None;
869        stake_pool.preferred_deposit_validator_vote_address = None;
870        stake_pool.preferred_withdraw_validator_vote_address = None;
871        stake_pool.stake_deposit_fee = deposit_fee;
872        stake_pool.stake_withdrawal_fee = withdrawal_fee;
873        stake_pool.next_stake_withdrawal_fee = FutureEpoch::None;
874        stake_pool.stake_referral_fee = referral_fee;
875        stake_pool.sol_deposit_authority = sol_deposit_authority;
876        stake_pool.sol_deposit_fee = deposit_fee;
877        stake_pool.sol_referral_fee = referral_fee;
878        stake_pool.sol_withdraw_authority = None;
879        stake_pool.sol_withdrawal_fee = withdrawal_fee;
880        stake_pool.next_sol_withdrawal_fee = FutureEpoch::None;
881        stake_pool.last_epoch_pool_token_supply = 0;
882        stake_pool.last_epoch_total_lamports = 0;
883
884        borsh::to_writer(&mut stake_pool_info.data.borrow_mut()[..], &stake_pool)
885            .map_err(|e| e.into())
886    }
887
888    /// Processes `AddValidatorToPool` instruction.
889    #[inline(never)] // needed due to stack size violation
890    fn process_add_validator_to_pool(
891        program_id: &Pubkey,
892        accounts: &[AccountInfo],
893        raw_validator_seed: u32,
894    ) -> ProgramResult {
895        let account_info_iter = &mut accounts.iter();
896        let stake_pool_info = next_account_info(account_info_iter)?;
897        let staker_info = next_account_info(account_info_iter)?;
898        let reserve_stake_info = next_account_info(account_info_iter)?;
899        let withdraw_authority_info = next_account_info(account_info_iter)?;
900        let validator_list_info = next_account_info(account_info_iter)?;
901        let stake_info = next_account_info(account_info_iter)?;
902        let validator_vote_info = next_account_info(account_info_iter)?;
903        let rent_info = next_account_info(account_info_iter)?;
904        let rent = &Rent::from_account_info(rent_info)?;
905        let clock_info = next_account_info(account_info_iter)?;
906        let clock = &Clock::from_account_info(clock_info)?;
907        let stake_history_info = next_account_info(account_info_iter)?;
908        let stake_config_info = next_account_info(account_info_iter)?;
909        let system_program_info = next_account_info(account_info_iter)?;
910        let stake_program_info = next_account_info(account_info_iter)?;
911
912        check_system_program(system_program_info.key)?;
913        check_stake_program(stake_program_info.key)?;
914
915        check_account_owner(stake_pool_info, program_id)?;
916        let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
917        if !stake_pool.is_valid() {
918            return Err(StakePoolError::InvalidState.into());
919        }
920
921        stake_pool.check_authority_withdraw(
922            withdraw_authority_info.key,
923            program_id,
924            stake_pool_info.key,
925        )?;
926
927        stake_pool.check_staker(staker_info)?;
928        stake_pool.check_reserve_stake(reserve_stake_info)?;
929        stake_pool.check_validator_list(validator_list_info)?;
930
931        if stake_pool.last_update_epoch < clock.epoch {
932            return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
933        }
934
935        check_account_owner(validator_list_info, program_id)?;
936        let mut validator_list_data = validator_list_info.data.borrow_mut();
937        let (header, mut validator_list) =
938            ValidatorListHeader::deserialize_vec(&mut validator_list_data)?;
939        if !header.is_valid() {
940            return Err(StakePoolError::InvalidState.into());
941        }
942        if header.max_validators == validator_list.len() {
943            return Err(ProgramError::AccountDataTooSmall);
944        }
945        if validator_list.len() >= MAX_VALIDATORS_IN_POOL {
946            return Err(StakePoolError::TooManyValidatorsInPool.into());
947        }
948        let maybe_validator_stake_info = validator_list.find::<ValidatorStakeInfo, _>(|x| {
949            ValidatorStakeInfo::memcmp_pubkey(x, validator_vote_info.key)
950        });
951        if maybe_validator_stake_info.is_some() {
952            return Err(StakePoolError::ValidatorAlreadyAdded.into());
953        }
954
955        let validator_seed = NonZeroU32::new(raw_validator_seed);
956        let (stake_address, bump_seed) = crate::find_stake_program_address(
957            program_id,
958            validator_vote_info.key,
959            stake_pool_info.key,
960            validator_seed,
961        );
962        if stake_address != *stake_info.key {
963            return Err(StakePoolError::InvalidStakeAccountAddress.into());
964        }
965
966        let validator_seed_bytes = validator_seed.map(|s| s.get().to_le_bytes());
967        let stake_account_signer_seeds: &[&[_]] = &[
968            validator_vote_info.key.as_ref(),
969            stake_pool_info.key.as_ref(),
970            validator_seed_bytes
971                .as_ref()
972                .map(|s| s.as_slice())
973                .unwrap_or(&[]),
974            &[bump_seed],
975        ];
976
977        // Fund the stake account with the minimum + rent-exempt balance
978        let stake_space = std::mem::size_of::<stake::state::StakeStateV2>();
979        let stake_minimum_delegation = stake::tools::get_minimum_delegation()?;
980        let required_lamports = minimum_delegation(stake_minimum_delegation)
981            .saturating_add(rent.minimum_balance(stake_space));
982
983        // Check that we're not draining the reserve totally
984        let reserve_rent = rent.minimum_balance(reserve_stake_info.data_len());
985        let minimum_lamports = minimum_reserve_lamports(reserve_rent);
986        let reserve_lamports = reserve_stake_info.lamports();
987        if reserve_lamports.saturating_sub(required_lamports) < minimum_lamports {
988            msg!(
989                "Need to add {} lamports for the reserve stake to be rent-exempt after adding a validator, reserve currently has {} lamports",
990                required_lamports.saturating_add(minimum_lamports).saturating_sub(reserve_lamports),
991                reserve_lamports
992            );
993            return Err(ProgramError::InsufficientFunds);
994        }
995
996        // Create new stake account
997        create_stake_account(stake_info.clone(), stake_account_signer_seeds, stake_space)?;
998        // split into validator stake account
999        Self::stake_split(
1000            stake_pool_info.key,
1001            reserve_stake_info.clone(),
1002            withdraw_authority_info.clone(),
1003            AUTHORITY_WITHDRAW,
1004            stake_pool.stake_withdraw_bump_seed,
1005            required_lamports,
1006            stake_info.clone(),
1007        )?;
1008
1009        Self::stake_delegate(
1010            stake_info.clone(),
1011            validator_vote_info.clone(),
1012            clock_info.clone(),
1013            stake_history_info.clone(),
1014            stake_config_info.clone(),
1015            withdraw_authority_info.clone(),
1016            stake_pool_info.key,
1017            AUTHORITY_WITHDRAW,
1018            stake_pool.stake_withdraw_bump_seed,
1019        )?;
1020
1021        validator_list.push(ValidatorStakeInfo {
1022            status: StakeStatus::Active.into(),
1023            vote_account_address: *validator_vote_info.key,
1024            active_stake_lamports: required_lamports.into(),
1025            transient_stake_lamports: 0.into(),
1026            last_update_epoch: clock.epoch.into(),
1027            transient_seed_suffix: 0.into(),
1028            unused: 0.into(),
1029            validator_seed_suffix: raw_validator_seed.into(),
1030        })?;
1031
1032        Ok(())
1033    }
1034
1035    /// Processes `RemoveValidatorFromPool` instruction.
1036    #[inline(never)] // needed due to stack size violation
1037    fn process_remove_validator_from_pool(
1038        program_id: &Pubkey,
1039        accounts: &[AccountInfo],
1040    ) -> ProgramResult {
1041        let account_info_iter = &mut accounts.iter();
1042        let stake_pool_info = next_account_info(account_info_iter)?;
1043        let staker_info = next_account_info(account_info_iter)?;
1044        let withdraw_authority_info = next_account_info(account_info_iter)?;
1045        let validator_list_info = next_account_info(account_info_iter)?;
1046        let stake_account_info = next_account_info(account_info_iter)?;
1047        let transient_stake_account_info = next_account_info(account_info_iter)?;
1048        let clock_info = next_account_info(account_info_iter)?;
1049        let clock = &Clock::from_account_info(clock_info)?;
1050        let stake_program_info = next_account_info(account_info_iter)?;
1051
1052        check_stake_program(stake_program_info.key)?;
1053        check_account_owner(stake_pool_info, program_id)?;
1054
1055        let mut stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
1056        if !stake_pool.is_valid() {
1057            return Err(StakePoolError::InvalidState.into());
1058        }
1059
1060        stake_pool.check_authority_withdraw(
1061            withdraw_authority_info.key,
1062            program_id,
1063            stake_pool_info.key,
1064        )?;
1065        stake_pool.check_staker(staker_info)?;
1066
1067        if stake_pool.last_update_epoch < clock.epoch {
1068            msg!(
1069                "clock {} pool {}",
1070                clock.epoch,
1071                stake_pool.last_update_epoch
1072            );
1073            return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
1074        }
1075
1076        stake_pool.check_validator_list(validator_list_info)?;
1077
1078        check_account_owner(validator_list_info, program_id)?;
1079        let mut validator_list_data = validator_list_info.data.borrow_mut();
1080        let (header, mut validator_list) =
1081            ValidatorListHeader::deserialize_vec(&mut validator_list_data)?;
1082        if !header.is_valid() {
1083            return Err(StakePoolError::InvalidState.into());
1084        }
1085
1086        let (_, stake) = get_stake_state(stake_account_info)?;
1087        let vote_account_address = stake.delegation.voter_pubkey;
1088        let maybe_validator_stake_info = validator_list.find_mut::<ValidatorStakeInfo, _>(|x| {
1089            ValidatorStakeInfo::memcmp_pubkey(x, &vote_account_address)
1090        });
1091        if maybe_validator_stake_info.is_none() {
1092            msg!(
1093                "Vote account {} not found in stake pool",
1094                vote_account_address
1095            );
1096            return Err(StakePoolError::ValidatorNotFound.into());
1097        }
1098        let validator_stake_info = maybe_validator_stake_info.unwrap();
1099        check_validator_stake_address(
1100            program_id,
1101            stake_pool_info.key,
1102            stake_account_info.key,
1103            &vote_account_address,
1104            NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()),
1105        )?;
1106
1107        if validator_stake_info.status != StakeStatus::Active.into() {
1108            msg!("Validator is already marked for removal");
1109            return Err(StakePoolError::ValidatorNotFound.into());
1110        }
1111
1112        let new_status = if u64::from(validator_stake_info.transient_stake_lamports) > 0 {
1113            check_transient_stake_address(
1114                program_id,
1115                stake_pool_info.key,
1116                transient_stake_account_info.key,
1117                &vote_account_address,
1118                validator_stake_info.transient_seed_suffix.into(),
1119            )?;
1120
1121            match get_stake_state(transient_stake_account_info) {
1122                Ok((meta, stake))
1123                    if stake_is_usable_by_pool(
1124                        &meta,
1125                        withdraw_authority_info.key,
1126                        &stake_pool.lockup,
1127                    ) && stake.delegation.deactivation_epoch == Epoch::MAX =>
1128                {
1129                    Self::stake_deactivate(
1130                        transient_stake_account_info.clone(),
1131                        clock_info.clone(),
1132                        withdraw_authority_info.clone(),
1133                        stake_pool_info.key,
1134                        AUTHORITY_WITHDRAW,
1135                        stake_pool.stake_withdraw_bump_seed,
1136                    )?;
1137                }
1138                _ => (),
1139            }
1140            StakeStatus::DeactivatingAll
1141        } else {
1142            StakeStatus::DeactivatingValidator
1143        };
1144
1145        // If the stake was force-deactivated through deactivate-delinquent or
1146        // some other means, we *do not* need to deactivate it again
1147        if stake.delegation.deactivation_epoch == Epoch::MAX {
1148            Self::stake_deactivate(
1149                stake_account_info.clone(),
1150                clock_info.clone(),
1151                withdraw_authority_info.clone(),
1152                stake_pool_info.key,
1153                AUTHORITY_WITHDRAW,
1154                stake_pool.stake_withdraw_bump_seed,
1155            )?;
1156        }
1157
1158        validator_stake_info.status = new_status.into();
1159
1160        if stake_pool.preferred_deposit_validator_vote_address == Some(vote_account_address) {
1161            stake_pool.preferred_deposit_validator_vote_address = None;
1162        }
1163        if stake_pool.preferred_withdraw_validator_vote_address == Some(vote_account_address) {
1164            stake_pool.preferred_withdraw_validator_vote_address = None;
1165        }
1166        borsh::to_writer(&mut stake_pool_info.data.borrow_mut()[..], &stake_pool)?;
1167
1168        Ok(())
1169    }
1170
1171    /// Processes `DecreaseValidatorStake` instruction.
1172    #[inline(never)] // needed due to stack size violation
1173    fn process_decrease_validator_stake(
1174        program_id: &Pubkey,
1175        accounts: &[AccountInfo],
1176        lamports: u64,
1177        transient_stake_seed: u64,
1178        maybe_ephemeral_stake_seed: Option<u64>,
1179        fund_rent_exempt_reserve: bool,
1180    ) -> ProgramResult {
1181        let account_info_iter = &mut accounts.iter();
1182        let stake_pool_info = next_account_info(account_info_iter)?;
1183        let staker_info = next_account_info(account_info_iter)?;
1184        let withdraw_authority_info = next_account_info(account_info_iter)?;
1185        let validator_list_info = next_account_info(account_info_iter)?;
1186        let maybe_reserve_stake_info = fund_rent_exempt_reserve
1187            .then(|| next_account_info(account_info_iter))
1188            .transpose()?;
1189        let validator_stake_account_info = next_account_info(account_info_iter)?;
1190        let maybe_ephemeral_stake_account_info = maybe_ephemeral_stake_seed
1191            .map(|_| next_account_info(account_info_iter))
1192            .transpose()?;
1193        let transient_stake_account_info = next_account_info(account_info_iter)?;
1194        let clock_info = next_account_info(account_info_iter)?;
1195        let clock = &Clock::from_account_info(clock_info)?;
1196        let (rent, maybe_stake_history_info) =
1197            if maybe_ephemeral_stake_seed.is_some() || fund_rent_exempt_reserve {
1198                (Rent::get()?, Some(next_account_info(account_info_iter)?))
1199            } else {
1200                // legacy instruction takes the rent account
1201                let rent_info = next_account_info(account_info_iter)?;
1202                (Rent::from_account_info(rent_info)?, None)
1203            };
1204        let system_program_info = next_account_info(account_info_iter)?;
1205        let stake_program_info = next_account_info(account_info_iter)?;
1206
1207        check_system_program(system_program_info.key)?;
1208        check_stake_program(stake_program_info.key)?;
1209        check_account_owner(stake_pool_info, program_id)?;
1210
1211        let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
1212        if !stake_pool.is_valid() {
1213            msg!("Expected valid stake pool");
1214            return Err(StakePoolError::InvalidState.into());
1215        }
1216
1217        stake_pool.check_authority_withdraw(
1218            withdraw_authority_info.key,
1219            program_id,
1220            stake_pool_info.key,
1221        )?;
1222        stake_pool.check_staker(staker_info)?;
1223
1224        if stake_pool.last_update_epoch < clock.epoch {
1225            return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
1226        }
1227
1228        stake_pool.check_validator_list(validator_list_info)?;
1229        check_account_owner(validator_list_info, program_id)?;
1230        let validator_list_data = &mut *validator_list_info.data.borrow_mut();
1231        let (validator_list_header, mut validator_list) =
1232            ValidatorListHeader::deserialize_vec(validator_list_data)?;
1233        if !validator_list_header.is_valid() {
1234            return Err(StakePoolError::InvalidState.into());
1235        }
1236
1237        if let Some(reserve_stake_info) = maybe_reserve_stake_info {
1238            stake_pool.check_reserve_stake(reserve_stake_info)?;
1239        }
1240
1241        let validator_stake_rent = rent.minimum_balance(validator_stake_account_info.data_len());
1242        let (_, stake) = get_stake_state(validator_stake_account_info)?;
1243        let vote_account_address = stake.delegation.voter_pubkey;
1244
1245        let maybe_validator_stake_info = validator_list.find_mut::<ValidatorStakeInfo, _>(|x| {
1246            ValidatorStakeInfo::memcmp_pubkey(x, &vote_account_address)
1247        });
1248        if maybe_validator_stake_info.is_none() {
1249            msg!(
1250                "Vote account {} not found in stake pool",
1251                vote_account_address
1252            );
1253            return Err(StakePoolError::ValidatorNotFound.into());
1254        }
1255        let validator_stake_info = maybe_validator_stake_info.unwrap();
1256        check_validator_stake_address(
1257            program_id,
1258            stake_pool_info.key,
1259            validator_stake_account_info.key,
1260            &vote_account_address,
1261            NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()),
1262        )?;
1263        if u64::from(validator_stake_info.transient_stake_lamports) > 0 {
1264            if maybe_ephemeral_stake_seed.is_none() {
1265                msg!("Attempting to decrease stake on a validator with pending transient stake, use DecreaseAdditionalValidatorStake with the existing seed");
1266                return Err(StakePoolError::TransientAccountInUse.into());
1267            }
1268            if transient_stake_seed != u64::from(validator_stake_info.transient_seed_suffix) {
1269                msg!(
1270                    "Transient stake already exists with seed {}, you must use that one",
1271                    u64::from(validator_stake_info.transient_seed_suffix)
1272                );
1273                return Err(ProgramError::InvalidSeeds);
1274            }
1275            check_if_stake_deactivating(
1276                transient_stake_account_info,
1277                &vote_account_address,
1278                clock.epoch,
1279            )?;
1280        }
1281
1282        if validator_stake_info.status != StakeStatus::Active.into() {
1283            msg!("Validator is marked for removal and no longer allows decreases");
1284            return Err(StakePoolError::ValidatorNotFound.into());
1285        }
1286
1287        let stake_space = std::mem::size_of::<stake::state::StakeStateV2>();
1288        let stake_rent = rent.minimum_balance(stake_space);
1289
1290        let stake_minimum_delegation = stake::tools::get_minimum_delegation()?;
1291        let current_minimum_lamports = minimum_delegation(stake_minimum_delegation);
1292        if lamports < current_minimum_lamports {
1293            msg!(
1294                "Need at least {} lamports for transient stake to meet minimum delegation and rent-exempt requirements, {} provided",
1295                current_minimum_lamports,
1296                lamports
1297            );
1298            return Err(ProgramError::AccountNotRentExempt);
1299        }
1300
1301        let remaining_lamports = validator_stake_account_info
1302            .lamports()
1303            .checked_sub(lamports)
1304            .ok_or(ProgramError::InsufficientFunds)?;
1305        let required_lamports =
1306            minimum_stake_lamports(validator_stake_rent, stake_minimum_delegation);
1307        if remaining_lamports < required_lamports {
1308            msg!("Need at least {} lamports in the stake account after decrease, {} requested, {} is the current possible maximum",
1309                required_lamports,
1310                lamports,
1311                validator_stake_account_info.lamports().checked_sub(required_lamports).ok_or(StakePoolError::CalculationFailure)?
1312            );
1313            return Err(ProgramError::InsufficientFunds);
1314        }
1315
1316        let (source_stake_account_info, split_lamports) =
1317            if let Some((ephemeral_stake_seed, ephemeral_stake_account_info)) =
1318                maybe_ephemeral_stake_seed.zip(maybe_ephemeral_stake_account_info)
1319            {
1320                let ephemeral_stake_bump_seed = check_ephemeral_stake_address(
1321                    program_id,
1322                    stake_pool_info.key,
1323                    ephemeral_stake_account_info.key,
1324                    ephemeral_stake_seed,
1325                )?;
1326                let ephemeral_stake_account_signer_seeds: &[&[_]] = &[
1327                    EPHEMERAL_STAKE_SEED_PREFIX,
1328                    stake_pool_info.key.as_ref(),
1329                    &ephemeral_stake_seed.to_le_bytes(),
1330                    &[ephemeral_stake_bump_seed],
1331                ];
1332                create_stake_account(
1333                    ephemeral_stake_account_info.clone(),
1334                    ephemeral_stake_account_signer_seeds,
1335                    stake_space,
1336                )?;
1337
1338                // if needed, withdraw rent-exempt reserve for ephemeral account
1339                if let Some(reserve_stake_info) = maybe_reserve_stake_info {
1340                    let required_lamports_for_rent_exemption =
1341                        stake_rent.saturating_sub(ephemeral_stake_account_info.lamports());
1342                    if required_lamports_for_rent_exemption > 0 {
1343                        if required_lamports_for_rent_exemption >= reserve_stake_info.lamports() {
1344                            return Err(StakePoolError::ReserveDepleted.into());
1345                        }
1346                        let stake_history_info = maybe_stake_history_info
1347                            .ok_or(StakePoolError::MissingRequiredSysvar)?;
1348                        Self::stake_withdraw(
1349                            stake_pool_info.key,
1350                            reserve_stake_info.clone(),
1351                            withdraw_authority_info.clone(),
1352                            AUTHORITY_WITHDRAW,
1353                            stake_pool.stake_withdraw_bump_seed,
1354                            ephemeral_stake_account_info.clone(),
1355                            clock_info.clone(),
1356                            stake_history_info.clone(),
1357                            required_lamports_for_rent_exemption,
1358                        )?;
1359                    }
1360                }
1361
1362                // split into ephemeral stake account
1363                Self::stake_split(
1364                    stake_pool_info.key,
1365                    validator_stake_account_info.clone(),
1366                    withdraw_authority_info.clone(),
1367                    AUTHORITY_WITHDRAW,
1368                    stake_pool.stake_withdraw_bump_seed,
1369                    lamports,
1370                    ephemeral_stake_account_info.clone(),
1371                )?;
1372
1373                Self::stake_deactivate(
1374                    ephemeral_stake_account_info.clone(),
1375                    clock_info.clone(),
1376                    withdraw_authority_info.clone(),
1377                    stake_pool_info.key,
1378                    AUTHORITY_WITHDRAW,
1379                    stake_pool.stake_withdraw_bump_seed,
1380                )?;
1381
1382                (
1383                    ephemeral_stake_account_info,
1384                    ephemeral_stake_account_info.lamports(),
1385                )
1386            } else {
1387                // if no ephemeral account is provided, split everything from the
1388                // validator stake account, into the transient stake account
1389                (validator_stake_account_info, lamports)
1390            };
1391
1392        let transient_stake_bump_seed = check_transient_stake_address(
1393            program_id,
1394            stake_pool_info.key,
1395            transient_stake_account_info.key,
1396            &vote_account_address,
1397            transient_stake_seed,
1398        )?;
1399
1400        if u64::from(validator_stake_info.transient_stake_lamports) > 0 {
1401            let stake_history_info = maybe_stake_history_info.unwrap();
1402            // transient stake exists, try to merge from the source account,
1403            // which is always an ephemeral account
1404            Self::stake_merge(
1405                stake_pool_info.key,
1406                source_stake_account_info.clone(),
1407                withdraw_authority_info.clone(),
1408                AUTHORITY_WITHDRAW,
1409                stake_pool.stake_withdraw_bump_seed,
1410                transient_stake_account_info.clone(),
1411                clock_info.clone(),
1412                stake_history_info.clone(),
1413            )?;
1414        } else {
1415            let transient_stake_account_signer_seeds: &[&[_]] = &[
1416                TRANSIENT_STAKE_SEED_PREFIX,
1417                vote_account_address.as_ref(),
1418                stake_pool_info.key.as_ref(),
1419                &transient_stake_seed.to_le_bytes(),
1420                &[transient_stake_bump_seed],
1421            ];
1422
1423            create_stake_account(
1424                transient_stake_account_info.clone(),
1425                transient_stake_account_signer_seeds,
1426                stake_space,
1427            )?;
1428
1429            // if needed, withdraw rent-exempt reserve for transient account
1430            if let Some(reserve_stake_info) = maybe_reserve_stake_info {
1431                let required_lamports =
1432                    stake_rent.saturating_sub(transient_stake_account_info.lamports());
1433                // in the case of doing a full split from an ephemeral account,
1434                // the rent-exempt reserve moves over, so no need to fund it from
1435                // the pool reserve
1436                if source_stake_account_info.lamports() != split_lamports {
1437                    let stake_history_info =
1438                        maybe_stake_history_info.ok_or(StakePoolError::MissingRequiredSysvar)?;
1439                    if required_lamports >= reserve_stake_info.lamports() {
1440                        return Err(StakePoolError::ReserveDepleted.into());
1441                    }
1442                    if required_lamports > 0 {
1443                        Self::stake_withdraw(
1444                            stake_pool_info.key,
1445                            reserve_stake_info.clone(),
1446                            withdraw_authority_info.clone(),
1447                            AUTHORITY_WITHDRAW,
1448                            stake_pool.stake_withdraw_bump_seed,
1449                            transient_stake_account_info.clone(),
1450                            clock_info.clone(),
1451                            stake_history_info.clone(),
1452                            required_lamports,
1453                        )?;
1454                    }
1455                }
1456            }
1457
1458            // split into transient stake account
1459            Self::stake_split(
1460                stake_pool_info.key,
1461                source_stake_account_info.clone(),
1462                withdraw_authority_info.clone(),
1463                AUTHORITY_WITHDRAW,
1464                stake_pool.stake_withdraw_bump_seed,
1465                split_lamports,
1466                transient_stake_account_info.clone(),
1467            )?;
1468
1469            // Deactivate transient stake if necessary
1470            let (_, stake) = get_stake_state(transient_stake_account_info)?;
1471            if stake.delegation.deactivation_epoch == Epoch::MAX {
1472                Self::stake_deactivate(
1473                    transient_stake_account_info.clone(),
1474                    clock_info.clone(),
1475                    withdraw_authority_info.clone(),
1476                    stake_pool_info.key,
1477                    AUTHORITY_WITHDRAW,
1478                    stake_pool.stake_withdraw_bump_seed,
1479                )?;
1480            }
1481        }
1482
1483        validator_stake_info.active_stake_lamports =
1484            u64::from(validator_stake_info.active_stake_lamports)
1485                .checked_sub(lamports)
1486                .ok_or(StakePoolError::CalculationFailure)?
1487                .into();
1488        validator_stake_info.transient_stake_lamports =
1489            transient_stake_account_info.lamports().into();
1490        validator_stake_info.transient_seed_suffix = transient_stake_seed.into();
1491
1492        Ok(())
1493    }
1494
1495    /// Processes `IncreaseValidatorStake` instruction.
1496    #[inline(never)] // needed due to stack size violation
1497    fn process_increase_validator_stake(
1498        program_id: &Pubkey,
1499        accounts: &[AccountInfo],
1500        lamports: u64,
1501        transient_stake_seed: u64,
1502        maybe_ephemeral_stake_seed: Option<u64>,
1503    ) -> ProgramResult {
1504        let account_info_iter = &mut accounts.iter();
1505        let stake_pool_info = next_account_info(account_info_iter)?;
1506        let staker_info = next_account_info(account_info_iter)?;
1507        let withdraw_authority_info = next_account_info(account_info_iter)?;
1508        let validator_list_info = next_account_info(account_info_iter)?;
1509        let reserve_stake_account_info = next_account_info(account_info_iter)?;
1510        let maybe_ephemeral_stake_account_info = maybe_ephemeral_stake_seed
1511            .map(|_| next_account_info(account_info_iter))
1512            .transpose()?;
1513        let transient_stake_account_info = next_account_info(account_info_iter)?;
1514        let validator_stake_account_info = next_account_info(account_info_iter)?;
1515        let validator_vote_account_info = next_account_info(account_info_iter)?;
1516        let clock_info = next_account_info(account_info_iter)?;
1517        let clock = &Clock::from_account_info(clock_info)?;
1518        let rent = if maybe_ephemeral_stake_seed.is_some() {
1519            // instruction with ephemeral account doesn't take the rent account
1520            Rent::get()?
1521        } else {
1522            // legacy instruction takes the rent account
1523            let rent_info = next_account_info(account_info_iter)?;
1524            Rent::from_account_info(rent_info)?
1525        };
1526        let stake_history_info = next_account_info(account_info_iter)?;
1527        let stake_config_info = next_account_info(account_info_iter)?;
1528        let system_program_info = next_account_info(account_info_iter)?;
1529        let stake_program_info = next_account_info(account_info_iter)?;
1530
1531        check_system_program(system_program_info.key)?;
1532        check_stake_program(stake_program_info.key)?;
1533        check_account_owner(stake_pool_info, program_id)?;
1534
1535        let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
1536        if !stake_pool.is_valid() {
1537            msg!("Expected valid stake pool");
1538            return Err(StakePoolError::InvalidState.into());
1539        }
1540
1541        stake_pool.check_authority_withdraw(
1542            withdraw_authority_info.key,
1543            program_id,
1544            stake_pool_info.key,
1545        )?;
1546        stake_pool.check_staker(staker_info)?;
1547
1548        if stake_pool.last_update_epoch < clock.epoch {
1549            return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
1550        }
1551
1552        stake_pool.check_validator_list(validator_list_info)?;
1553        stake_pool.check_reserve_stake(reserve_stake_account_info)?;
1554        check_account_owner(validator_list_info, program_id)?;
1555
1556        let mut validator_list_data = validator_list_info.data.borrow_mut();
1557        let (header, mut validator_list) =
1558            ValidatorListHeader::deserialize_vec(&mut validator_list_data)?;
1559        if !header.is_valid() {
1560            return Err(StakePoolError::InvalidState.into());
1561        }
1562
1563        let vote_account_address = validator_vote_account_info.key;
1564
1565        let maybe_validator_stake_info = validator_list.find_mut::<ValidatorStakeInfo, _>(|x| {
1566            ValidatorStakeInfo::memcmp_pubkey(x, vote_account_address)
1567        });
1568        if maybe_validator_stake_info.is_none() {
1569            msg!(
1570                "Vote account {} not found in stake pool",
1571                vote_account_address
1572            );
1573            return Err(StakePoolError::ValidatorNotFound.into());
1574        }
1575        let validator_stake_info = maybe_validator_stake_info.unwrap();
1576        if u64::from(validator_stake_info.transient_stake_lamports) > 0 {
1577            if maybe_ephemeral_stake_seed.is_none() {
1578                msg!("Attempting to increase stake on a validator with pending transient stake, use IncreaseAdditionalValidatorStake with the existing seed");
1579                return Err(StakePoolError::TransientAccountInUse.into());
1580            }
1581            if transient_stake_seed != u64::from(validator_stake_info.transient_seed_suffix) {
1582                msg!(
1583                    "Transient stake already exists with seed {}, you must use that one",
1584                    u64::from(validator_stake_info.transient_seed_suffix)
1585                );
1586                return Err(ProgramError::InvalidSeeds);
1587            }
1588            check_if_stake_activating(
1589                transient_stake_account_info,
1590                vote_account_address,
1591                clock.epoch,
1592            )?;
1593        }
1594
1595        check_validator_stake_account(
1596            validator_stake_account_info,
1597            program_id,
1598            stake_pool_info.key,
1599            withdraw_authority_info.key,
1600            vote_account_address,
1601            validator_stake_info.validator_seed_suffix.into(),
1602            &stake_pool.lockup,
1603        )?;
1604
1605        if validator_stake_info.status != StakeStatus::Active.into() {
1606            msg!("Validator is marked for removal and no longer allows increases");
1607            return Err(StakePoolError::ValidatorNotFound.into());
1608        }
1609
1610        let stake_space = std::mem::size_of::<stake::state::StakeStateV2>();
1611        let stake_rent = rent.minimum_balance(stake_space);
1612        let stake_minimum_delegation = stake::tools::get_minimum_delegation()?;
1613        let current_minimum_delegation = minimum_delegation(stake_minimum_delegation);
1614        if lamports < current_minimum_delegation {
1615            msg!(
1616                "Need more than {} lamports for transient stake to meet minimum delegation requirement, {} provided",
1617                current_minimum_delegation,
1618                lamports
1619            );
1620            return Err(ProgramError::Custom(
1621                stake::error::StakeError::InsufficientDelegation as u32,
1622            ));
1623        }
1624
1625        // the stake account rent exemption is withdrawn after the merge, so
1626        // to add `lamports` to a validator, we need to create a stake account
1627        // with `lamports + stake_rent`
1628        let total_lamports = lamports.saturating_add(stake_rent);
1629
1630        if reserve_stake_account_info
1631            .lamports()
1632            .saturating_sub(total_lamports)
1633            < stake_rent
1634        {
1635            let max_split_amount = reserve_stake_account_info
1636                .lamports()
1637                .saturating_sub(stake_rent.saturating_mul(2));
1638            msg!(
1639                "Reserve stake does not have enough lamports for increase, maximum amount {}, {} requested",
1640                max_split_amount,
1641                lamports
1642            );
1643            return Err(ProgramError::InsufficientFunds);
1644        }
1645
1646        let source_stake_account_info =
1647            if let Some((ephemeral_stake_seed, ephemeral_stake_account_info)) =
1648                maybe_ephemeral_stake_seed.zip(maybe_ephemeral_stake_account_info)
1649            {
1650                let ephemeral_stake_bump_seed = check_ephemeral_stake_address(
1651                    program_id,
1652                    stake_pool_info.key,
1653                    ephemeral_stake_account_info.key,
1654                    ephemeral_stake_seed,
1655                )?;
1656                let ephemeral_stake_account_signer_seeds: &[&[_]] = &[
1657                    EPHEMERAL_STAKE_SEED_PREFIX,
1658                    stake_pool_info.key.as_ref(),
1659                    &ephemeral_stake_seed.to_le_bytes(),
1660                    &[ephemeral_stake_bump_seed],
1661                ];
1662                create_stake_account(
1663                    ephemeral_stake_account_info.clone(),
1664                    ephemeral_stake_account_signer_seeds,
1665                    stake_space,
1666                )?;
1667
1668                // split into ephemeral stake account
1669                Self::stake_split(
1670                    stake_pool_info.key,
1671                    reserve_stake_account_info.clone(),
1672                    withdraw_authority_info.clone(),
1673                    AUTHORITY_WITHDRAW,
1674                    stake_pool.stake_withdraw_bump_seed,
1675                    total_lamports,
1676                    ephemeral_stake_account_info.clone(),
1677                )?;
1678
1679                // activate stake to validator
1680                Self::stake_delegate(
1681                    ephemeral_stake_account_info.clone(),
1682                    validator_vote_account_info.clone(),
1683                    clock_info.clone(),
1684                    stake_history_info.clone(),
1685                    stake_config_info.clone(),
1686                    withdraw_authority_info.clone(),
1687                    stake_pool_info.key,
1688                    AUTHORITY_WITHDRAW,
1689                    stake_pool.stake_withdraw_bump_seed,
1690                )?;
1691                ephemeral_stake_account_info
1692            } else {
1693                // if no ephemeral account is provided, split everything from the
1694                // reserve account, into the transient stake account
1695                reserve_stake_account_info
1696            };
1697
1698        let transient_stake_bump_seed = check_transient_stake_address(
1699            program_id,
1700            stake_pool_info.key,
1701            transient_stake_account_info.key,
1702            vote_account_address,
1703            transient_stake_seed,
1704        )?;
1705
1706        if u64::from(validator_stake_info.transient_stake_lamports) > 0 {
1707            // transient stake exists, try to merge from the source account,
1708            // which is always an ephemeral account
1709            Self::stake_merge(
1710                stake_pool_info.key,
1711                source_stake_account_info.clone(),
1712                withdraw_authority_info.clone(),
1713                AUTHORITY_WITHDRAW,
1714                stake_pool.stake_withdraw_bump_seed,
1715                transient_stake_account_info.clone(),
1716                clock_info.clone(),
1717                stake_history_info.clone(),
1718            )?;
1719        } else {
1720            // no transient stake, split
1721            let transient_stake_account_signer_seeds: &[&[_]] = &[
1722                TRANSIENT_STAKE_SEED_PREFIX,
1723                vote_account_address.as_ref(),
1724                stake_pool_info.key.as_ref(),
1725                &transient_stake_seed.to_le_bytes(),
1726                &[transient_stake_bump_seed],
1727            ];
1728
1729            create_stake_account(
1730                transient_stake_account_info.clone(),
1731                transient_stake_account_signer_seeds,
1732                stake_space,
1733            )?;
1734
1735            // split into transient stake account
1736            Self::stake_split(
1737                stake_pool_info.key,
1738                source_stake_account_info.clone(),
1739                withdraw_authority_info.clone(),
1740                AUTHORITY_WITHDRAW,
1741                stake_pool.stake_withdraw_bump_seed,
1742                total_lamports,
1743                transient_stake_account_info.clone(),
1744            )?;
1745
1746            // Activate transient stake to validator if necessary
1747            let stake_state = try_from_slice_unchecked::<stake::state::StakeStateV2>(
1748                &transient_stake_account_info.data.borrow(),
1749            )?;
1750            match stake_state {
1751                // if it was delegated on or before this epoch, we're good
1752                stake::state::StakeStateV2::Stake(_, stake, _)
1753                    if stake.delegation.activation_epoch <= clock.epoch => {}
1754                // all other situations, delegate!
1755                _ => {
1756                    Self::stake_delegate(
1757                        transient_stake_account_info.clone(),
1758                        validator_vote_account_info.clone(),
1759                        clock_info.clone(),
1760                        stake_history_info.clone(),
1761                        stake_config_info.clone(),
1762                        withdraw_authority_info.clone(),
1763                        stake_pool_info.key,
1764                        AUTHORITY_WITHDRAW,
1765                        stake_pool.stake_withdraw_bump_seed,
1766                    )?;
1767                }
1768            }
1769        }
1770
1771        validator_stake_info.transient_stake_lamports =
1772            u64::from(validator_stake_info.transient_stake_lamports)
1773                .checked_add(total_lamports)
1774                .ok_or(StakePoolError::CalculationFailure)?
1775                .into();
1776        validator_stake_info.transient_seed_suffix = transient_stake_seed.into();
1777
1778        Ok(())
1779    }
1780
1781    /// Process `SetPreferredValidator` instruction
1782    #[inline(never)] // needed due to stack size violation
1783    fn process_set_preferred_validator(
1784        program_id: &Pubkey,
1785        accounts: &[AccountInfo],
1786        validator_type: PreferredValidatorType,
1787        vote_account_address: Option<Pubkey>,
1788    ) -> ProgramResult {
1789        let account_info_iter = &mut accounts.iter();
1790        let stake_pool_info = next_account_info(account_info_iter)?;
1791        let staker_info = next_account_info(account_info_iter)?;
1792        let validator_list_info = next_account_info(account_info_iter)?;
1793
1794        check_account_owner(stake_pool_info, program_id)?;
1795        check_account_owner(validator_list_info, program_id)?;
1796
1797        let mut stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
1798        if !stake_pool.is_valid() {
1799            msg!("Expected valid stake pool");
1800            return Err(StakePoolError::InvalidState.into());
1801        }
1802
1803        stake_pool.check_staker(staker_info)?;
1804        stake_pool.check_validator_list(validator_list_info)?;
1805
1806        let mut validator_list_data = validator_list_info.data.borrow_mut();
1807        let (header, validator_list) =
1808            ValidatorListHeader::deserialize_vec(&mut validator_list_data)?;
1809        if !header.is_valid() {
1810            return Err(StakePoolError::InvalidState.into());
1811        }
1812
1813        if let Some(vote_account_address) = vote_account_address {
1814            let maybe_validator_stake_info = validator_list.find::<ValidatorStakeInfo, _>(|x| {
1815                ValidatorStakeInfo::memcmp_pubkey(x, &vote_account_address)
1816            });
1817            match maybe_validator_stake_info {
1818                Some(vsi) => {
1819                    if vsi.status != StakeStatus::Active.into() {
1820                        msg!("Validator for {:?} about to be removed, cannot set as preferred deposit account", validator_type);
1821                        return Err(StakePoolError::InvalidPreferredValidator.into());
1822                    }
1823                }
1824                None => {
1825                    msg!("Validator for {:?} not present in the stake pool, cannot set as preferred deposit account", validator_type);
1826                    return Err(StakePoolError::ValidatorNotFound.into());
1827                }
1828            }
1829        }
1830
1831        match validator_type {
1832            PreferredValidatorType::Deposit => {
1833                stake_pool.preferred_deposit_validator_vote_address = vote_account_address
1834            }
1835            PreferredValidatorType::Withdraw => {
1836                stake_pool.preferred_withdraw_validator_vote_address = vote_account_address
1837            }
1838        };
1839        borsh::to_writer(&mut stake_pool_info.data.borrow_mut()[..], &stake_pool)?;
1840        Ok(())
1841    }
1842
1843    /// Processes `UpdateValidatorListBalance` instruction.
1844    #[inline(always)] // needed to maximize number of validators
1845    fn process_update_validator_list_balance(
1846        program_id: &Pubkey,
1847        accounts: &[AccountInfo],
1848        start_index: u32,
1849        no_merge: bool,
1850    ) -> ProgramResult {
1851        let account_info_iter = &mut accounts.iter();
1852        let stake_pool_info = next_account_info(account_info_iter)?;
1853        let withdraw_authority_info = next_account_info(account_info_iter)?;
1854        let validator_list_info = next_account_info(account_info_iter)?;
1855        let reserve_stake_info = next_account_info(account_info_iter)?;
1856        let clock_info = next_account_info(account_info_iter)?;
1857        let clock = &Clock::from_account_info(clock_info)?;
1858        let stake_history_info = next_account_info(account_info_iter)?;
1859        let stake_program_info = next_account_info(account_info_iter)?;
1860        let validator_stake_accounts = account_info_iter.as_slice();
1861
1862        let rent = Rent::get()?;
1863
1864        check_account_owner(stake_pool_info, program_id)?;
1865        let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
1866        if !stake_pool.is_valid() {
1867            return Err(StakePoolError::InvalidState.into());
1868        }
1869        stake_pool.check_validator_list(validator_list_info)?;
1870        stake_pool.check_authority_withdraw(
1871            withdraw_authority_info.key,
1872            program_id,
1873            stake_pool_info.key,
1874        )?;
1875        stake_pool.check_reserve_stake(reserve_stake_info)?;
1876        check_stake_program(stake_program_info.key)?;
1877
1878        // If rewards are being distributed, abort
1879        let epoch_rewards = EpochRewards::get()?;
1880        if epoch_rewards.active {
1881            return Err(StakePoolError::EpochRewardDistributionInProgress.into());
1882        }
1883
1884        if validator_stake_accounts
1885            .len()
1886            .checked_rem(2)
1887            .ok_or(StakePoolError::CalculationFailure)?
1888            != 0
1889        {
1890            msg!("Odd number of validator stake accounts passed in, should be pairs of validator stake and transient stake accounts");
1891            return Err(StakePoolError::UnexpectedValidatorListAccountSize.into());
1892        }
1893
1894        check_account_owner(validator_list_info, program_id)?;
1895        let mut validator_list_data = validator_list_info.data.borrow_mut();
1896        let (validator_list_header, mut big_vec) =
1897            ValidatorListHeader::deserialize_vec(&mut validator_list_data)?;
1898        let validator_slice = ValidatorListHeader::deserialize_mut_slice(
1899            &mut big_vec,
1900            start_index as usize,
1901            validator_stake_accounts.len() / 2,
1902        )?;
1903
1904        if !validator_list_header.is_valid() {
1905            return Err(StakePoolError::InvalidState.into());
1906        }
1907
1908        let validator_iter = &mut validator_slice
1909            .iter_mut()
1910            .zip(validator_stake_accounts.chunks_exact(2));
1911        for (validator_stake_record, validator_stakes) in validator_iter {
1912            // chunks_exact means that we always get 2 elements, making this safe
1913            let validator_stake_info = validator_stakes
1914                .first()
1915                .ok_or(ProgramError::InvalidInstructionData)?;
1916            let transient_stake_info = validator_stakes
1917                .last()
1918                .ok_or(ProgramError::InvalidInstructionData)?;
1919            if check_validator_stake_address(
1920                program_id,
1921                stake_pool_info.key,
1922                validator_stake_info.key,
1923                &validator_stake_record.vote_account_address,
1924                NonZeroU32::new(validator_stake_record.validator_seed_suffix.into()),
1925            )
1926            .is_err()
1927            {
1928                continue;
1929            };
1930            if check_transient_stake_address(
1931                program_id,
1932                stake_pool_info.key,
1933                transient_stake_info.key,
1934                &validator_stake_record.vote_account_address,
1935                validator_stake_record.transient_seed_suffix.into(),
1936            )
1937            .is_err()
1938            {
1939                continue;
1940            };
1941
1942            let validator_stake_rent = rent.minimum_balance(validator_stake_info.data_len());
1943
1944            let mut active_stake_lamports = 0;
1945            let mut transient_stake_lamports = 0;
1946            let validator_stake_state = try_from_slice_unchecked::<stake::state::StakeStateV2>(
1947                &validator_stake_info.data.borrow(),
1948            )
1949            .ok();
1950            let transient_stake_state = try_from_slice_unchecked::<stake::state::StakeStateV2>(
1951                &transient_stake_info.data.borrow(),
1952            )
1953            .ok();
1954
1955            // Possible merge situations for transient stake
1956            //  * active -> merge into validator stake
1957            //  * activating -> nothing, just account its lamports
1958            //  * deactivating -> nothing, just account its lamports
1959            //  * inactive -> merge into reserve stake
1960            //  * not a stake -> ignore
1961            if validator_stake_record.transient_stake_lamports != 0.into() {
1962                match transient_stake_state {
1963                    Some(stake::state::StakeStateV2::Initialized(meta)) => {
1964                        if stake_is_usable_by_pool(
1965                            &meta,
1966                            withdraw_authority_info.key,
1967                            &stake_pool.lockup,
1968                        ) {
1969                            if no_merge {
1970                                transient_stake_lamports = transient_stake_info.lamports();
1971                            } else {
1972                                // merge into reserve
1973                                Self::stake_merge(
1974                                    stake_pool_info.key,
1975                                    transient_stake_info.clone(),
1976                                    withdraw_authority_info.clone(),
1977                                    AUTHORITY_WITHDRAW,
1978                                    stake_pool.stake_withdraw_bump_seed,
1979                                    reserve_stake_info.clone(),
1980                                    clock_info.clone(),
1981                                    stake_history_info.clone(),
1982                                )?;
1983                                validator_stake_record.status.remove_transient_stake()?;
1984                            }
1985                        }
1986                    }
1987                    Some(stake::state::StakeStateV2::Stake(meta, stake, _)) => {
1988                        if stake_is_usable_by_pool(
1989                            &meta,
1990                            withdraw_authority_info.key,
1991                            &stake_pool.lockup,
1992                        ) {
1993                            if !no_merge {
1994                                if stake_is_inactive_without_history(&stake, clock.epoch) {
1995                                    // deactivated, merge into reserve
1996                                    Self::stake_merge(
1997                                        stake_pool_info.key,
1998                                        transient_stake_info.clone(),
1999                                        withdraw_authority_info.clone(),
2000                                        AUTHORITY_WITHDRAW,
2001                                        stake_pool.stake_withdraw_bump_seed,
2002                                        reserve_stake_info.clone(),
2003                                        clock_info.clone(),
2004                                        stake_history_info.clone(),
2005                                    )?;
2006                                    validator_stake_record.status.remove_transient_stake()?;
2007                                } else if validator_stake_record.status.try_into()
2008                                    == Ok(StakeStatus::Active)
2009                                {
2010                                    if stake.delegation.activation_epoch < clock.epoch {
2011                                        if let Some(stake::state::StakeStateV2::Stake(
2012                                            _,
2013                                            validator_stake,
2014                                            _,
2015                                        )) = validator_stake_state
2016                                        {
2017                                            if validator_stake.delegation.activation_epoch
2018                                                < clock.epoch
2019                                            {
2020                                                Self::stake_merge(
2021                                                    stake_pool_info.key,
2022                                                    transient_stake_info.clone(),
2023                                                    withdraw_authority_info.clone(),
2024                                                    AUTHORITY_WITHDRAW,
2025                                                    stake_pool.stake_withdraw_bump_seed,
2026                                                    validator_stake_info.clone(),
2027                                                    clock_info.clone(),
2028                                                    stake_history_info.clone(),
2029                                                )?;
2030                                            } else {
2031                                                msg!("Stake activating or just active, not ready to merge");
2032                                            }
2033                                        } else {
2034                                            msg!("Transient stake is activating or active, but validator stake is not, need to add the validator stake account on {} back into the stake pool", stake.delegation.voter_pubkey);
2035                                        }
2036                                    } else {
2037                                        msg!("Transient stake not ready to be merged anywhere");
2038                                    }
2039                                } else if stake.delegation.deactivation_epoch == Epoch::MAX {
2040                                    msg!("Transient stake is activating or active, deactivating.");
2041                                    Self::stake_deactivate(
2042                                        transient_stake_info.clone(),
2043                                        clock_info.clone(),
2044                                        withdraw_authority_info.clone(),
2045                                        stake_pool_info.key,
2046                                        AUTHORITY_WITHDRAW,
2047                                        stake_pool.stake_withdraw_bump_seed,
2048                                    )?;
2049                                }
2050                            }
2051                            transient_stake_lamports = transient_stake_info.lamports();
2052                        }
2053                    }
2054                    None
2055                    | Some(stake::state::StakeStateV2::Uninitialized)
2056                    | Some(stake::state::StakeStateV2::RewardsPool) => {} // do nothing
2057                }
2058            }
2059            // Status for validator stake
2060            //  * active -> do everything
2061            //  * any other state / not a stake -> error state, but account for transient
2062            //    stake
2063            let validator_stake_state = try_from_slice_unchecked::<stake::state::StakeStateV2>(
2064                &validator_stake_info.data.borrow(),
2065            )
2066            .ok();
2067            match validator_stake_state {
2068                Some(stake::state::StakeStateV2::Stake(meta, stake, _))
2069                    if stake_is_usable_by_pool(
2070                        &meta,
2071                        withdraw_authority_info.key,
2072                        &stake_pool.lockup,
2073                    ) =>
2074                {
2075                    let additional_lamports = validator_stake_info
2076                        .lamports()
2077                        .saturating_sub(stake.delegation.stake)
2078                        .saturating_sub(validator_stake_rent);
2079                    // withdraw any extra lamports back to the reserve
2080                    if additional_lamports > 0 {
2081                        Self::stake_withdraw(
2082                            stake_pool_info.key,
2083                            validator_stake_info.clone(),
2084                            withdraw_authority_info.clone(),
2085                            AUTHORITY_WITHDRAW,
2086                            stake_pool.stake_withdraw_bump_seed,
2087                            reserve_stake_info.clone(),
2088                            clock_info.clone(),
2089                            stake_history_info.clone(),
2090                            additional_lamports,
2091                        )?;
2092                    }
2093                    match validator_stake_record.status.try_into()? {
2094                        StakeStatus::Active => {
2095                            active_stake_lamports = validator_stake_info.lamports();
2096                        }
2097                        StakeStatus::DeactivatingValidator | StakeStatus::DeactivatingAll => {
2098                            if no_merge {
2099                                active_stake_lamports = validator_stake_info.lamports();
2100                            } else if stake_is_inactive_without_history(&stake, clock.epoch) {
2101                                // Validator was removed through normal means.
2102                                // Absorb the lamports into the reserve.
2103                                Self::stake_merge(
2104                                    stake_pool_info.key,
2105                                    validator_stake_info.clone(),
2106                                    withdraw_authority_info.clone(),
2107                                    AUTHORITY_WITHDRAW,
2108                                    stake_pool.stake_withdraw_bump_seed,
2109                                    reserve_stake_info.clone(),
2110                                    clock_info.clone(),
2111                                    stake_history_info.clone(),
2112                                )?;
2113                                validator_stake_record.status.remove_validator_stake()?;
2114                            } else {
2115                                active_stake_lamports = validator_stake_info.lamports();
2116                            }
2117                        }
2118                        StakeStatus::DeactivatingTransient | StakeStatus::ReadyForRemoval => {
2119                            msg!("Validator stake account no longer part of the pool, ignoring");
2120                        }
2121                    }
2122                }
2123                Some(stake::state::StakeStateV2::Initialized(meta))
2124                    if stake_is_usable_by_pool(
2125                        &meta,
2126                        withdraw_authority_info.key,
2127                        &stake_pool.lockup,
2128                    ) =>
2129                {
2130                    // If a validator stake is `Initialized`, the validator could
2131                    // have been destaked during a cluster restart or removed through
2132                    // normal means. Either way, absorb those lamports into the reserve.
2133                    // The transient stake was likely absorbed into the reserve earlier.
2134                    Self::stake_merge(
2135                        stake_pool_info.key,
2136                        validator_stake_info.clone(),
2137                        withdraw_authority_info.clone(),
2138                        AUTHORITY_WITHDRAW,
2139                        stake_pool.stake_withdraw_bump_seed,
2140                        reserve_stake_info.clone(),
2141                        clock_info.clone(),
2142                        stake_history_info.clone(),
2143                    )?;
2144                    if transient_stake_lamports != 0 {
2145                        validator_stake_record.status = StakeStatus::DeactivatingTransient.into();
2146                    } else {
2147                        validator_stake_record.status = StakeStatus::ReadyForRemoval.into();
2148                    }
2149                }
2150                Some(stake::state::StakeStateV2::Stake(_, _, _))
2151                | Some(stake::state::StakeStateV2::Initialized(_))
2152                | Some(stake::state::StakeStateV2::Uninitialized)
2153                | Some(stake::state::StakeStateV2::RewardsPool)
2154                | None => {
2155                    msg!("Validator stake account no longer part of the pool, ignoring");
2156                }
2157            }
2158
2159            validator_stake_record.last_update_epoch = clock.epoch.into();
2160            validator_stake_record.active_stake_lamports = active_stake_lamports.into();
2161            validator_stake_record.transient_stake_lamports = transient_stake_lamports.into();
2162        }
2163
2164        Ok(())
2165    }
2166
2167    /// Processes `UpdateStakePoolBalance` instruction.
2168    #[inline(always)] // needed to optimize number of validators
2169    fn process_update_stake_pool_balance(
2170        program_id: &Pubkey,
2171        accounts: &[AccountInfo],
2172    ) -> ProgramResult {
2173        let account_info_iter = &mut accounts.iter();
2174        let stake_pool_info = next_account_info(account_info_iter)?;
2175        let withdraw_info = next_account_info(account_info_iter)?;
2176        let validator_list_info = next_account_info(account_info_iter)?;
2177        let reserve_stake_info = next_account_info(account_info_iter)?;
2178        let manager_fee_info = next_account_info(account_info_iter)?;
2179        let pool_mint_info = next_account_info(account_info_iter)?;
2180        let token_program_info = next_account_info(account_info_iter)?;
2181        let clock = Clock::get()?;
2182        let rent = Rent::get()?;
2183
2184        check_account_owner(stake_pool_info, program_id)?;
2185        let mut stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
2186        if !stake_pool.is_valid() {
2187            return Err(StakePoolError::InvalidState.into());
2188        }
2189        stake_pool.check_mint(pool_mint_info)?;
2190        stake_pool.check_authority_withdraw(withdraw_info.key, program_id, stake_pool_info.key)?;
2191        stake_pool.check_reserve_stake(reserve_stake_info)?;
2192        if stake_pool.manager_fee_account != *manager_fee_info.key {
2193            return Err(StakePoolError::InvalidFeeAccount.into());
2194        }
2195
2196        if *validator_list_info.key != stake_pool.validator_list {
2197            return Err(StakePoolError::InvalidValidatorStakeList.into());
2198        }
2199        if stake_pool.token_program_id != *token_program_info.key {
2200            return Err(ProgramError::IncorrectProgramId);
2201        }
2202
2203        check_account_owner(validator_list_info, program_id)?;
2204        let mut validator_list_data = validator_list_info.data.borrow_mut();
2205        let (header, validator_list) =
2206            ValidatorListHeader::deserialize_vec(&mut validator_list_data)?;
2207        if !header.is_valid() {
2208            return Err(StakePoolError::InvalidState.into());
2209        }
2210
2211        let previous_lamports = stake_pool.total_lamports;
2212        let previous_pool_token_supply = stake_pool.pool_token_supply;
2213        let reserve_rent = rent.minimum_balance(reserve_stake_info.data_len());
2214
2215        // Use `saturating_sub` here in case rent goes up and the reserve doesn't
2216        // have enough lamports to cover rent.
2217        let mut total_lamports = reserve_stake_info
2218            .lamports()
2219            .saturating_sub(minimum_reserve_lamports(reserve_rent));
2220
2221        for validator_stake_record in validator_list
2222            .deserialize_slice::<ValidatorStakeInfo>(0, validator_list.len() as usize)?
2223        {
2224            if u64::from(validator_stake_record.last_update_epoch) < clock.epoch {
2225                return Err(StakePoolError::StakeListOutOfDate.into());
2226            }
2227            total_lamports = total_lamports
2228                .checked_add(validator_stake_record.stake_lamports()?)
2229                .ok_or(StakePoolError::CalculationFailure)?;
2230        }
2231
2232        let reward_lamports = total_lamports.saturating_sub(previous_lamports);
2233
2234        // If the manager fee info is invalid, they don't deserve to receive the fee.
2235        let fee = if stake_pool.check_manager_fee_info(manager_fee_info).is_ok() {
2236            stake_pool
2237                .calc_epoch_fee_amount(reward_lamports)
2238                .ok_or(StakePoolError::CalculationFailure)?
2239        } else {
2240            0
2241        };
2242
2243        if fee > 0 {
2244            Self::token_mint_to(
2245                stake_pool_info.key,
2246                token_program_info.clone(),
2247                pool_mint_info.clone(),
2248                manager_fee_info.clone(),
2249                withdraw_info.clone(),
2250                AUTHORITY_WITHDRAW,
2251                stake_pool.stake_withdraw_bump_seed,
2252                fee,
2253            )?;
2254        }
2255
2256        if stake_pool.last_update_epoch < clock.epoch {
2257            if let Some(fee) = stake_pool.next_epoch_fee.get() {
2258                stake_pool.epoch_fee = *fee;
2259            }
2260            stake_pool.next_epoch_fee.update_epoch();
2261
2262            if let Some(fee) = stake_pool.next_stake_withdrawal_fee.get() {
2263                stake_pool.stake_withdrawal_fee = *fee;
2264            }
2265            stake_pool.next_stake_withdrawal_fee.update_epoch();
2266
2267            if let Some(fee) = stake_pool.next_sol_withdrawal_fee.get() {
2268                stake_pool.sol_withdrawal_fee = *fee;
2269            }
2270            stake_pool.next_sol_withdrawal_fee.update_epoch();
2271
2272            stake_pool.last_update_epoch = clock.epoch;
2273            stake_pool.last_epoch_total_lamports = previous_lamports;
2274            stake_pool.last_epoch_pool_token_supply = previous_pool_token_supply;
2275        }
2276        stake_pool.total_lamports = total_lamports;
2277
2278        let pool_mint_data = pool_mint_info.try_borrow_data()?;
2279        let pool_mint = StateWithExtensions::<Mint>::unpack(&pool_mint_data)?;
2280        stake_pool.pool_token_supply = pool_mint.base.supply;
2281
2282        borsh::to_writer(&mut stake_pool_info.data.borrow_mut()[..], &stake_pool)?;
2283
2284        Ok(())
2285    }
2286
2287    /// Processes the `CleanupRemovedValidatorEntries` instruction
2288    #[inline(never)] // needed to avoid stack size violation
2289    fn process_cleanup_removed_validator_entries(
2290        program_id: &Pubkey,
2291        accounts: &[AccountInfo],
2292    ) -> ProgramResult {
2293        let account_info_iter = &mut accounts.iter();
2294        let stake_pool_info = next_account_info(account_info_iter)?;
2295        let validator_list_info = next_account_info(account_info_iter)?;
2296
2297        check_account_owner(stake_pool_info, program_id)?;
2298        let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
2299        if !stake_pool.is_valid() {
2300            return Err(StakePoolError::InvalidState.into());
2301        }
2302        stake_pool.check_validator_list(validator_list_info)?;
2303
2304        check_account_owner(validator_list_info, program_id)?;
2305        let mut validator_list_data = validator_list_info.data.borrow_mut();
2306        let (header, mut validator_list) =
2307            ValidatorListHeader::deserialize_vec(&mut validator_list_data)?;
2308        if !header.is_valid() {
2309            return Err(StakePoolError::InvalidState.into());
2310        }
2311
2312        validator_list.retain::<ValidatorStakeInfo, _>(|x| !ValidatorStakeInfo::is_removed(x))?;
2313
2314        if stake_pool_info.is_writable {
2315            msg!("Checking preferred validators");
2316            let mut stake_pool =
2317                try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
2318
2319            // Check and reset preferred validators if they don't exist or aren't active
2320            // Check preferred deposit validator
2321            if let Some(preferred_deposit) = stake_pool.preferred_deposit_validator_vote_address {
2322                let maybe_validator = validator_list.find::<ValidatorStakeInfo, _>(|x| {
2323                    ValidatorStakeInfo::memcmp_pubkey(x, &preferred_deposit)
2324                });
2325
2326                let should_reset = match maybe_validator {
2327                    Some(validator) => {
2328                        // Check if validator status is not Active
2329                        match validator.status.try_into() {
2330                            Ok(StakeStatus::Active) => false, // Valid, keep it
2331                            _ => true,                        // Not active, reset it
2332                        }
2333                    }
2334                    None => true, // Not found in list, reset it
2335                };
2336
2337                if should_reset {
2338                    msg!(
2339                        "Preferred deposit validator {} not found or not active, resetting",
2340                        preferred_deposit
2341                    );
2342                    stake_pool.preferred_deposit_validator_vote_address = None;
2343                }
2344            }
2345
2346            // Check preferred withdrawal validator
2347            if let Some(preferred_withdraw) = stake_pool.preferred_withdraw_validator_vote_address {
2348                let maybe_validator = validator_list.find::<ValidatorStakeInfo, _>(|x| {
2349                    ValidatorStakeInfo::memcmp_pubkey(x, &preferred_withdraw)
2350                });
2351
2352                let should_reset = match maybe_validator {
2353                    Some(validator) => {
2354                        // Check if validator status is not Active
2355                        match validator.status.try_into() {
2356                            Ok(StakeStatus::Active) => false, // Valid, keep it
2357                            _ => true,                        // Not active, reset it
2358                        }
2359                    }
2360                    None => true, // Not found in list, reset it
2361                };
2362
2363                if should_reset {
2364                    msg!(
2365                        "Preferred withdrawal validator {} not found or not active, resetting",
2366                        preferred_withdraw
2367                    );
2368                    stake_pool.preferred_withdraw_validator_vote_address = None;
2369                }
2370            }
2371
2372            // Save the updated stake pool state
2373            borsh::to_writer(&mut stake_pool_info.data.borrow_mut()[..], &stake_pool)?;
2374        }
2375
2376        Ok(())
2377    }
2378
2379    /// Processes [`DepositStake`](enum.Instruction.html).
2380    #[inline(never)] // needed to avoid stack size violation
2381    fn process_deposit_stake(
2382        program_id: &Pubkey,
2383        accounts: &[AccountInfo],
2384        minimum_pool_tokens_out: Option<u64>,
2385    ) -> ProgramResult {
2386        let account_info_iter = &mut accounts.iter();
2387        let stake_pool_info = next_account_info(account_info_iter)?;
2388        let validator_list_info = next_account_info(account_info_iter)?;
2389        let stake_deposit_authority_info = next_account_info(account_info_iter)?;
2390        let withdraw_authority_info = next_account_info(account_info_iter)?;
2391        let stake_info = next_account_info(account_info_iter)?;
2392        let validator_stake_account_info = next_account_info(account_info_iter)?;
2393        let reserve_stake_account_info = next_account_info(account_info_iter)?;
2394        let dest_user_pool_info = next_account_info(account_info_iter)?;
2395        let manager_fee_info = next_account_info(account_info_iter)?;
2396        let referrer_fee_info = next_account_info(account_info_iter)?;
2397        let pool_mint_info = next_account_info(account_info_iter)?;
2398        let clock_info = next_account_info(account_info_iter)?;
2399        let clock = &Clock::from_account_info(clock_info)?;
2400        let stake_history_info = next_account_info(account_info_iter)?;
2401        let token_program_info = next_account_info(account_info_iter)?;
2402        let stake_program_info = next_account_info(account_info_iter)?;
2403
2404        check_stake_program(stake_program_info.key)?;
2405
2406        check_account_owner(stake_pool_info, program_id)?;
2407        let mut stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
2408        if !stake_pool.is_valid() {
2409            return Err(StakePoolError::InvalidState.into());
2410        }
2411
2412        stake_pool.check_authority_withdraw(
2413            withdraw_authority_info.key,
2414            program_id,
2415            stake_pool_info.key,
2416        )?;
2417        stake_pool.check_stake_deposit_authority(stake_deposit_authority_info.key)?;
2418        stake_pool.check_mint(pool_mint_info)?;
2419        stake_pool.check_validator_list(validator_list_info)?;
2420        stake_pool.check_reserve_stake(reserve_stake_account_info)?;
2421
2422        if stake_pool.token_program_id != *token_program_info.key {
2423            return Err(ProgramError::IncorrectProgramId);
2424        }
2425
2426        if stake_pool.manager_fee_account != *manager_fee_info.key {
2427            return Err(StakePoolError::InvalidFeeAccount.into());
2428        }
2429        // There is no bypass if the manager fee account is invalid. Deposits
2430        // don't hold user funds hostage, so if the fee account is invalid, users
2431        // cannot deposit in the pool.  Let it fail here!
2432
2433        if stake_pool.last_update_epoch < clock.epoch {
2434            return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
2435        }
2436
2437        check_account_owner(validator_list_info, program_id)?;
2438        let mut validator_list_data = validator_list_info.data.borrow_mut();
2439        let (header, mut validator_list) =
2440            ValidatorListHeader::deserialize_vec(&mut validator_list_data)?;
2441        if !header.is_valid() {
2442            return Err(StakePoolError::InvalidState.into());
2443        }
2444
2445        let (_, validator_stake) = get_stake_state(validator_stake_account_info)?;
2446        let pre_all_validator_lamports = validator_stake_account_info.lamports();
2447        let vote_account_address = validator_stake.delegation.voter_pubkey;
2448        if let Some(preferred_deposit) = stake_pool.preferred_deposit_validator_vote_address {
2449            if preferred_deposit != vote_account_address {
2450                msg!(
2451                    "Incorrect deposit address, expected {}, received {}",
2452                    preferred_deposit,
2453                    vote_account_address
2454                );
2455                return Err(StakePoolError::IncorrectDepositVoteAddress.into());
2456            }
2457        }
2458
2459        let validator_stake_info = validator_list
2460            .find_mut::<ValidatorStakeInfo, _>(|x| {
2461                ValidatorStakeInfo::memcmp_pubkey(x, &vote_account_address)
2462            })
2463            .ok_or(StakePoolError::ValidatorNotFound)?;
2464        check_validator_stake_address(
2465            program_id,
2466            stake_pool_info.key,
2467            validator_stake_account_info.key,
2468            &vote_account_address,
2469            NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()),
2470        )?;
2471
2472        if validator_stake_info.status != StakeStatus::Active.into() {
2473            msg!("Validator is marked for removal and no longer accepting deposits");
2474            return Err(StakePoolError::ValidatorNotFound.into());
2475        }
2476
2477        msg!("Stake pre merge {}", validator_stake.delegation.stake);
2478
2479        let (stake_deposit_authority_program_address, deposit_bump_seed) =
2480            find_deposit_authority_program_address(program_id, stake_pool_info.key);
2481        if *stake_deposit_authority_info.key == stake_deposit_authority_program_address {
2482            Self::stake_authorize_signed(
2483                stake_pool_info.key,
2484                stake_info.clone(),
2485                stake_deposit_authority_info.clone(),
2486                AUTHORITY_DEPOSIT,
2487                deposit_bump_seed,
2488                withdraw_authority_info.key,
2489                clock_info.clone(),
2490            )?;
2491        } else {
2492            Self::stake_authorize(
2493                stake_info.clone(),
2494                stake_deposit_authority_info.clone(),
2495                withdraw_authority_info.key,
2496                clock_info.clone(),
2497            )?;
2498        }
2499
2500        Self::stake_merge(
2501            stake_pool_info.key,
2502            stake_info.clone(),
2503            withdraw_authority_info.clone(),
2504            AUTHORITY_WITHDRAW,
2505            stake_pool.stake_withdraw_bump_seed,
2506            validator_stake_account_info.clone(),
2507            clock_info.clone(),
2508            stake_history_info.clone(),
2509        )?;
2510
2511        let (_, post_validator_stake) = get_stake_state(validator_stake_account_info)?;
2512        let post_all_validator_lamports = validator_stake_account_info.lamports();
2513        msg!("Stake post merge {}", post_validator_stake.delegation.stake);
2514
2515        let total_deposit_lamports = post_all_validator_lamports
2516            .checked_sub(pre_all_validator_lamports)
2517            .ok_or(StakePoolError::CalculationFailure)?;
2518        let stake_deposit_lamports = post_validator_stake
2519            .delegation
2520            .stake
2521            .checked_sub(validator_stake.delegation.stake)
2522            .ok_or(StakePoolError::CalculationFailure)?;
2523        let sol_deposit_lamports = total_deposit_lamports
2524            .checked_sub(stake_deposit_lamports)
2525            .ok_or(StakePoolError::CalculationFailure)?;
2526
2527        let new_pool_tokens = stake_pool
2528            .calc_pool_tokens_for_deposit(total_deposit_lamports)
2529            .ok_or(StakePoolError::CalculationFailure)?;
2530        let new_pool_tokens_from_stake = stake_pool
2531            .calc_pool_tokens_for_deposit(stake_deposit_lamports)
2532            .ok_or(StakePoolError::CalculationFailure)?;
2533        let new_pool_tokens_from_sol = new_pool_tokens
2534            .checked_sub(new_pool_tokens_from_stake)
2535            .ok_or(StakePoolError::CalculationFailure)?;
2536
2537        let stake_deposit_fee = stake_pool
2538            .calc_pool_tokens_stake_deposit_fee(new_pool_tokens_from_stake)
2539            .ok_or(StakePoolError::CalculationFailure)?;
2540        let sol_deposit_fee = stake_pool
2541            .calc_pool_tokens_sol_deposit_fee(new_pool_tokens_from_sol)
2542            .ok_or(StakePoolError::CalculationFailure)?;
2543
2544        let total_fee = stake_deposit_fee
2545            .checked_add(sol_deposit_fee)
2546            .ok_or(StakePoolError::CalculationFailure)?;
2547        let pool_tokens_user = new_pool_tokens
2548            .checked_sub(total_fee)
2549            .ok_or(StakePoolError::CalculationFailure)?;
2550
2551        let pool_tokens_referral_fee = stake_pool
2552            .calc_pool_tokens_stake_referral_fee(total_fee)
2553            .ok_or(StakePoolError::CalculationFailure)?;
2554
2555        let pool_tokens_manager_deposit_fee = total_fee
2556            .checked_sub(pool_tokens_referral_fee)
2557            .ok_or(StakePoolError::CalculationFailure)?;
2558
2559        if pool_tokens_user
2560            .saturating_add(pool_tokens_manager_deposit_fee)
2561            .saturating_add(pool_tokens_referral_fee)
2562            != new_pool_tokens
2563        {
2564            return Err(StakePoolError::CalculationFailure.into());
2565        }
2566
2567        if pool_tokens_user == 0 {
2568            return Err(StakePoolError::DepositTooSmall.into());
2569        }
2570
2571        if let Some(minimum_pool_tokens_out) = minimum_pool_tokens_out {
2572            if pool_tokens_user < minimum_pool_tokens_out {
2573                return Err(StakePoolError::ExceededSlippage.into());
2574            }
2575        }
2576
2577        Self::token_mint_to(
2578            stake_pool_info.key,
2579            token_program_info.clone(),
2580            pool_mint_info.clone(),
2581            dest_user_pool_info.clone(),
2582            withdraw_authority_info.clone(),
2583            AUTHORITY_WITHDRAW,
2584            stake_pool.stake_withdraw_bump_seed,
2585            pool_tokens_user,
2586        )?;
2587        if pool_tokens_manager_deposit_fee > 0 {
2588            Self::token_mint_to(
2589                stake_pool_info.key,
2590                token_program_info.clone(),
2591                pool_mint_info.clone(),
2592                manager_fee_info.clone(),
2593                withdraw_authority_info.clone(),
2594                AUTHORITY_WITHDRAW,
2595                stake_pool.stake_withdraw_bump_seed,
2596                pool_tokens_manager_deposit_fee,
2597            )?;
2598        }
2599        if pool_tokens_referral_fee > 0 {
2600            Self::token_mint_to(
2601                stake_pool_info.key,
2602                token_program_info.clone(),
2603                pool_mint_info.clone(),
2604                referrer_fee_info.clone(),
2605                withdraw_authority_info.clone(),
2606                AUTHORITY_WITHDRAW,
2607                stake_pool.stake_withdraw_bump_seed,
2608                pool_tokens_referral_fee,
2609            )?;
2610        }
2611
2612        // withdraw additional lamports to the reserve
2613        if sol_deposit_lamports > 0 {
2614            Self::stake_withdraw(
2615                stake_pool_info.key,
2616                validator_stake_account_info.clone(),
2617                withdraw_authority_info.clone(),
2618                AUTHORITY_WITHDRAW,
2619                stake_pool.stake_withdraw_bump_seed,
2620                reserve_stake_account_info.clone(),
2621                clock_info.clone(),
2622                stake_history_info.clone(),
2623                sol_deposit_lamports,
2624            )?;
2625        }
2626
2627        stake_pool.pool_token_supply = stake_pool
2628            .pool_token_supply
2629            .checked_add(new_pool_tokens)
2630            .ok_or(StakePoolError::CalculationFailure)?;
2631        // We treat the extra lamports as though they were
2632        // transferred directly to the reserve stake account.
2633        stake_pool.total_lamports = stake_pool
2634            .total_lamports
2635            .checked_add(total_deposit_lamports)
2636            .ok_or(StakePoolError::CalculationFailure)?;
2637        borsh::to_writer(&mut stake_pool_info.data.borrow_mut()[..], &stake_pool)?;
2638
2639        validator_stake_info.active_stake_lamports = validator_stake_account_info.lamports().into();
2640
2641        Ok(())
2642    }
2643
2644    /// Processes [`DepositSol`](enum.Instruction.html).
2645    #[inline(never)] // needed to avoid stack size violation
2646    fn process_deposit_sol(
2647        program_id: &Pubkey,
2648        accounts: &[AccountInfo],
2649        deposit_lamports: u64,
2650        minimum_pool_tokens_out: Option<u64>,
2651    ) -> ProgramResult {
2652        let account_info_iter = &mut accounts.iter();
2653        let stake_pool_info = next_account_info(account_info_iter)?;
2654        let withdraw_authority_info = next_account_info(account_info_iter)?;
2655        let reserve_stake_account_info = next_account_info(account_info_iter)?;
2656        let from_user_lamports_info = next_account_info(account_info_iter)?;
2657        let dest_user_pool_info = next_account_info(account_info_iter)?;
2658        let manager_fee_info = next_account_info(account_info_iter)?;
2659        let referrer_fee_info = next_account_info(account_info_iter)?;
2660        let pool_mint_info = next_account_info(account_info_iter)?;
2661        let system_program_info = next_account_info(account_info_iter)?;
2662        let token_program_info = next_account_info(account_info_iter)?;
2663        let sol_deposit_authority_info = next_account_info(account_info_iter);
2664
2665        let clock = Clock::get()?;
2666
2667        check_account_owner(stake_pool_info, program_id)?;
2668        let mut stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
2669        if !stake_pool.is_valid() {
2670            return Err(StakePoolError::InvalidState.into());
2671        }
2672
2673        stake_pool.check_authority_withdraw(
2674            withdraw_authority_info.key,
2675            program_id,
2676            stake_pool_info.key,
2677        )?;
2678        stake_pool.check_sol_deposit_authority(sol_deposit_authority_info)?;
2679        stake_pool.check_mint(pool_mint_info)?;
2680        stake_pool.check_reserve_stake(reserve_stake_account_info)?;
2681
2682        if stake_pool.token_program_id != *token_program_info.key {
2683            return Err(ProgramError::IncorrectProgramId);
2684        }
2685        check_system_program(system_program_info.key)?;
2686
2687        if stake_pool.manager_fee_account != *manager_fee_info.key {
2688            return Err(StakePoolError::InvalidFeeAccount.into());
2689        }
2690        // There is no bypass if the manager fee account is invalid. Deposits
2691        // don't hold user funds hostage, so if the fee account is invalid, users
2692        // cannot deposit in the pool.  Let it fail here!
2693
2694        // We want this to hold to ensure that deposit_sol mints pool tokens
2695        // at the right price
2696        if stake_pool.last_update_epoch < clock.epoch {
2697            return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
2698        }
2699
2700        let new_pool_tokens = stake_pool
2701            .calc_pool_tokens_for_deposit(deposit_lamports)
2702            .ok_or(StakePoolError::CalculationFailure)?;
2703
2704        let pool_tokens_sol_deposit_fee = stake_pool
2705            .calc_pool_tokens_sol_deposit_fee(new_pool_tokens)
2706            .ok_or(StakePoolError::CalculationFailure)?;
2707        let pool_tokens_user = new_pool_tokens
2708            .checked_sub(pool_tokens_sol_deposit_fee)
2709            .ok_or(StakePoolError::CalculationFailure)?;
2710
2711        let pool_tokens_referral_fee = stake_pool
2712            .calc_pool_tokens_sol_referral_fee(pool_tokens_sol_deposit_fee)
2713            .ok_or(StakePoolError::CalculationFailure)?;
2714        let pool_tokens_manager_deposit_fee = pool_tokens_sol_deposit_fee
2715            .checked_sub(pool_tokens_referral_fee)
2716            .ok_or(StakePoolError::CalculationFailure)?;
2717
2718        if pool_tokens_user
2719            .saturating_add(pool_tokens_manager_deposit_fee)
2720            .saturating_add(pool_tokens_referral_fee)
2721            != new_pool_tokens
2722        {
2723            return Err(StakePoolError::CalculationFailure.into());
2724        }
2725
2726        if pool_tokens_user == 0 {
2727            return Err(StakePoolError::DepositTooSmall.into());
2728        }
2729
2730        if let Some(minimum_pool_tokens_out) = minimum_pool_tokens_out {
2731            if pool_tokens_user < minimum_pool_tokens_out {
2732                return Err(StakePoolError::ExceededSlippage.into());
2733            }
2734        }
2735
2736        Self::sol_transfer(
2737            from_user_lamports_info.clone(),
2738            reserve_stake_account_info.clone(),
2739            deposit_lamports,
2740        )?;
2741
2742        Self::token_mint_to(
2743            stake_pool_info.key,
2744            token_program_info.clone(),
2745            pool_mint_info.clone(),
2746            dest_user_pool_info.clone(),
2747            withdraw_authority_info.clone(),
2748            AUTHORITY_WITHDRAW,
2749            stake_pool.stake_withdraw_bump_seed,
2750            pool_tokens_user,
2751        )?;
2752
2753        if pool_tokens_manager_deposit_fee > 0 {
2754            Self::token_mint_to(
2755                stake_pool_info.key,
2756                token_program_info.clone(),
2757                pool_mint_info.clone(),
2758                manager_fee_info.clone(),
2759                withdraw_authority_info.clone(),
2760                AUTHORITY_WITHDRAW,
2761                stake_pool.stake_withdraw_bump_seed,
2762                pool_tokens_manager_deposit_fee,
2763            )?;
2764        }
2765
2766        if pool_tokens_referral_fee > 0 {
2767            Self::token_mint_to(
2768                stake_pool_info.key,
2769                token_program_info.clone(),
2770                pool_mint_info.clone(),
2771                referrer_fee_info.clone(),
2772                withdraw_authority_info.clone(),
2773                AUTHORITY_WITHDRAW,
2774                stake_pool.stake_withdraw_bump_seed,
2775                pool_tokens_referral_fee,
2776            )?;
2777        }
2778
2779        stake_pool.pool_token_supply = stake_pool
2780            .pool_token_supply
2781            .checked_add(new_pool_tokens)
2782            .ok_or(StakePoolError::CalculationFailure)?;
2783        stake_pool.total_lamports = stake_pool
2784            .total_lamports
2785            .checked_add(deposit_lamports)
2786            .ok_or(StakePoolError::CalculationFailure)?;
2787        borsh::to_writer(&mut stake_pool_info.data.borrow_mut()[..], &stake_pool)?;
2788
2789        Ok(())
2790    }
2791
2792    /// Processes [`WithdrawStake`](enum.Instruction.html).
2793    #[inline(never)] // needed to avoid stack size violation
2794    fn process_withdraw_stake(
2795        program_id: &Pubkey,
2796        accounts: &[AccountInfo],
2797        pool_tokens: u64,
2798        minimum_lamports_out: Option<u64>,
2799    ) -> ProgramResult {
2800        let account_info_iter = &mut accounts.iter();
2801        let stake_pool_info = next_account_info(account_info_iter)?;
2802        let validator_list_info = next_account_info(account_info_iter)?;
2803        let withdraw_authority_info = next_account_info(account_info_iter)?;
2804        let stake_split_from = next_account_info(account_info_iter)?;
2805        let stake_split_to = next_account_info(account_info_iter)?;
2806        let user_stake_authority_info = next_account_info(account_info_iter)?;
2807        let user_transfer_authority_info = next_account_info(account_info_iter)?;
2808        let burn_from_pool_info = next_account_info(account_info_iter)?;
2809        let manager_fee_info = next_account_info(account_info_iter)?;
2810        let pool_mint_info = next_account_info(account_info_iter)?;
2811        let clock_info = next_account_info(account_info_iter)?;
2812        let clock = &Clock::from_account_info(clock_info)?;
2813        let token_program_info = next_account_info(account_info_iter)?;
2814        let stake_program_info = next_account_info(account_info_iter)?;
2815
2816        let rent = Rent::get()?;
2817
2818        check_stake_program(stake_program_info.key)?;
2819        check_account_owner(stake_pool_info, program_id)?;
2820        let mut stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
2821        if !stake_pool.is_valid() {
2822            return Err(StakePoolError::InvalidState.into());
2823        }
2824
2825        let decimals = stake_pool.check_mint(pool_mint_info)?;
2826        stake_pool.check_validator_list(validator_list_info)?;
2827        stake_pool.check_authority_withdraw(
2828            withdraw_authority_info.key,
2829            program_id,
2830            stake_pool_info.key,
2831        )?;
2832
2833        if stake_pool.manager_fee_account != *manager_fee_info.key {
2834            return Err(StakePoolError::InvalidFeeAccount.into());
2835        }
2836        if stake_pool.token_program_id != *token_program_info.key {
2837            return Err(ProgramError::IncorrectProgramId);
2838        }
2839
2840        if stake_pool.last_update_epoch < clock.epoch {
2841            return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
2842        }
2843
2844        check_account_owner(validator_list_info, program_id)?;
2845        let mut validator_list_data = validator_list_info.data.borrow_mut();
2846        let (header, mut validator_list) =
2847            ValidatorListHeader::deserialize_vec(&mut validator_list_data)?;
2848        if !header.is_valid() {
2849            return Err(StakePoolError::InvalidState.into());
2850        }
2851
2852        // To prevent a faulty manager fee account from preventing withdrawals
2853        // if the token program does not own the account, or if the account is not
2854        // initialized
2855        let pool_tokens_fee = if stake_pool.manager_fee_account == *burn_from_pool_info.key
2856            || stake_pool.check_manager_fee_info(manager_fee_info).is_err()
2857        {
2858            0
2859        } else {
2860            stake_pool
2861                .calc_pool_tokens_stake_withdrawal_fee(pool_tokens)
2862                .ok_or(StakePoolError::CalculationFailure)?
2863        };
2864        let pool_tokens_burnt = pool_tokens
2865            .checked_sub(pool_tokens_fee)
2866            .ok_or(StakePoolError::CalculationFailure)?;
2867
2868        let mut withdraw_lamports = stake_pool
2869            .calc_lamports_withdraw_amount(pool_tokens_burnt)
2870            .ok_or(StakePoolError::CalculationFailure)?;
2871
2872        if withdraw_lamports == 0 {
2873            return Err(StakePoolError::WithdrawalTooSmall.into());
2874        }
2875
2876        let split_from_rent = rent.minimum_balance(stake_split_from.data_len());
2877        let stake_minimum_delegation = stake::tools::get_minimum_delegation()?;
2878        let stake_state = try_from_slice_unchecked::<stake::state::StakeStateV2>(
2879            &stake_split_from.data.borrow(),
2880        )?;
2881        let required_lamports = minimum_stake_lamports(split_from_rent, stake_minimum_delegation);
2882
2883        let lamports_per_pool_token = stake_pool
2884            .get_lamports_per_pool_token()
2885            .ok_or(StakePoolError::CalculationFailure)?;
2886
2887        // Since this instruction performs a stake split on an active stake, the
2888        // source stake needs to have at least twice the minimum delegation
2889        // amount, so that both accounts have at least the minimum delegation
2890        // afterwards.
2891        let minimum_lamports_with_tolerance = required_lamports
2892            .saturating_add(stake_minimum_delegation)
2893            .saturating_add(lamports_per_pool_token);
2894
2895        let has_withdrawable_active_stake = validator_list
2896            .find::<ValidatorStakeInfo, _>(|x| {
2897                ValidatorStakeInfo::active_lamports_greater_than(
2898                    x,
2899                    &minimum_lamports_with_tolerance,
2900                ) && ValidatorStakeInfo::is_active(x)
2901            })
2902            .is_some();
2903        let has_withdrawable_transient_stake = validator_list
2904            .find::<ValidatorStakeInfo, _>(|x| {
2905                ValidatorStakeInfo::transient_lamports_greater_than(
2906                    x,
2907                    &minimum_lamports_with_tolerance,
2908                ) && ValidatorStakeInfo::is_active(x)
2909            })
2910            .is_some();
2911
2912        let validator_list_item_info = if *stake_split_from.key == stake_pool.reserve_stake {
2913            // check that the validator stake accounts have no withdrawable stake
2914            if has_withdrawable_transient_stake || has_withdrawable_active_stake {
2915                msg!("Error withdrawing from reserve: validator stake accounts have lamports available, please use those first.");
2916                return Err(StakePoolError::StakeLamportsNotEqualToMinimum.into());
2917            }
2918
2919            // check that reserve has enough
2920            let minimum_reserve_lamports = minimum_reserve_lamports(split_from_rent);
2921            if stake_split_from
2922                .lamports()
2923                .saturating_sub(withdraw_lamports)
2924                < minimum_reserve_lamports
2925            {
2926                msg!("Attempting to withdraw {} lamports, maximum possible SOL withdrawal is {} lamports",
2927                    withdraw_lamports,
2928                    stake_split_from.lamports().saturating_sub(minimum_reserve_lamports)
2929                );
2930                return Err(StakePoolError::SolWithdrawalTooLarge.into());
2931            }
2932            None
2933        } else {
2934            let delegation = stake_state
2935                .delegation()
2936                .ok_or(StakePoolError::WrongStakeStake)?;
2937            let vote_account_address = delegation.voter_pubkey;
2938
2939            if let Some(preferred_withdraw_validator) =
2940                stake_pool.preferred_withdraw_validator_vote_address
2941            {
2942                // Defensive check, in case the preferred validator was somehow
2943                // removed.
2944                if let Some(preferred_validator_info) = validator_list
2945                    .find::<ValidatorStakeInfo, _>(|x| {
2946                        ValidatorStakeInfo::memcmp_pubkey(x, &preferred_withdraw_validator)
2947                    })
2948                {
2949                    if preferred_withdraw_validator != vote_account_address
2950                        && u64::from(preferred_validator_info.active_stake_lamports)
2951                            >= minimum_lamports_with_tolerance
2952                    {
2953                        msg!("Validator vote address {} is preferred for withdrawals, it currently has {} lamports available. Please withdraw those before using other validator stake accounts.", preferred_withdraw_validator, u64::from(preferred_validator_info.active_stake_lamports));
2954                        return Err(StakePoolError::IncorrectWithdrawVoteAddress.into());
2955                    }
2956                } else {
2957                    msg!("Preferred withdraw validator not found, allowing withdrawal from any validator");
2958                }
2959            }
2960
2961            let validator_stake_info = validator_list
2962                .find_mut::<ValidatorStakeInfo, _>(|x| {
2963                    ValidatorStakeInfo::memcmp_pubkey(x, &vote_account_address)
2964                })
2965                .ok_or(StakePoolError::ValidatorNotFound)?;
2966
2967            let withdraw_source = if has_withdrawable_active_stake {
2968                // if there's any active stake, we must withdraw from an active
2969                // stake account
2970                check_validator_stake_address(
2971                    program_id,
2972                    stake_pool_info.key,
2973                    stake_split_from.key,
2974                    &vote_account_address,
2975                    NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()),
2976                )?;
2977                StakeWithdrawSource::Active
2978            } else if has_withdrawable_transient_stake
2979                || validator_stake_info.transient_stake_lamports != 0.into()
2980            {
2981                // if there's any transient stake, we must withdraw from there
2982                // Be particularly cautious to avoid removing a validator with
2983                // transient lamports tied to it
2984                check_transient_stake_address(
2985                    program_id,
2986                    stake_pool_info.key,
2987                    stake_split_from.key,
2988                    &vote_account_address,
2989                    validator_stake_info.transient_seed_suffix.into(),
2990                )?;
2991                StakeWithdrawSource::Transient
2992            } else {
2993                // if there's no active or transient stake, we can take the whole account
2994                check_validator_stake_address(
2995                    program_id,
2996                    stake_pool_info.key,
2997                    stake_split_from.key,
2998                    &vote_account_address,
2999                    NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()),
3000                )?;
3001                StakeWithdrawSource::ValidatorRemoval
3002            };
3003
3004            if validator_stake_info.status != StakeStatus::Active.into() {
3005                msg!("Validator is marked for removal and no longer allowing withdrawals");
3006                return Err(StakePoolError::ValidatorNotFound.into());
3007            }
3008
3009            match withdraw_source {
3010                StakeWithdrawSource::Active | StakeWithdrawSource::Transient => {
3011                    let remaining_lamports = stake_split_from
3012                        .lamports()
3013                        .saturating_sub(withdraw_lamports);
3014                    if remaining_lamports < required_lamports {
3015                        msg!("Attempting to withdraw {} lamports from validator account with {} stake lamports, {} must remain", withdraw_lamports, stake_split_from.lamports(), required_lamports);
3016                        return Err(StakePoolError::StakeLamportsNotEqualToMinimum.into());
3017                    }
3018                }
3019                StakeWithdrawSource::ValidatorRemoval => {
3020                    let split_from_lamports = stake_split_from.lamports();
3021                    let upper_bound = split_from_lamports.saturating_add(lamports_per_pool_token);
3022                    if withdraw_lamports < split_from_lamports || withdraw_lamports > upper_bound {
3023                        msg!(
3024                            "Cannot withdraw a whole account worth {} lamports, \
3025                              must withdraw at least {} lamports worth of pool tokens \
3026                              with a margin of {} lamports",
3027                            withdraw_lamports,
3028                            split_from_lamports,
3029                            lamports_per_pool_token
3030                        );
3031                        return Err(StakePoolError::StakeLamportsNotEqualToMinimum.into());
3032                    }
3033                    // truncate the lamports down to the amount in the account
3034                    withdraw_lamports = split_from_lamports;
3035
3036                    // reset the preferred validator if needed
3037                    if stake_pool.preferred_deposit_validator_vote_address
3038                        == Some(vote_account_address)
3039                    {
3040                        stake_pool.preferred_deposit_validator_vote_address = None;
3041                    }
3042                    if stake_pool.preferred_withdraw_validator_vote_address
3043                        == Some(vote_account_address)
3044                    {
3045                        stake_pool.preferred_withdraw_validator_vote_address = None;
3046                    }
3047                }
3048            }
3049            Some((validator_stake_info, withdraw_source))
3050        };
3051
3052        if let Some(minimum_lamports_out) = minimum_lamports_out {
3053            if withdraw_lamports < minimum_lamports_out {
3054                return Err(StakePoolError::ExceededSlippage.into());
3055            }
3056        }
3057
3058        Self::token_burn(
3059            token_program_info.clone(),
3060            burn_from_pool_info.clone(),
3061            pool_mint_info.clone(),
3062            user_transfer_authority_info.clone(),
3063            pool_tokens_burnt,
3064        )?;
3065
3066        Self::stake_split(
3067            stake_pool_info.key,
3068            stake_split_from.clone(),
3069            withdraw_authority_info.clone(),
3070            AUTHORITY_WITHDRAW,
3071            stake_pool.stake_withdraw_bump_seed,
3072            withdraw_lamports,
3073            stake_split_to.clone(),
3074        )?;
3075
3076        Self::stake_authorize_signed(
3077            stake_pool_info.key,
3078            stake_split_to.clone(),
3079            withdraw_authority_info.clone(),
3080            AUTHORITY_WITHDRAW,
3081            stake_pool.stake_withdraw_bump_seed,
3082            user_stake_authority_info.key,
3083            clock_info.clone(),
3084        )?;
3085
3086        if pool_tokens_fee > 0 {
3087            Self::token_transfer(
3088                token_program_info.clone(),
3089                burn_from_pool_info.clone(),
3090                pool_mint_info.clone(),
3091                manager_fee_info.clone(),
3092                user_transfer_authority_info.clone(),
3093                pool_tokens_fee,
3094                decimals,
3095            )?;
3096        }
3097
3098        stake_pool.pool_token_supply = stake_pool
3099            .pool_token_supply
3100            .checked_sub(pool_tokens_burnt)
3101            .ok_or(StakePoolError::CalculationFailure)?;
3102        stake_pool.total_lamports = stake_pool
3103            .total_lamports
3104            .checked_sub(withdraw_lamports)
3105            .ok_or(StakePoolError::CalculationFailure)?;
3106        borsh::to_writer(&mut stake_pool_info.data.borrow_mut()[..], &stake_pool)?;
3107
3108        if let Some((validator_list_item, withdraw_source)) = validator_list_item_info {
3109            match withdraw_source {
3110                StakeWithdrawSource::Active => {
3111                    validator_list_item.active_stake_lamports =
3112                        u64::from(validator_list_item.active_stake_lamports)
3113                            .checked_sub(withdraw_lamports)
3114                            .ok_or(StakePoolError::CalculationFailure)?
3115                            .into()
3116                }
3117                StakeWithdrawSource::Transient => {
3118                    validator_list_item.transient_stake_lamports =
3119                        u64::from(validator_list_item.transient_stake_lamports)
3120                            .checked_sub(withdraw_lamports)
3121                            .ok_or(StakePoolError::CalculationFailure)?
3122                            .into()
3123                }
3124                StakeWithdrawSource::ValidatorRemoval => {
3125                    validator_list_item.active_stake_lamports =
3126                        u64::from(validator_list_item.active_stake_lamports)
3127                            .checked_sub(withdraw_lamports)
3128                            .ok_or(StakePoolError::CalculationFailure)?
3129                            .into();
3130                    if u64::from(validator_list_item.active_stake_lamports) != 0 {
3131                        msg!("Attempting to remove a validator from the pool, but withdrawal leaves {} lamports, update the pool to merge any unaccounted lamports",
3132                            u64::from(validator_list_item.active_stake_lamports));
3133                        return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
3134                    }
3135                    // since we already checked that there's no transient stake,
3136                    // we can immediately set this as ready for removal
3137                    validator_list_item.status = StakeStatus::ReadyForRemoval.into();
3138                }
3139            }
3140        }
3141
3142        Ok(())
3143    }
3144
3145    /// Processes [`WithdrawSol`](enum.Instruction.html).
3146    #[inline(never)] // needed to avoid stack size violation
3147    fn process_withdraw_sol(
3148        program_id: &Pubkey,
3149        accounts: &[AccountInfo],
3150        pool_tokens: u64,
3151        minimum_lamports_out: Option<u64>,
3152    ) -> ProgramResult {
3153        let account_info_iter = &mut accounts.iter();
3154        let stake_pool_info = next_account_info(account_info_iter)?;
3155        let withdraw_authority_info = next_account_info(account_info_iter)?;
3156        let user_transfer_authority_info = next_account_info(account_info_iter)?;
3157        let burn_from_pool_info = next_account_info(account_info_iter)?;
3158        let reserve_stake_info = next_account_info(account_info_iter)?;
3159        let destination_lamports_info = next_account_info(account_info_iter)?;
3160        let manager_fee_info = next_account_info(account_info_iter)?;
3161        let pool_mint_info = next_account_info(account_info_iter)?;
3162        let clock_info = next_account_info(account_info_iter)?;
3163        let stake_history_info = next_account_info(account_info_iter)?;
3164        let stake_program_info = next_account_info(account_info_iter)?;
3165        let token_program_info = next_account_info(account_info_iter)?;
3166        let sol_withdraw_authority_info = next_account_info(account_info_iter);
3167
3168        let rent = Rent::get()?;
3169
3170        check_account_owner(stake_pool_info, program_id)?;
3171        let mut stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
3172        if !stake_pool.is_valid() {
3173            return Err(StakePoolError::InvalidState.into());
3174        }
3175
3176        stake_pool.check_authority_withdraw(
3177            withdraw_authority_info.key,
3178            program_id,
3179            stake_pool_info.key,
3180        )?;
3181        stake_pool.check_sol_withdraw_authority(sol_withdraw_authority_info)?;
3182        let decimals = stake_pool.check_mint(pool_mint_info)?;
3183        stake_pool.check_reserve_stake(reserve_stake_info)?;
3184
3185        if stake_pool.token_program_id != *token_program_info.key {
3186            return Err(ProgramError::IncorrectProgramId);
3187        }
3188        check_stake_program(stake_program_info.key)?;
3189
3190        if stake_pool.manager_fee_account != *manager_fee_info.key {
3191            return Err(StakePoolError::InvalidFeeAccount.into());
3192        }
3193
3194        // We want this to hold to ensure that withdraw_sol burns pool tokens
3195        // at the right price
3196        if stake_pool.last_update_epoch < Clock::get()?.epoch {
3197            return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
3198        }
3199
3200        // To prevent a faulty manager fee account from preventing withdrawals
3201        // if the token program does not own the account, or if the account is not
3202        // initialized
3203        let pool_tokens_fee = if stake_pool.manager_fee_account == *burn_from_pool_info.key
3204            || stake_pool.check_manager_fee_info(manager_fee_info).is_err()
3205        {
3206            0
3207        } else {
3208            stake_pool
3209                .calc_pool_tokens_sol_withdrawal_fee(pool_tokens)
3210                .ok_or(StakePoolError::CalculationFailure)?
3211        };
3212        let pool_tokens_burnt = pool_tokens
3213            .checked_sub(pool_tokens_fee)
3214            .ok_or(StakePoolError::CalculationFailure)?;
3215
3216        let withdraw_lamports = stake_pool
3217            .calc_lamports_withdraw_amount(pool_tokens_burnt)
3218            .ok_or(StakePoolError::CalculationFailure)?;
3219
3220        if withdraw_lamports == 0 {
3221            return Err(StakePoolError::WithdrawalTooSmall.into());
3222        }
3223
3224        if let Some(minimum_lamports_out) = minimum_lamports_out {
3225            if withdraw_lamports < minimum_lamports_out {
3226                return Err(StakePoolError::ExceededSlippage.into());
3227            }
3228        }
3229
3230        let reserve_rent = rent.minimum_balance(reserve_stake_info.data_len());
3231        let minimum_reserve_lamports = minimum_reserve_lamports(reserve_rent);
3232        let new_reserve_lamports = reserve_stake_info
3233            .lamports()
3234            .saturating_sub(withdraw_lamports);
3235
3236        if new_reserve_lamports < minimum_reserve_lamports {
3237            msg!("Attempting to withdraw {} lamports, maximum possible SOL withdrawal is {} lamports",
3238                withdraw_lamports,
3239                reserve_stake_info.lamports().saturating_sub(minimum_reserve_lamports)
3240            );
3241            return Err(StakePoolError::SolWithdrawalTooLarge.into());
3242        }
3243
3244        Self::token_burn(
3245            token_program_info.clone(),
3246            burn_from_pool_info.clone(),
3247            pool_mint_info.clone(),
3248            user_transfer_authority_info.clone(),
3249            pool_tokens_burnt,
3250        )?;
3251
3252        if pool_tokens_fee > 0 {
3253            Self::token_transfer(
3254                token_program_info.clone(),
3255                burn_from_pool_info.clone(),
3256                pool_mint_info.clone(),
3257                manager_fee_info.clone(),
3258                user_transfer_authority_info.clone(),
3259                pool_tokens_fee,
3260                decimals,
3261            )?;
3262        }
3263
3264        Self::stake_withdraw(
3265            stake_pool_info.key,
3266            reserve_stake_info.clone(),
3267            withdraw_authority_info.clone(),
3268            AUTHORITY_WITHDRAW,
3269            stake_pool.stake_withdraw_bump_seed,
3270            destination_lamports_info.clone(),
3271            clock_info.clone(),
3272            stake_history_info.clone(),
3273            withdraw_lamports,
3274        )?;
3275
3276        stake_pool.pool_token_supply = stake_pool
3277            .pool_token_supply
3278            .checked_sub(pool_tokens_burnt)
3279            .ok_or(StakePoolError::CalculationFailure)?;
3280        stake_pool.total_lamports = stake_pool
3281            .total_lamports
3282            .checked_sub(withdraw_lamports)
3283            .ok_or(StakePoolError::CalculationFailure)?;
3284        borsh::to_writer(&mut stake_pool_info.data.borrow_mut()[..], &stake_pool)?;
3285
3286        Ok(())
3287    }
3288
3289    #[inline(never)]
3290    fn process_create_pool_token_metadata(
3291        program_id: &Pubkey,
3292        accounts: &[AccountInfo],
3293        name: String,
3294        symbol: String,
3295        uri: String,
3296    ) -> ProgramResult {
3297        let account_info_iter = &mut accounts.iter();
3298        let stake_pool_info = next_account_info(account_info_iter)?;
3299        let manager_info = next_account_info(account_info_iter)?;
3300        let withdraw_authority_info = next_account_info(account_info_iter)?;
3301        let pool_mint_info = next_account_info(account_info_iter)?;
3302        let payer_info = next_account_info(account_info_iter)?;
3303        let metadata_info = next_account_info(account_info_iter)?;
3304        let mpl_token_metadata_program_info = next_account_info(account_info_iter)?;
3305        let system_program_info = next_account_info(account_info_iter)?;
3306
3307        if !payer_info.is_signer {
3308            msg!("Payer did not sign metadata creation");
3309            return Err(StakePoolError::SignatureMissing.into());
3310        }
3311
3312        check_system_program(system_program_info.key)?;
3313        check_account_owner(payer_info, &system_program::id())?;
3314        check_account_owner(stake_pool_info, program_id)?;
3315        check_mpl_metadata_program(mpl_token_metadata_program_info.key)?;
3316
3317        let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
3318        if !stake_pool.is_valid() {
3319            return Err(StakePoolError::InvalidState.into());
3320        }
3321
3322        stake_pool.check_manager(manager_info)?;
3323        stake_pool.check_authority_withdraw(
3324            withdraw_authority_info.key,
3325            program_id,
3326            stake_pool_info.key,
3327        )?;
3328        stake_pool.check_mint(pool_mint_info)?;
3329        check_mpl_metadata_account_address(metadata_info.key, &stake_pool.pool_mint)?;
3330
3331        // Token mint authority for stake-pool token is stake-pool withdraw authority
3332        let token_mint_authority = withdraw_authority_info;
3333
3334        let new_metadata_instruction = create_metadata_accounts_v3(
3335            *mpl_token_metadata_program_info.key,
3336            *metadata_info.key,
3337            *pool_mint_info.key,
3338            *token_mint_authority.key,
3339            *payer_info.key,
3340            *token_mint_authority.key,
3341            name,
3342            symbol,
3343            uri,
3344        );
3345
3346        let (_, stake_withdraw_bump_seed) =
3347            crate::find_withdraw_authority_program_address(program_id, stake_pool_info.key);
3348
3349        let token_mint_authority_signer_seeds: &[&[_]] = &[
3350            stake_pool_info.key.as_ref(),
3351            AUTHORITY_WITHDRAW,
3352            &[stake_withdraw_bump_seed],
3353        ];
3354
3355        invoke_signed(
3356            &new_metadata_instruction,
3357            &[
3358                metadata_info.clone(),
3359                pool_mint_info.clone(),
3360                withdraw_authority_info.clone(),
3361                payer_info.clone(),
3362                withdraw_authority_info.clone(),
3363                system_program_info.clone(),
3364            ],
3365            &[token_mint_authority_signer_seeds],
3366        )?;
3367
3368        Ok(())
3369    }
3370
3371    #[inline(never)]
3372    fn process_update_pool_token_metadata(
3373        program_id: &Pubkey,
3374        accounts: &[AccountInfo],
3375        name: String,
3376        symbol: String,
3377        uri: String,
3378    ) -> ProgramResult {
3379        let account_info_iter = &mut accounts.iter();
3380
3381        let stake_pool_info = next_account_info(account_info_iter)?;
3382        let manager_info = next_account_info(account_info_iter)?;
3383        let withdraw_authority_info = next_account_info(account_info_iter)?;
3384        let metadata_info = next_account_info(account_info_iter)?;
3385        let mpl_token_metadata_program_info = next_account_info(account_info_iter)?;
3386
3387        check_account_owner(stake_pool_info, program_id)?;
3388
3389        check_mpl_metadata_program(mpl_token_metadata_program_info.key)?;
3390
3391        let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
3392        if !stake_pool.is_valid() {
3393            return Err(StakePoolError::InvalidState.into());
3394        }
3395
3396        stake_pool.check_manager(manager_info)?;
3397        stake_pool.check_authority_withdraw(
3398            withdraw_authority_info.key,
3399            program_id,
3400            stake_pool_info.key,
3401        )?;
3402        check_mpl_metadata_account_address(metadata_info.key, &stake_pool.pool_mint)?;
3403
3404        // Token mint authority for stake-pool token is withdraw authority only
3405        let token_mint_authority = withdraw_authority_info;
3406
3407        let update_metadata_accounts_instruction = update_metadata_accounts_v2(
3408            *mpl_token_metadata_program_info.key,
3409            *metadata_info.key,
3410            *token_mint_authority.key,
3411            None,
3412            Some(DataV2 {
3413                name,
3414                symbol,
3415                uri,
3416                seller_fee_basis_points: 0,
3417                creators: None,
3418                collection: None,
3419                uses: None,
3420            }),
3421            None,
3422            Some(true),
3423        );
3424
3425        let (_, stake_withdraw_bump_seed) =
3426            crate::find_withdraw_authority_program_address(program_id, stake_pool_info.key);
3427
3428        let token_mint_authority_signer_seeds: &[&[_]] = &[
3429            stake_pool_info.key.as_ref(),
3430            AUTHORITY_WITHDRAW,
3431            &[stake_withdraw_bump_seed],
3432        ];
3433
3434        invoke_signed(
3435            &update_metadata_accounts_instruction,
3436            &[metadata_info.clone(), withdraw_authority_info.clone()],
3437            &[token_mint_authority_signer_seeds],
3438        )?;
3439
3440        Ok(())
3441    }
3442
3443    /// Processes [`SetManager`](enum.Instruction.html).
3444    #[inline(never)] // needed to avoid stack size violation
3445    fn process_set_manager(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
3446        let account_info_iter = &mut accounts.iter();
3447        let stake_pool_info = next_account_info(account_info_iter)?;
3448        let manager_info = next_account_info(account_info_iter)?;
3449        let new_manager_info = next_account_info(account_info_iter)?;
3450        let new_manager_fee_info = next_account_info(account_info_iter)?;
3451
3452        check_account_owner(stake_pool_info, program_id)?;
3453        let mut stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
3454        check_account_owner(new_manager_fee_info, &stake_pool.token_program_id)?;
3455        if !stake_pool.is_valid() {
3456            return Err(StakePoolError::InvalidState.into());
3457        }
3458
3459        stake_pool.check_manager(manager_info)?;
3460        if !new_manager_info.is_signer {
3461            msg!("New manager signature missing");
3462            return Err(StakePoolError::SignatureMissing.into());
3463        }
3464
3465        stake_pool.check_manager_fee_info(new_manager_fee_info)?;
3466
3467        stake_pool.manager = *new_manager_info.key;
3468        stake_pool.manager_fee_account = *new_manager_fee_info.key;
3469        borsh::to_writer(&mut stake_pool_info.data.borrow_mut()[..], &stake_pool)?;
3470        Ok(())
3471    }
3472
3473    /// Processes [`SetFee`](enum.Instruction.html).
3474    #[inline(never)] // needed to avoid stack size violation
3475    fn process_set_fee(
3476        program_id: &Pubkey,
3477        accounts: &[AccountInfo],
3478        fee: FeeType,
3479    ) -> ProgramResult {
3480        let account_info_iter = &mut accounts.iter();
3481        let stake_pool_info = next_account_info(account_info_iter)?;
3482        let manager_info = next_account_info(account_info_iter)?;
3483        let clock = Clock::get()?;
3484
3485        check_account_owner(stake_pool_info, program_id)?;
3486        let mut stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
3487        if !stake_pool.is_valid() {
3488            return Err(StakePoolError::InvalidState.into());
3489        }
3490        stake_pool.check_manager(manager_info)?;
3491
3492        if fee.can_only_change_next_epoch() && stake_pool.last_update_epoch < clock.epoch {
3493            return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
3494        }
3495
3496        fee.check_too_high()?;
3497        stake_pool.update_fee(&fee)?;
3498        borsh::to_writer(&mut stake_pool_info.data.borrow_mut()[..], &stake_pool)?;
3499        Ok(())
3500    }
3501
3502    /// Processes [`SetStaker`](enum.Instruction.html).
3503    #[inline(never)] // needed to avoid stack size violation
3504    fn process_set_staker(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
3505        let account_info_iter = &mut accounts.iter();
3506        let stake_pool_info = next_account_info(account_info_iter)?;
3507        let set_staker_authority_info = next_account_info(account_info_iter)?;
3508        let new_staker_info = next_account_info(account_info_iter)?;
3509
3510        check_account_owner(stake_pool_info, program_id)?;
3511        let mut stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
3512        if !stake_pool.is_valid() {
3513            return Err(StakePoolError::InvalidState.into());
3514        }
3515
3516        let staker_signed = stake_pool.check_staker(set_staker_authority_info);
3517        let manager_signed = stake_pool.check_manager(set_staker_authority_info);
3518        if staker_signed.is_err() && manager_signed.is_err() {
3519            return Err(StakePoolError::SignatureMissing.into());
3520        }
3521        stake_pool.staker = *new_staker_info.key;
3522        borsh::to_writer(&mut stake_pool_info.data.borrow_mut()[..], &stake_pool)?;
3523        Ok(())
3524    }
3525
3526    /// Processes [`SetFundingAuthority`](enum.Instruction.html).
3527    #[inline(never)] // needed to avoid stack size violation
3528    fn process_set_funding_authority(
3529        program_id: &Pubkey,
3530        accounts: &[AccountInfo],
3531        funding_type: FundingType,
3532    ) -> ProgramResult {
3533        let account_info_iter = &mut accounts.iter();
3534        let stake_pool_info = next_account_info(account_info_iter)?;
3535        let manager_info = next_account_info(account_info_iter)?;
3536
3537        let new_authority = next_account_info(account_info_iter)
3538            .ok()
3539            .map(|new_authority_account_info| *new_authority_account_info.key);
3540
3541        check_account_owner(stake_pool_info, program_id)?;
3542        let mut stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
3543        if !stake_pool.is_valid() {
3544            return Err(StakePoolError::InvalidState.into());
3545        }
3546        stake_pool.check_manager(manager_info)?;
3547        match funding_type {
3548            FundingType::StakeDeposit => {
3549                stake_pool.stake_deposit_authority = new_authority.unwrap_or(
3550                    find_deposit_authority_program_address(program_id, stake_pool_info.key).0,
3551                );
3552            }
3553            FundingType::SolDeposit => stake_pool.sol_deposit_authority = new_authority,
3554            FundingType::SolWithdraw => stake_pool.sol_withdraw_authority = new_authority,
3555        }
3556        borsh::to_writer(&mut stake_pool_info.data.borrow_mut()[..], &stake_pool)?;
3557        Ok(())
3558    }
3559
3560    /// Processes [`Instruction`](enum.Instruction.html).
3561    pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
3562        let instruction = StakePoolInstruction::try_from_slice(input)?;
3563        match instruction {
3564            StakePoolInstruction::Initialize {
3565                fee,
3566                withdrawal_fee,
3567                deposit_fee,
3568                referral_fee,
3569                max_validators,
3570            } => {
3571                msg!("Instruction: Initialize stake pool");
3572                Self::process_initialize(
3573                    program_id,
3574                    accounts,
3575                    fee,
3576                    withdrawal_fee,
3577                    deposit_fee,
3578                    referral_fee,
3579                    max_validators,
3580                )
3581            }
3582            StakePoolInstruction::AddValidatorToPool(seed) => {
3583                msg!("Instruction: AddValidatorToPool");
3584                Self::process_add_validator_to_pool(program_id, accounts, seed)
3585            }
3586            StakePoolInstruction::RemoveValidatorFromPool => {
3587                msg!("Instruction: RemoveValidatorFromPool");
3588                Self::process_remove_validator_from_pool(program_id, accounts)
3589            }
3590            StakePoolInstruction::DecreaseValidatorStake {
3591                lamports,
3592                transient_stake_seed,
3593            } => {
3594                msg!("Instruction: DecreaseValidatorStake");
3595                msg!("NOTE: This instruction is deprecated, please use `DecreaseValidatorStakeWithReserve`");
3596                Self::process_decrease_validator_stake(
3597                    program_id,
3598                    accounts,
3599                    lamports,
3600                    transient_stake_seed,
3601                    None,
3602                    false,
3603                )
3604            }
3605            StakePoolInstruction::DecreaseValidatorStakeWithReserve {
3606                lamports,
3607                transient_stake_seed,
3608            } => {
3609                msg!("Instruction: DecreaseValidatorStakeWithReserve");
3610                Self::process_decrease_validator_stake(
3611                    program_id,
3612                    accounts,
3613                    lamports,
3614                    transient_stake_seed,
3615                    None,
3616                    true,
3617                )
3618            }
3619            StakePoolInstruction::DecreaseAdditionalValidatorStake {
3620                lamports,
3621                transient_stake_seed,
3622                ephemeral_stake_seed,
3623            } => {
3624                msg!("Instruction: DecreaseAdditionalValidatorStake");
3625                Self::process_decrease_validator_stake(
3626                    program_id,
3627                    accounts,
3628                    lamports,
3629                    transient_stake_seed,
3630                    Some(ephemeral_stake_seed),
3631                    true,
3632                )
3633            }
3634            StakePoolInstruction::IncreaseValidatorStake {
3635                lamports,
3636                transient_stake_seed,
3637            } => {
3638                msg!("Instruction: IncreaseValidatorStake");
3639                Self::process_increase_validator_stake(
3640                    program_id,
3641                    accounts,
3642                    lamports,
3643                    transient_stake_seed,
3644                    None,
3645                )
3646            }
3647            StakePoolInstruction::IncreaseAdditionalValidatorStake {
3648                lamports,
3649                transient_stake_seed,
3650                ephemeral_stake_seed,
3651            } => {
3652                msg!("Instruction: IncreaseAdditionalValidatorStake");
3653                Self::process_increase_validator_stake(
3654                    program_id,
3655                    accounts,
3656                    lamports,
3657                    transient_stake_seed,
3658                    Some(ephemeral_stake_seed),
3659                )
3660            }
3661            StakePoolInstruction::SetPreferredValidator {
3662                validator_type,
3663                validator_vote_address,
3664            } => {
3665                msg!("Instruction: SetPreferredValidator");
3666                Self::process_set_preferred_validator(
3667                    program_id,
3668                    accounts,
3669                    validator_type,
3670                    validator_vote_address,
3671                )
3672            }
3673            StakePoolInstruction::UpdateValidatorListBalance {
3674                start_index,
3675                no_merge,
3676            } => {
3677                msg!("Instruction: UpdateValidatorListBalance");
3678                Self::process_update_validator_list_balance(
3679                    program_id,
3680                    accounts,
3681                    start_index,
3682                    no_merge,
3683                )
3684            }
3685            StakePoolInstruction::UpdateStakePoolBalance => {
3686                msg!("Instruction: UpdateStakePoolBalance");
3687                Self::process_update_stake_pool_balance(program_id, accounts)
3688            }
3689            StakePoolInstruction::CleanupRemovedValidatorEntries => {
3690                msg!("Instruction: CleanupRemovedValidatorEntries");
3691                Self::process_cleanup_removed_validator_entries(program_id, accounts)
3692            }
3693            StakePoolInstruction::DepositStake => {
3694                msg!("Instruction: DepositStake");
3695                Self::process_deposit_stake(program_id, accounts, None)
3696            }
3697            StakePoolInstruction::WithdrawStake(amount) => {
3698                msg!("Instruction: WithdrawStake");
3699                Self::process_withdraw_stake(program_id, accounts, amount, None)
3700            }
3701            StakePoolInstruction::SetFee { fee } => {
3702                msg!("Instruction: SetFee");
3703                Self::process_set_fee(program_id, accounts, fee)
3704            }
3705            StakePoolInstruction::SetManager => {
3706                msg!("Instruction: SetManager");
3707                Self::process_set_manager(program_id, accounts)
3708            }
3709            StakePoolInstruction::SetStaker => {
3710                msg!("Instruction: SetStaker");
3711                Self::process_set_staker(program_id, accounts)
3712            }
3713            StakePoolInstruction::SetFundingAuthority(funding_type) => {
3714                msg!("Instruction: SetFundingAuthority");
3715                Self::process_set_funding_authority(program_id, accounts, funding_type)
3716            }
3717            StakePoolInstruction::DepositSol(lamports) => {
3718                msg!("Instruction: DepositSol");
3719                Self::process_deposit_sol(program_id, accounts, lamports, None)
3720            }
3721            StakePoolInstruction::WithdrawSol(pool_tokens) => {
3722                msg!("Instruction: WithdrawSol");
3723                Self::process_withdraw_sol(program_id, accounts, pool_tokens, None)
3724            }
3725            StakePoolInstruction::CreateTokenMetadata { name, symbol, uri } => {
3726                msg!("Instruction: CreateTokenMetadata");
3727                Self::process_create_pool_token_metadata(program_id, accounts, name, symbol, uri)
3728            }
3729            StakePoolInstruction::UpdateTokenMetadata { name, symbol, uri } => {
3730                msg!("Instruction: UpdateTokenMetadata");
3731                Self::process_update_pool_token_metadata(program_id, accounts, name, symbol, uri)
3732            }
3733            #[allow(deprecated)]
3734            StakePoolInstruction::Redelegate { .. } => {
3735                msg!("Instruction: Redelegate will not be enabled");
3736                Err(ProgramError::InvalidInstructionData)
3737            }
3738            StakePoolInstruction::DepositStakeWithSlippage {
3739                minimum_pool_tokens_out,
3740            } => {
3741                msg!("Instruction: DepositStakeWithSlippage");
3742                Self::process_deposit_stake(program_id, accounts, Some(minimum_pool_tokens_out))
3743            }
3744            StakePoolInstruction::WithdrawStakeWithSlippage {
3745                pool_tokens_in,
3746                minimum_lamports_out,
3747            } => {
3748                msg!("Instruction: WithdrawStakeWithSlippage");
3749                Self::process_withdraw_stake(
3750                    program_id,
3751                    accounts,
3752                    pool_tokens_in,
3753                    Some(minimum_lamports_out),
3754                )
3755            }
3756            StakePoolInstruction::DepositSolWithSlippage {
3757                lamports_in,
3758                minimum_pool_tokens_out,
3759            } => {
3760                msg!("Instruction: DepositSolWithSlippage");
3761                Self::process_deposit_sol(
3762                    program_id,
3763                    accounts,
3764                    lamports_in,
3765                    Some(minimum_pool_tokens_out),
3766                )
3767            }
3768            StakePoolInstruction::WithdrawSolWithSlippage {
3769                pool_tokens_in,
3770                minimum_lamports_out,
3771            } => {
3772                msg!("Instruction: WithdrawSolWithSlippage");
3773                Self::process_withdraw_sol(
3774                    program_id,
3775                    accounts,
3776                    pool_tokens_in,
3777                    Some(minimum_lamports_out),
3778                )
3779            }
3780        }
3781    }
3782}