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