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