1#![allow(deprecated)]
5
6use {
7 crate::state::{Authorized, Lockup, StakeAuthorize},
8 solana_clock::{Epoch, UnixTimestamp},
9 solana_pubkey::Pubkey,
10};
11#[cfg(feature = "bincode")]
12use {
13 crate::{config, program::ID, state::StakeStateV2},
14 solana_instruction::{AccountMeta, Instruction},
15};
16
17#[cfg(feature = "bincode")]
23const CLOCK_ID: Pubkey = Pubkey::from_str_const("SysvarC1ock11111111111111111111111111111111");
24
25#[cfg(feature = "bincode")]
26const RENT_ID: Pubkey = Pubkey::from_str_const("SysvarRent111111111111111111111111111111111");
27
28#[cfg(feature = "bincode")]
29const STAKE_HISTORY_ID: Pubkey =
30 Pubkey::from_str_const("SysvarStakeHistory1111111111111111111111111");
31
32#[cfg_attr(
33 feature = "serde",
34 derive(serde_derive::Deserialize, serde_derive::Serialize)
35)]
36#[derive(Debug, PartialEq, Eq, Clone)]
37pub enum StakeInstruction {
38 Initialize(Authorized, Lockup),
48
49 Authorize(Pubkey, StakeAuthorize),
58
59 DelegateStake,
72
73 Split(u64),
80
81 Withdraw(u64),
94
95 Deactivate,
102
103 SetLockup(LockupArgs),
112
113 Merge,
138
139 AuthorizeWithSeed(AuthorizeWithSeedArgs),
148
149 InitializeChecked,
160
161 AuthorizeChecked(StakeAuthorize),
174
175 AuthorizeCheckedWithSeed(AuthorizeCheckedWithSeedArgs),
188
189 SetLockupChecked(LockupCheckedArgs),
202
203 GetMinimumDelegation,
214
215 DeactivateDelinquent,
227
228 #[deprecated(since = "2.1.0", note = "Redelegate will not be enabled")]
249 Redelegate,
250
251 MoveStake(u64),
271
272 MoveLamports(u64),
286}
287
288#[cfg_attr(
289 feature = "serde",
290 derive(serde_derive::Deserialize, serde_derive::Serialize)
291)]
292#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
293pub struct LockupArgs {
294 pub unix_timestamp: Option<UnixTimestamp>,
295 pub epoch: Option<Epoch>,
296 pub custodian: Option<Pubkey>,
297}
298
299#[cfg_attr(
300 feature = "serde",
301 derive(serde_derive::Deserialize, serde_derive::Serialize)
302)]
303#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
304pub struct LockupCheckedArgs {
305 pub unix_timestamp: Option<UnixTimestamp>,
306 pub epoch: Option<Epoch>,
307}
308
309#[cfg_attr(
310 feature = "serde",
311 derive(serde_derive::Deserialize, serde_derive::Serialize)
312)]
313#[derive(Debug, PartialEq, Eq, Clone)]
314pub struct AuthorizeWithSeedArgs {
315 pub new_authorized_pubkey: Pubkey,
316 pub stake_authorize: StakeAuthorize,
317 pub authority_seed: String,
318 pub authority_owner: Pubkey,
319}
320
321#[cfg_attr(
322 feature = "serde",
323 derive(serde_derive::Deserialize, serde_derive::Serialize)
324)]
325#[derive(Debug, PartialEq, Eq, Clone)]
326pub struct AuthorizeCheckedWithSeedArgs {
327 pub stake_authorize: StakeAuthorize,
328 pub authority_seed: String,
329 pub authority_owner: Pubkey,
330}
331
332#[cfg(feature = "bincode")]
333pub fn initialize(stake_pubkey: &Pubkey, authorized: &Authorized, lockup: &Lockup) -> Instruction {
334 Instruction::new_with_bincode(
335 ID,
336 &StakeInstruction::Initialize(*authorized, *lockup),
337 vec![
338 AccountMeta::new(*stake_pubkey, false),
339 AccountMeta::new_readonly(RENT_ID, false),
340 ],
341 )
342}
343
344#[cfg(feature = "bincode")]
345pub fn initialize_checked(stake_pubkey: &Pubkey, authorized: &Authorized) -> Instruction {
346 Instruction::new_with_bincode(
347 ID,
348 &StakeInstruction::InitializeChecked,
349 vec![
350 AccountMeta::new(*stake_pubkey, false),
351 AccountMeta::new_readonly(RENT_ID, false),
352 AccountMeta::new_readonly(authorized.staker, false),
353 AccountMeta::new_readonly(authorized.withdrawer, true),
354 ],
355 )
356}
357
358#[cfg(feature = "bincode")]
359pub fn create_account_with_seed(
360 from_pubkey: &Pubkey,
361 stake_pubkey: &Pubkey,
362 base: &Pubkey,
363 seed: &str,
364 authorized: &Authorized,
365 lockup: &Lockup,
366 lamports: u64,
367) -> Vec<Instruction> {
368 vec![
369 solana_system_interface::instruction::create_account_with_seed(
370 from_pubkey,
371 stake_pubkey,
372 base,
373 seed,
374 lamports,
375 StakeStateV2::size_of() as u64,
376 &ID,
377 ),
378 initialize(stake_pubkey, authorized, lockup),
379 ]
380}
381
382#[cfg(feature = "bincode")]
383pub fn create_account(
384 from_pubkey: &Pubkey,
385 stake_pubkey: &Pubkey,
386 authorized: &Authorized,
387 lockup: &Lockup,
388 lamports: u64,
389) -> Vec<Instruction> {
390 vec![
391 solana_system_interface::instruction::create_account(
392 from_pubkey,
393 stake_pubkey,
394 lamports,
395 StakeStateV2::size_of() as u64,
396 &ID,
397 ),
398 initialize(stake_pubkey, authorized, lockup),
399 ]
400}
401
402#[cfg(feature = "bincode")]
403pub fn create_account_with_seed_checked(
404 from_pubkey: &Pubkey,
405 stake_pubkey: &Pubkey,
406 base: &Pubkey,
407 seed: &str,
408 authorized: &Authorized,
409 lamports: u64,
410) -> Vec<Instruction> {
411 vec![
412 solana_system_interface::instruction::create_account_with_seed(
413 from_pubkey,
414 stake_pubkey,
415 base,
416 seed,
417 lamports,
418 StakeStateV2::size_of() as u64,
419 &ID,
420 ),
421 initialize_checked(stake_pubkey, authorized),
422 ]
423}
424
425#[cfg(feature = "bincode")]
426pub fn create_account_checked(
427 from_pubkey: &Pubkey,
428 stake_pubkey: &Pubkey,
429 authorized: &Authorized,
430 lamports: u64,
431) -> Vec<Instruction> {
432 vec![
433 solana_system_interface::instruction::create_account(
434 from_pubkey,
435 stake_pubkey,
436 lamports,
437 StakeStateV2::size_of() as u64,
438 &ID,
439 ),
440 initialize_checked(stake_pubkey, authorized),
441 ]
442}
443
444#[cfg(feature = "bincode")]
445fn _split(
446 stake_pubkey: &Pubkey,
447 authorized_pubkey: &Pubkey,
448 lamports: u64,
449 split_stake_pubkey: &Pubkey,
450) -> Instruction {
451 let account_metas = vec![
452 AccountMeta::new(*stake_pubkey, false),
453 AccountMeta::new(*split_stake_pubkey, false),
454 AccountMeta::new_readonly(*authorized_pubkey, true),
455 ];
456
457 Instruction::new_with_bincode(ID, &StakeInstruction::Split(lamports), account_metas)
458}
459
460#[cfg(feature = "bincode")]
461pub fn split(
462 stake_pubkey: &Pubkey,
463 authorized_pubkey: &Pubkey,
464 lamports: u64,
465 split_stake_pubkey: &Pubkey,
466) -> Vec<Instruction> {
467 vec![
468 solana_system_interface::instruction::allocate(
469 split_stake_pubkey,
470 StakeStateV2::size_of() as u64,
471 ),
472 solana_system_interface::instruction::assign(split_stake_pubkey, &ID),
473 _split(
474 stake_pubkey,
475 authorized_pubkey,
476 lamports,
477 split_stake_pubkey,
478 ),
479 ]
480}
481
482#[cfg(feature = "bincode")]
483pub fn split_with_seed(
484 stake_pubkey: &Pubkey,
485 authorized_pubkey: &Pubkey,
486 lamports: u64,
487 split_stake_pubkey: &Pubkey, base: &Pubkey, seed: &str, ) -> Vec<Instruction> {
491 vec![
492 solana_system_interface::instruction::allocate_with_seed(
493 split_stake_pubkey,
494 base,
495 seed,
496 StakeStateV2::size_of() as u64,
497 &ID,
498 ),
499 _split(
500 stake_pubkey,
501 authorized_pubkey,
502 lamports,
503 split_stake_pubkey,
504 ),
505 ]
506}
507
508#[cfg(feature = "bincode")]
509pub fn merge(
510 destination_stake_pubkey: &Pubkey,
511 source_stake_pubkey: &Pubkey,
512 authorized_pubkey: &Pubkey,
513) -> Vec<Instruction> {
514 let account_metas = vec![
515 AccountMeta::new(*destination_stake_pubkey, false),
516 AccountMeta::new(*source_stake_pubkey, false),
517 AccountMeta::new_readonly(CLOCK_ID, false),
518 AccountMeta::new_readonly(STAKE_HISTORY_ID, false),
519 AccountMeta::new_readonly(*authorized_pubkey, true),
520 ];
521
522 vec![Instruction::new_with_bincode(
523 ID,
524 &StakeInstruction::Merge,
525 account_metas,
526 )]
527}
528
529#[cfg(feature = "bincode")]
530pub fn create_account_and_delegate_stake(
531 from_pubkey: &Pubkey,
532 stake_pubkey: &Pubkey,
533 vote_pubkey: &Pubkey,
534 authorized: &Authorized,
535 lockup: &Lockup,
536 lamports: u64,
537) -> Vec<Instruction> {
538 let mut instructions = create_account(from_pubkey, stake_pubkey, authorized, lockup, lamports);
539 instructions.push(delegate_stake(
540 stake_pubkey,
541 &authorized.staker,
542 vote_pubkey,
543 ));
544 instructions
545}
546
547#[cfg(feature = "bincode")]
548#[allow(clippy::too_many_arguments)]
549pub fn create_account_with_seed_and_delegate_stake(
550 from_pubkey: &Pubkey,
551 stake_pubkey: &Pubkey,
552 base: &Pubkey,
553 seed: &str,
554 vote_pubkey: &Pubkey,
555 authorized: &Authorized,
556 lockup: &Lockup,
557 lamports: u64,
558) -> Vec<Instruction> {
559 let mut instructions = create_account_with_seed(
560 from_pubkey,
561 stake_pubkey,
562 base,
563 seed,
564 authorized,
565 lockup,
566 lamports,
567 );
568 instructions.push(delegate_stake(
569 stake_pubkey,
570 &authorized.staker,
571 vote_pubkey,
572 ));
573 instructions
574}
575
576#[cfg(feature = "bincode")]
577pub fn authorize(
578 stake_pubkey: &Pubkey,
579 authorized_pubkey: &Pubkey,
580 new_authorized_pubkey: &Pubkey,
581 stake_authorize: StakeAuthorize,
582 custodian_pubkey: Option<&Pubkey>,
583) -> Instruction {
584 let mut account_metas = vec![
585 AccountMeta::new(*stake_pubkey, false),
586 AccountMeta::new_readonly(CLOCK_ID, false),
587 AccountMeta::new_readonly(*authorized_pubkey, true),
588 ];
589
590 if let Some(custodian_pubkey) = custodian_pubkey {
591 account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
592 }
593
594 Instruction::new_with_bincode(
595 ID,
596 &StakeInstruction::Authorize(*new_authorized_pubkey, stake_authorize),
597 account_metas,
598 )
599}
600
601#[cfg(feature = "bincode")]
602pub fn authorize_checked(
603 stake_pubkey: &Pubkey,
604 authorized_pubkey: &Pubkey,
605 new_authorized_pubkey: &Pubkey,
606 stake_authorize: StakeAuthorize,
607 custodian_pubkey: Option<&Pubkey>,
608) -> Instruction {
609 let mut account_metas = vec![
610 AccountMeta::new(*stake_pubkey, false),
611 AccountMeta::new_readonly(CLOCK_ID, false),
612 AccountMeta::new_readonly(*authorized_pubkey, true),
613 AccountMeta::new_readonly(*new_authorized_pubkey, true),
614 ];
615
616 if let Some(custodian_pubkey) = custodian_pubkey {
617 account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
618 }
619
620 Instruction::new_with_bincode(
621 ID,
622 &StakeInstruction::AuthorizeChecked(stake_authorize),
623 account_metas,
624 )
625}
626
627#[cfg(feature = "bincode")]
628pub fn authorize_with_seed(
629 stake_pubkey: &Pubkey,
630 authority_base: &Pubkey,
631 authority_seed: String,
632 authority_owner: &Pubkey,
633 new_authorized_pubkey: &Pubkey,
634 stake_authorize: StakeAuthorize,
635 custodian_pubkey: Option<&Pubkey>,
636) -> Instruction {
637 let mut account_metas = vec![
638 AccountMeta::new(*stake_pubkey, false),
639 AccountMeta::new_readonly(*authority_base, true),
640 AccountMeta::new_readonly(CLOCK_ID, false),
641 ];
642
643 if let Some(custodian_pubkey) = custodian_pubkey {
644 account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
645 }
646
647 let args = AuthorizeWithSeedArgs {
648 new_authorized_pubkey: *new_authorized_pubkey,
649 stake_authorize,
650 authority_seed,
651 authority_owner: *authority_owner,
652 };
653
654 Instruction::new_with_bincode(
655 ID,
656 &StakeInstruction::AuthorizeWithSeed(args),
657 account_metas,
658 )
659}
660
661#[cfg(feature = "bincode")]
662pub fn authorize_checked_with_seed(
663 stake_pubkey: &Pubkey,
664 authority_base: &Pubkey,
665 authority_seed: String,
666 authority_owner: &Pubkey,
667 new_authorized_pubkey: &Pubkey,
668 stake_authorize: StakeAuthorize,
669 custodian_pubkey: Option<&Pubkey>,
670) -> Instruction {
671 let mut account_metas = vec![
672 AccountMeta::new(*stake_pubkey, false),
673 AccountMeta::new_readonly(*authority_base, true),
674 AccountMeta::new_readonly(CLOCK_ID, false),
675 AccountMeta::new_readonly(*new_authorized_pubkey, true),
676 ];
677
678 if let Some(custodian_pubkey) = custodian_pubkey {
679 account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
680 }
681
682 let args = AuthorizeCheckedWithSeedArgs {
683 stake_authorize,
684 authority_seed,
685 authority_owner: *authority_owner,
686 };
687
688 Instruction::new_with_bincode(
689 ID,
690 &StakeInstruction::AuthorizeCheckedWithSeed(args),
691 account_metas,
692 )
693}
694
695#[cfg(feature = "bincode")]
696pub fn delegate_stake(
697 stake_pubkey: &Pubkey,
698 authorized_pubkey: &Pubkey,
699 vote_pubkey: &Pubkey,
700) -> Instruction {
701 let account_metas = vec![
702 AccountMeta::new(*stake_pubkey, false),
703 AccountMeta::new_readonly(*vote_pubkey, false),
704 AccountMeta::new_readonly(CLOCK_ID, false),
705 AccountMeta::new_readonly(STAKE_HISTORY_ID, false),
706 AccountMeta::new_readonly(config::ID, false),
708 AccountMeta::new_readonly(*authorized_pubkey, true),
709 ];
710 Instruction::new_with_bincode(ID, &StakeInstruction::DelegateStake, account_metas)
711}
712
713#[cfg(feature = "bincode")]
714pub fn withdraw(
715 stake_pubkey: &Pubkey,
716 withdrawer_pubkey: &Pubkey,
717 to_pubkey: &Pubkey,
718 lamports: u64,
719 custodian_pubkey: Option<&Pubkey>,
720) -> Instruction {
721 let mut account_metas = vec![
722 AccountMeta::new(*stake_pubkey, false),
723 AccountMeta::new(*to_pubkey, false),
724 AccountMeta::new_readonly(CLOCK_ID, false),
725 AccountMeta::new_readonly(STAKE_HISTORY_ID, false),
726 AccountMeta::new_readonly(*withdrawer_pubkey, true),
727 ];
728
729 if let Some(custodian_pubkey) = custodian_pubkey {
730 account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
731 }
732
733 Instruction::new_with_bincode(ID, &StakeInstruction::Withdraw(lamports), account_metas)
734}
735
736#[cfg(feature = "bincode")]
737pub fn deactivate_stake(stake_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> Instruction {
738 let account_metas = vec![
739 AccountMeta::new(*stake_pubkey, false),
740 AccountMeta::new_readonly(CLOCK_ID, false),
741 AccountMeta::new_readonly(*authorized_pubkey, true),
742 ];
743 Instruction::new_with_bincode(ID, &StakeInstruction::Deactivate, account_metas)
744}
745
746#[cfg(feature = "bincode")]
747pub fn set_lockup(
748 stake_pubkey: &Pubkey,
749 lockup: &LockupArgs,
750 custodian_pubkey: &Pubkey,
751) -> Instruction {
752 let account_metas = vec![
753 AccountMeta::new(*stake_pubkey, false),
754 AccountMeta::new_readonly(*custodian_pubkey, true),
755 ];
756 Instruction::new_with_bincode(ID, &StakeInstruction::SetLockup(*lockup), account_metas)
757}
758
759#[cfg(feature = "bincode")]
760pub fn set_lockup_checked(
761 stake_pubkey: &Pubkey,
762 lockup: &LockupArgs,
763 custodian_pubkey: &Pubkey,
764) -> Instruction {
765 let mut account_metas = vec![
766 AccountMeta::new(*stake_pubkey, false),
767 AccountMeta::new_readonly(*custodian_pubkey, true),
768 ];
769
770 let lockup_checked = LockupCheckedArgs {
771 unix_timestamp: lockup.unix_timestamp,
772 epoch: lockup.epoch,
773 };
774 if let Some(new_custodian) = lockup.custodian {
775 account_metas.push(AccountMeta::new_readonly(new_custodian, true));
776 }
777 Instruction::new_with_bincode(
778 ID,
779 &StakeInstruction::SetLockupChecked(lockup_checked),
780 account_metas,
781 )
782}
783
784#[cfg(feature = "bincode")]
785pub fn get_minimum_delegation() -> Instruction {
786 Instruction::new_with_bincode(ID, &StakeInstruction::GetMinimumDelegation, Vec::default())
787}
788
789#[cfg(feature = "bincode")]
790pub fn deactivate_delinquent_stake(
791 stake_account: &Pubkey,
792 delinquent_vote_account: &Pubkey,
793 reference_vote_account: &Pubkey,
794) -> Instruction {
795 let account_metas = vec![
796 AccountMeta::new(*stake_account, false),
797 AccountMeta::new_readonly(*delinquent_vote_account, false),
798 AccountMeta::new_readonly(*reference_vote_account, false),
799 ];
800 Instruction::new_with_bincode(ID, &StakeInstruction::DeactivateDelinquent, account_metas)
801}
802
803#[cfg(feature = "bincode")]
804fn _redelegate(
805 stake_pubkey: &Pubkey,
806 authorized_pubkey: &Pubkey,
807 vote_pubkey: &Pubkey,
808 uninitialized_stake_pubkey: &Pubkey,
809) -> Instruction {
810 let account_metas = vec![
811 AccountMeta::new(*stake_pubkey, false),
812 AccountMeta::new(*uninitialized_stake_pubkey, false),
813 AccountMeta::new_readonly(*vote_pubkey, false),
814 AccountMeta::new_readonly(config::ID, false),
816 AccountMeta::new_readonly(*authorized_pubkey, true),
817 ];
818 Instruction::new_with_bincode(ID, &StakeInstruction::Redelegate, account_metas)
819}
820
821#[cfg(feature = "bincode")]
822#[deprecated(since = "2.1.0", note = "Redelegate will not be enabled")]
823pub fn redelegate(
824 stake_pubkey: &Pubkey,
825 authorized_pubkey: &Pubkey,
826 vote_pubkey: &Pubkey,
827 uninitialized_stake_pubkey: &Pubkey,
828) -> Vec<Instruction> {
829 vec![
830 solana_system_interface::instruction::allocate(
831 uninitialized_stake_pubkey,
832 StakeStateV2::size_of() as u64,
833 ),
834 solana_system_interface::instruction::assign(uninitialized_stake_pubkey, &ID),
835 _redelegate(
836 stake_pubkey,
837 authorized_pubkey,
838 vote_pubkey,
839 uninitialized_stake_pubkey,
840 ),
841 ]
842}
843
844#[cfg(feature = "bincode")]
845#[deprecated(since = "2.1.0", note = "Redelegate will not be enabled")]
846pub fn redelegate_with_seed(
847 stake_pubkey: &Pubkey,
848 authorized_pubkey: &Pubkey,
849 vote_pubkey: &Pubkey,
850 uninitialized_stake_pubkey: &Pubkey, base: &Pubkey, seed: &str, ) -> Vec<Instruction> {
854 vec![
855 solana_system_interface::instruction::allocate_with_seed(
856 uninitialized_stake_pubkey,
857 base,
858 seed,
859 StakeStateV2::size_of() as u64,
860 &ID,
861 ),
862 _redelegate(
863 stake_pubkey,
864 authorized_pubkey,
865 vote_pubkey,
866 uninitialized_stake_pubkey,
867 ),
868 ]
869}
870
871#[cfg(feature = "bincode")]
872pub fn move_stake(
873 source_stake_pubkey: &Pubkey,
874 destination_stake_pubkey: &Pubkey,
875 authorized_pubkey: &Pubkey,
876 lamports: u64,
877) -> Instruction {
878 let account_metas = vec![
879 AccountMeta::new(*source_stake_pubkey, false),
880 AccountMeta::new(*destination_stake_pubkey, false),
881 AccountMeta::new_readonly(*authorized_pubkey, true),
882 ];
883
884 Instruction::new_with_bincode(ID, &StakeInstruction::MoveStake(lamports), account_metas)
885}
886
887#[cfg(feature = "bincode")]
888pub fn move_lamports(
889 source_stake_pubkey: &Pubkey,
890 destination_stake_pubkey: &Pubkey,
891 authorized_pubkey: &Pubkey,
892 lamports: u64,
893) -> Instruction {
894 let account_metas = vec![
895 AccountMeta::new(*source_stake_pubkey, false),
896 AccountMeta::new(*destination_stake_pubkey, false),
897 AccountMeta::new_readonly(*authorized_pubkey, true),
898 ];
899
900 Instruction::new_with_bincode(ID, &StakeInstruction::MoveLamports(lamports), account_metas)
901}
902
903#[cfg(feature = "bincode")]
904#[cfg(test)]
905mod tests {
906 use super::*;
907
908 #[allow(deprecated)]
909 #[test]
910 fn test_constants() {
911 assert_eq!(CLOCK_ID, solana_sdk_ids::sysvar::clock::ID);
913
914 assert_eq!(STAKE_HISTORY_ID, solana_sdk_ids::sysvar::stake_history::ID);
916
917 assert_eq!(RENT_ID, solana_sdk_ids::sysvar::rent::ID);
919 }
920}