spl_stake_pool/
processor.rs

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