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