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