spl_stake_pool/
processor.rs

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