1use crate::{check_program_account, error::TokenError};
4use solana_program::{
5 instruction::{AccountMeta, Instruction},
6 program_error::ProgramError,
7 program_option::COption,
8 pubkey::Pubkey,
9 sysvar,
10};
11use std::convert::TryInto;
12use std::mem::size_of;
13
14pub const MIN_SIGNERS: usize = 1;
16pub const MAX_SIGNERS: usize = 11;
18const U64_BYTES: usize = 8;
20
21#[repr(C)]
23#[derive(Clone, Debug, PartialEq)]
24pub enum TokenInstruction<'a> {
25 InitializeMint {
40 decimals: u8,
42 mint_authority: Pubkey,
44 freeze_authority: COption<Pubkey>,
46 },
47 InitializeAccount,
66 InitializeMultisig {
86 m: u8,
89 },
90 Transfer {
108 amount: u64,
110 },
111 Approve {
127 amount: u64,
129 },
130 Revoke,
143 SetAuthority {
156 authority_type: AuthorityType,
158 new_authority: COption<Pubkey>,
160 },
161 MintTo {
177 amount: u64,
179 },
180 Burn {
196 amount: u64,
198 },
199 CloseAccount,
215 FreezeAccount,
231 ThawAccount,
246
247 TransferChecked {
271 amount: u64,
273 decimals: u8,
275 },
276 ApproveChecked {
298 amount: u64,
300 decimals: u8,
302 },
303 MintToChecked {
323 amount: u64,
325 decimals: u8,
327 },
328 BurnChecked {
349 amount: u64,
351 decimals: u8,
353 },
354 InitializeAccount2 {
365 owner: Pubkey,
367 },
368 SyncNative,
378 InitializeAccount3 {
385 owner: Pubkey,
387 },
388 InitializeMultisig2 {
396 m: u8,
399 },
400 InitializeMint2 {
407 decimals: u8,
409 mint_authority: Pubkey,
411 freeze_authority: COption<Pubkey>,
413 },
414 GetAccountDataSize, InitializeImmutableOwner,
439 AmountToUiAmount {
451 amount: u64,
453 },
454 UiAmountToAmount {
464 ui_amount: &'a str,
466 },
467 }
471impl<'a> TokenInstruction<'a> {
472 pub fn unpack(input: &'a [u8]) -> Result<Self, ProgramError> {
474 use TokenError::InvalidInstruction;
475
476 let (&tag, rest) = input.split_first().ok_or(InvalidInstruction)?;
477 Ok(match tag {
478 0 => {
479 let (&decimals, rest) = rest.split_first().ok_or(InvalidInstruction)?;
480 let (mint_authority, rest) = Self::unpack_pubkey(rest)?;
481 let (freeze_authority, _rest) = Self::unpack_pubkey_option(rest)?;
482 Self::InitializeMint {
483 mint_authority,
484 freeze_authority,
485 decimals,
486 }
487 }
488 1 => Self::InitializeAccount,
489 2 => {
490 let &m = rest.get(0).ok_or(InvalidInstruction)?;
491 Self::InitializeMultisig { m }
492 }
493 3 | 4 | 7 | 8 => {
494 let amount = rest
495 .get(..8)
496 .and_then(|slice| slice.try_into().ok())
497 .map(u64::from_le_bytes)
498 .ok_or(InvalidInstruction)?;
499 match tag {
500 3 => Self::Transfer { amount },
501 4 => Self::Approve { amount },
502 7 => Self::MintTo { amount },
503 8 => Self::Burn { amount },
504 _ => unreachable!(),
505 }
506 }
507 5 => Self::Revoke,
508 6 => {
509 let (authority_type, rest) = rest
510 .split_first()
511 .ok_or_else(|| ProgramError::from(InvalidInstruction))
512 .and_then(|(&t, rest)| Ok((AuthorityType::from(t)?, rest)))?;
513 let (new_authority, _rest) = Self::unpack_pubkey_option(rest)?;
514
515 Self::SetAuthority {
516 authority_type,
517 new_authority,
518 }
519 }
520 9 => Self::CloseAccount,
521 10 => Self::FreezeAccount,
522 11 => Self::ThawAccount,
523 12 => {
524 let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
525 Self::TransferChecked { amount, decimals }
526 }
527 13 => {
528 let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
529 Self::ApproveChecked { amount, decimals }
530 }
531 14 => {
532 let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
533 Self::MintToChecked { amount, decimals }
534 }
535 15 => {
536 let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
537 Self::BurnChecked { amount, decimals }
538 }
539 16 => {
540 let (owner, _rest) = Self::unpack_pubkey(rest)?;
541 Self::InitializeAccount2 { owner }
542 }
543 17 => Self::SyncNative,
544 18 => {
545 let (owner, _rest) = Self::unpack_pubkey(rest)?;
546 Self::InitializeAccount3 { owner }
547 }
548 19 => {
549 let &m = rest.get(0).ok_or(InvalidInstruction)?;
550 Self::InitializeMultisig2 { m }
551 }
552 20 => {
553 let (&decimals, rest) = rest.split_first().ok_or(InvalidInstruction)?;
554 let (mint_authority, rest) = Self::unpack_pubkey(rest)?;
555 let (freeze_authority, _rest) = Self::unpack_pubkey_option(rest)?;
556 Self::InitializeMint2 {
557 mint_authority,
558 freeze_authority,
559 decimals,
560 }
561 }
562 21 => Self::GetAccountDataSize,
563 22 => Self::InitializeImmutableOwner,
564 23 => {
565 let (amount, _rest) = Self::unpack_u64(rest)?;
566 Self::AmountToUiAmount { amount }
567 }
568 24 => {
569 let ui_amount = std::str::from_utf8(rest).map_err(|_| InvalidInstruction)?;
570 Self::UiAmountToAmount { ui_amount }
571 }
572 _ => return Err(TokenError::InvalidInstruction.into()),
573 })
574 }
575
576 pub fn pack(&self) -> Vec<u8> {
578 let mut buf = Vec::with_capacity(size_of::<Self>());
579 match self {
580 &Self::InitializeMint {
581 ref mint_authority,
582 ref freeze_authority,
583 decimals,
584 } => {
585 buf.push(0);
586 buf.push(decimals);
587 buf.extend_from_slice(mint_authority.as_ref());
588 Self::pack_pubkey_option(freeze_authority, &mut buf);
589 }
590 Self::InitializeAccount => buf.push(1),
591 &Self::InitializeMultisig { m } => {
592 buf.push(2);
593 buf.push(m);
594 }
595 &Self::Transfer { amount } => {
596 buf.push(3);
597 buf.extend_from_slice(&amount.to_le_bytes());
598 }
599 &Self::Approve { amount } => {
600 buf.push(4);
601 buf.extend_from_slice(&amount.to_le_bytes());
602 }
603 &Self::MintTo { amount } => {
604 buf.push(7);
605 buf.extend_from_slice(&amount.to_le_bytes());
606 }
607 &Self::Burn { amount } => {
608 buf.push(8);
609 buf.extend_from_slice(&amount.to_le_bytes());
610 }
611 Self::Revoke => buf.push(5),
612 Self::SetAuthority {
613 authority_type,
614 ref new_authority,
615 } => {
616 buf.push(6);
617 buf.push(authority_type.into());
618 Self::pack_pubkey_option(new_authority, &mut buf);
619 }
620 Self::CloseAccount => buf.push(9),
621 Self::FreezeAccount => buf.push(10),
622 Self::ThawAccount => buf.push(11),
623 &Self::TransferChecked { amount, decimals } => {
624 buf.push(12);
625 buf.extend_from_slice(&amount.to_le_bytes());
626 buf.push(decimals);
627 }
628 &Self::ApproveChecked { amount, decimals } => {
629 buf.push(13);
630 buf.extend_from_slice(&amount.to_le_bytes());
631 buf.push(decimals);
632 }
633 &Self::MintToChecked { amount, decimals } => {
634 buf.push(14);
635 buf.extend_from_slice(&amount.to_le_bytes());
636 buf.push(decimals);
637 }
638 &Self::BurnChecked { amount, decimals } => {
639 buf.push(15);
640 buf.extend_from_slice(&amount.to_le_bytes());
641 buf.push(decimals);
642 }
643 &Self::InitializeAccount2 { owner } => {
644 buf.push(16);
645 buf.extend_from_slice(owner.as_ref());
646 }
647 &Self::SyncNative => {
648 buf.push(17);
649 }
650 &Self::InitializeAccount3 { owner } => {
651 buf.push(18);
652 buf.extend_from_slice(owner.as_ref());
653 }
654 &Self::InitializeMultisig2 { m } => {
655 buf.push(19);
656 buf.push(m);
657 }
658 &Self::InitializeMint2 {
659 ref mint_authority,
660 ref freeze_authority,
661 decimals,
662 } => {
663 buf.push(20);
664 buf.push(decimals);
665 buf.extend_from_slice(mint_authority.as_ref());
666 Self::pack_pubkey_option(freeze_authority, &mut buf);
667 }
668 &Self::GetAccountDataSize => {
669 buf.push(21);
670 }
671 &Self::InitializeImmutableOwner => {
672 buf.push(22);
673 }
674 &Self::AmountToUiAmount { amount } => {
675 buf.push(23);
676 buf.extend_from_slice(&amount.to_le_bytes());
677 }
678 Self::UiAmountToAmount { ui_amount } => {
679 buf.push(24);
680 buf.extend_from_slice(ui_amount.as_bytes());
681 }
682 };
683 buf
684 }
685
686 fn unpack_pubkey(input: &[u8]) -> Result<(Pubkey, &[u8]), ProgramError> {
687 if input.len() >= 32 {
688 let (key, rest) = input.split_at(32);
689 let pk = Pubkey::new(key);
690 Ok((pk, rest))
691 } else {
692 Err(TokenError::InvalidInstruction.into())
693 }
694 }
695
696 fn unpack_pubkey_option(input: &[u8]) -> Result<(COption<Pubkey>, &[u8]), ProgramError> {
697 match input.split_first() {
698 Option::Some((&0, rest)) => Ok((COption::None, rest)),
699 Option::Some((&1, rest)) if rest.len() >= 32 => {
700 let (key, rest) = rest.split_at(32);
701 let pk = Pubkey::new(key);
702 Ok((COption::Some(pk), rest))
703 }
704 _ => Err(TokenError::InvalidInstruction.into()),
705 }
706 }
707
708 fn pack_pubkey_option(value: &COption<Pubkey>, buf: &mut Vec<u8>) {
709 match *value {
710 COption::Some(ref key) => {
711 buf.push(1);
712 buf.extend_from_slice(&key.to_bytes());
713 }
714 COption::None => buf.push(0),
715 }
716 }
717
718 fn unpack_u64(input: &[u8]) -> Result<(u64, &[u8]), ProgramError> {
719 let value = input
720 .get(..U64_BYTES)
721 .and_then(|slice| slice.try_into().ok())
722 .map(u64::from_le_bytes)
723 .ok_or(TokenError::InvalidInstruction)?;
724 Ok((value, &input[U64_BYTES..]))
725 }
726
727 fn unpack_amount_decimals(input: &[u8]) -> Result<(u64, u8, &[u8]), ProgramError> {
728 let (amount, rest) = Self::unpack_u64(input)?;
729 let (&decimals, rest) = rest.split_first().ok_or(TokenError::InvalidInstruction)?;
730 Ok((amount, decimals, rest))
731 }
732}
733
734#[repr(u8)]
736#[derive(Clone, Debug, PartialEq)]
737pub enum AuthorityType {
738 MintTokens,
740 FreezeAccount,
742 AccountOwner,
744 CloseAccount,
746}
747
748impl AuthorityType {
749 fn into(&self) -> u8 {
750 match self {
751 AuthorityType::MintTokens => 0,
752 AuthorityType::FreezeAccount => 1,
753 AuthorityType::AccountOwner => 2,
754 AuthorityType::CloseAccount => 3,
755 }
756 }
757
758 fn from(index: u8) -> Result<Self, ProgramError> {
759 match index {
760 0 => Ok(AuthorityType::MintTokens),
761 1 => Ok(AuthorityType::FreezeAccount),
762 2 => Ok(AuthorityType::AccountOwner),
763 3 => Ok(AuthorityType::CloseAccount),
764 _ => Err(TokenError::InvalidInstruction.into()),
765 }
766 }
767}
768
769pub fn initialize_mint(
771 token_program_id: &Pubkey,
772 mint_pubkey: &Pubkey,
773 mint_authority_pubkey: &Pubkey,
774 freeze_authority_pubkey: Option<&Pubkey>,
775 decimals: u8,
776) -> Result<Instruction, ProgramError> {
777 check_program_account(token_program_id)?;
778 let freeze_authority = freeze_authority_pubkey.cloned().into();
779 let data = TokenInstruction::InitializeMint {
780 mint_authority: *mint_authority_pubkey,
781 freeze_authority,
782 decimals,
783 }
784 .pack();
785
786 let accounts = vec![
787 AccountMeta::new(*mint_pubkey, false),
788 AccountMeta::new_readonly(sysvar::rent::id(), false),
789 ];
790
791 Ok(Instruction {
792 program_id: *token_program_id,
793 accounts,
794 data,
795 })
796}
797
798pub fn initialize_mint2(
800 token_program_id: &Pubkey,
801 mint_pubkey: &Pubkey,
802 mint_authority_pubkey: &Pubkey,
803 freeze_authority_pubkey: Option<&Pubkey>,
804 decimals: u8,
805) -> Result<Instruction, ProgramError> {
806 check_program_account(token_program_id)?;
807 let freeze_authority = freeze_authority_pubkey.cloned().into();
808 let data = TokenInstruction::InitializeMint2 {
809 mint_authority: *mint_authority_pubkey,
810 freeze_authority,
811 decimals,
812 }
813 .pack();
814
815 let accounts = vec![AccountMeta::new(*mint_pubkey, false)];
816
817 Ok(Instruction {
818 program_id: *token_program_id,
819 accounts,
820 data,
821 })
822}
823
824pub fn initialize_account(
826 token_program_id: &Pubkey,
827 account_pubkey: &Pubkey,
828 mint_pubkey: &Pubkey,
829 owner_pubkey: &Pubkey,
830) -> Result<Instruction, ProgramError> {
831 check_program_account(token_program_id)?;
832 let data = TokenInstruction::InitializeAccount.pack();
833
834 let accounts = vec![
835 AccountMeta::new(*account_pubkey, false),
836 AccountMeta::new_readonly(*mint_pubkey, false),
837 AccountMeta::new_readonly(*owner_pubkey, false),
838 AccountMeta::new_readonly(sysvar::rent::id(), false),
839 ];
840
841 Ok(Instruction {
842 program_id: *token_program_id,
843 accounts,
844 data,
845 })
846}
847
848pub fn initialize_account2(
850 token_program_id: &Pubkey,
851 account_pubkey: &Pubkey,
852 mint_pubkey: &Pubkey,
853 owner_pubkey: &Pubkey,
854) -> Result<Instruction, ProgramError> {
855 check_program_account(token_program_id)?;
856 let data = TokenInstruction::InitializeAccount2 {
857 owner: *owner_pubkey,
858 }
859 .pack();
860
861 let accounts = vec![
862 AccountMeta::new(*account_pubkey, false),
863 AccountMeta::new_readonly(*mint_pubkey, false),
864 AccountMeta::new_readonly(sysvar::rent::id(), false),
865 ];
866
867 Ok(Instruction {
868 program_id: *token_program_id,
869 accounts,
870 data,
871 })
872}
873
874pub fn initialize_account3(
876 token_program_id: &Pubkey,
877 account_pubkey: &Pubkey,
878 mint_pubkey: &Pubkey,
879 owner_pubkey: &Pubkey,
880) -> Result<Instruction, ProgramError> {
881 check_program_account(token_program_id)?;
882 let data = TokenInstruction::InitializeAccount3 {
883 owner: *owner_pubkey,
884 }
885 .pack();
886
887 let accounts = vec![
888 AccountMeta::new(*account_pubkey, false),
889 AccountMeta::new_readonly(*mint_pubkey, false),
890 ];
891
892 Ok(Instruction {
893 program_id: *token_program_id,
894 accounts,
895 data,
896 })
897}
898
899pub fn initialize_multisig(
901 token_program_id: &Pubkey,
902 multisig_pubkey: &Pubkey,
903 signer_pubkeys: &[&Pubkey],
904 m: u8,
905) -> Result<Instruction, ProgramError> {
906 check_program_account(token_program_id)?;
907 if !is_valid_signer_index(m as usize)
908 || !is_valid_signer_index(signer_pubkeys.len())
909 || m as usize > signer_pubkeys.len()
910 {
911 return Err(ProgramError::MissingRequiredSignature);
912 }
913 let data = TokenInstruction::InitializeMultisig { m }.pack();
914
915 let mut accounts = Vec::with_capacity(1 + 1 + signer_pubkeys.len());
916 accounts.push(AccountMeta::new(*multisig_pubkey, false));
917 accounts.push(AccountMeta::new_readonly(sysvar::rent::id(), false));
918 for signer_pubkey in signer_pubkeys.iter() {
919 accounts.push(AccountMeta::new_readonly(**signer_pubkey, false));
920 }
921
922 Ok(Instruction {
923 program_id: *token_program_id,
924 accounts,
925 data,
926 })
927}
928
929pub fn initialize_multisig2(
931 token_program_id: &Pubkey,
932 multisig_pubkey: &Pubkey,
933 signer_pubkeys: &[&Pubkey],
934 m: u8,
935) -> Result<Instruction, ProgramError> {
936 check_program_account(token_program_id)?;
937 if !is_valid_signer_index(m as usize)
938 || !is_valid_signer_index(signer_pubkeys.len())
939 || m as usize > signer_pubkeys.len()
940 {
941 return Err(ProgramError::MissingRequiredSignature);
942 }
943 let data = TokenInstruction::InitializeMultisig2 { m }.pack();
944
945 let mut accounts = Vec::with_capacity(1 + 1 + signer_pubkeys.len());
946 accounts.push(AccountMeta::new(*multisig_pubkey, false));
947 for signer_pubkey in signer_pubkeys.iter() {
948 accounts.push(AccountMeta::new_readonly(**signer_pubkey, false));
949 }
950
951 Ok(Instruction {
952 program_id: *token_program_id,
953 accounts,
954 data,
955 })
956}
957
958pub fn transfer(
960 token_program_id: &Pubkey,
961 source_pubkey: &Pubkey,
962 destination_pubkey: &Pubkey,
963 authority_pubkey: &Pubkey,
964 signer_pubkeys: &[&Pubkey],
965 amount: u64,
966) -> Result<Instruction, ProgramError> {
967 check_program_account(token_program_id)?;
968 let data = TokenInstruction::Transfer { amount }.pack();
969
970 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
971 accounts.push(AccountMeta::new(*source_pubkey, false));
972 accounts.push(AccountMeta::new(*destination_pubkey, false));
973 accounts.push(AccountMeta::new_readonly(
974 *authority_pubkey,
975 signer_pubkeys.is_empty(),
976 ));
977 for signer_pubkey in signer_pubkeys.iter() {
978 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
979 }
980
981 Ok(Instruction {
982 program_id: *token_program_id,
983 accounts,
984 data,
985 })
986}
987
988pub fn approve(
990 token_program_id: &Pubkey,
991 source_pubkey: &Pubkey,
992 delegate_pubkey: &Pubkey,
993 owner_pubkey: &Pubkey,
994 signer_pubkeys: &[&Pubkey],
995 amount: u64,
996) -> Result<Instruction, ProgramError> {
997 check_program_account(token_program_id)?;
998 let data = TokenInstruction::Approve { amount }.pack();
999
1000 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1001 accounts.push(AccountMeta::new(*source_pubkey, false));
1002 accounts.push(AccountMeta::new_readonly(*delegate_pubkey, false));
1003 accounts.push(AccountMeta::new_readonly(
1004 *owner_pubkey,
1005 signer_pubkeys.is_empty(),
1006 ));
1007 for signer_pubkey in signer_pubkeys.iter() {
1008 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1009 }
1010
1011 Ok(Instruction {
1012 program_id: *token_program_id,
1013 accounts,
1014 data,
1015 })
1016}
1017
1018pub fn revoke(
1020 token_program_id: &Pubkey,
1021 source_pubkey: &Pubkey,
1022 owner_pubkey: &Pubkey,
1023 signer_pubkeys: &[&Pubkey],
1024) -> Result<Instruction, ProgramError> {
1025 check_program_account(token_program_id)?;
1026 let data = TokenInstruction::Revoke.pack();
1027
1028 let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len());
1029 accounts.push(AccountMeta::new(*source_pubkey, false));
1030 accounts.push(AccountMeta::new_readonly(
1031 *owner_pubkey,
1032 signer_pubkeys.is_empty(),
1033 ));
1034 for signer_pubkey in signer_pubkeys.iter() {
1035 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1036 }
1037
1038 Ok(Instruction {
1039 program_id: *token_program_id,
1040 accounts,
1041 data,
1042 })
1043}
1044
1045pub fn set_authority(
1047 token_program_id: &Pubkey,
1048 owned_pubkey: &Pubkey,
1049 new_authority_pubkey: Option<&Pubkey>,
1050 authority_type: AuthorityType,
1051 owner_pubkey: &Pubkey,
1052 signer_pubkeys: &[&Pubkey],
1053) -> Result<Instruction, ProgramError> {
1054 check_program_account(token_program_id)?;
1055 let new_authority = new_authority_pubkey.cloned().into();
1056 let data = TokenInstruction::SetAuthority {
1057 authority_type,
1058 new_authority,
1059 }
1060 .pack();
1061
1062 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1063 accounts.push(AccountMeta::new(*owned_pubkey, false));
1064 accounts.push(AccountMeta::new_readonly(
1065 *owner_pubkey,
1066 signer_pubkeys.is_empty(),
1067 ));
1068 for signer_pubkey in signer_pubkeys.iter() {
1069 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1070 }
1071
1072 Ok(Instruction {
1073 program_id: *token_program_id,
1074 accounts,
1075 data,
1076 })
1077}
1078
1079pub fn mint_to(
1081 token_program_id: &Pubkey,
1082 mint_pubkey: &Pubkey,
1083 account_pubkey: &Pubkey,
1084 owner_pubkey: &Pubkey,
1085 signer_pubkeys: &[&Pubkey],
1086 amount: u64,
1087) -> Result<Instruction, ProgramError> {
1088 check_program_account(token_program_id)?;
1089 let data = TokenInstruction::MintTo { amount }.pack();
1090
1091 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1092 accounts.push(AccountMeta::new(*mint_pubkey, false));
1093 accounts.push(AccountMeta::new(*account_pubkey, false));
1094 accounts.push(AccountMeta::new_readonly(
1095 *owner_pubkey,
1096 signer_pubkeys.is_empty(),
1097 ));
1098 for signer_pubkey in signer_pubkeys.iter() {
1099 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1100 }
1101
1102 Ok(Instruction {
1103 program_id: *token_program_id,
1104 accounts,
1105 data,
1106 })
1107}
1108
1109pub fn burn(
1111 token_program_id: &Pubkey,
1112 account_pubkey: &Pubkey,
1113 mint_pubkey: &Pubkey,
1114 authority_pubkey: &Pubkey,
1115 signer_pubkeys: &[&Pubkey],
1116 amount: u64,
1117) -> Result<Instruction, ProgramError> {
1118 check_program_account(token_program_id)?;
1119 let data = TokenInstruction::Burn { amount }.pack();
1120
1121 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1122 accounts.push(AccountMeta::new(*account_pubkey, false));
1123 accounts.push(AccountMeta::new(*mint_pubkey, false));
1124 accounts.push(AccountMeta::new_readonly(
1125 *authority_pubkey,
1126 signer_pubkeys.is_empty(),
1127 ));
1128 for signer_pubkey in signer_pubkeys.iter() {
1129 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1130 }
1131
1132 Ok(Instruction {
1133 program_id: *token_program_id,
1134 accounts,
1135 data,
1136 })
1137}
1138
1139pub fn close_account(
1141 token_program_id: &Pubkey,
1142 account_pubkey: &Pubkey,
1143 destination_pubkey: &Pubkey,
1144 owner_pubkey: &Pubkey,
1145 signer_pubkeys: &[&Pubkey],
1146) -> Result<Instruction, ProgramError> {
1147 check_program_account(token_program_id)?;
1148 let data = TokenInstruction::CloseAccount.pack();
1149
1150 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1151 accounts.push(AccountMeta::new(*account_pubkey, false));
1152 accounts.push(AccountMeta::new(*destination_pubkey, false));
1153 accounts.push(AccountMeta::new_readonly(
1154 *owner_pubkey,
1155 signer_pubkeys.is_empty(),
1156 ));
1157 for signer_pubkey in signer_pubkeys.iter() {
1158 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1159 }
1160
1161 Ok(Instruction {
1162 program_id: *token_program_id,
1163 accounts,
1164 data,
1165 })
1166}
1167
1168pub fn freeze_account(
1170 token_program_id: &Pubkey,
1171 account_pubkey: &Pubkey,
1172 mint_pubkey: &Pubkey,
1173 owner_pubkey: &Pubkey,
1174 signer_pubkeys: &[&Pubkey],
1175) -> Result<Instruction, ProgramError> {
1176 check_program_account(token_program_id)?;
1177 let data = TokenInstruction::FreezeAccount.pack();
1178
1179 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1180 accounts.push(AccountMeta::new(*account_pubkey, false));
1181 accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
1182 accounts.push(AccountMeta::new_readonly(
1183 *owner_pubkey,
1184 signer_pubkeys.is_empty(),
1185 ));
1186 for signer_pubkey in signer_pubkeys.iter() {
1187 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1188 }
1189
1190 Ok(Instruction {
1191 program_id: *token_program_id,
1192 accounts,
1193 data,
1194 })
1195}
1196
1197pub fn thaw_account(
1199 token_program_id: &Pubkey,
1200 account_pubkey: &Pubkey,
1201 mint_pubkey: &Pubkey,
1202 owner_pubkey: &Pubkey,
1203 signer_pubkeys: &[&Pubkey],
1204) -> Result<Instruction, ProgramError> {
1205 check_program_account(token_program_id)?;
1206 let data = TokenInstruction::ThawAccount.pack();
1207
1208 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1209 accounts.push(AccountMeta::new(*account_pubkey, false));
1210 accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
1211 accounts.push(AccountMeta::new_readonly(
1212 *owner_pubkey,
1213 signer_pubkeys.is_empty(),
1214 ));
1215 for signer_pubkey in signer_pubkeys.iter() {
1216 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1217 }
1218
1219 Ok(Instruction {
1220 program_id: *token_program_id,
1221 accounts,
1222 data,
1223 })
1224}
1225
1226#[allow(clippy::too_many_arguments)]
1228pub fn transfer_checked(
1229 token_program_id: &Pubkey,
1230 source_pubkey: &Pubkey,
1231 mint_pubkey: &Pubkey,
1232 destination_pubkey: &Pubkey,
1233 authority_pubkey: &Pubkey,
1234 signer_pubkeys: &[&Pubkey],
1235 amount: u64,
1236 decimals: u8,
1237) -> Result<Instruction, ProgramError> {
1238 check_program_account(token_program_id)?;
1239 let data = TokenInstruction::TransferChecked { amount, decimals }.pack();
1240
1241 let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len());
1242 accounts.push(AccountMeta::new(*source_pubkey, false));
1243 accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
1244 accounts.push(AccountMeta::new(*destination_pubkey, false));
1245 accounts.push(AccountMeta::new_readonly(
1246 *authority_pubkey,
1247 signer_pubkeys.is_empty(),
1248 ));
1249 for signer_pubkey in signer_pubkeys.iter() {
1250 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1251 }
1252
1253 Ok(Instruction {
1254 program_id: *token_program_id,
1255 accounts,
1256 data,
1257 })
1258}
1259
1260#[allow(clippy::too_many_arguments)]
1262pub fn approve_checked(
1263 token_program_id: &Pubkey,
1264 source_pubkey: &Pubkey,
1265 mint_pubkey: &Pubkey,
1266 delegate_pubkey: &Pubkey,
1267 owner_pubkey: &Pubkey,
1268 signer_pubkeys: &[&Pubkey],
1269 amount: u64,
1270 decimals: u8,
1271) -> Result<Instruction, ProgramError> {
1272 check_program_account(token_program_id)?;
1273 let data = TokenInstruction::ApproveChecked { amount, decimals }.pack();
1274
1275 let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len());
1276 accounts.push(AccountMeta::new(*source_pubkey, false));
1277 accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
1278 accounts.push(AccountMeta::new_readonly(*delegate_pubkey, false));
1279 accounts.push(AccountMeta::new_readonly(
1280 *owner_pubkey,
1281 signer_pubkeys.is_empty(),
1282 ));
1283 for signer_pubkey in signer_pubkeys.iter() {
1284 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1285 }
1286
1287 Ok(Instruction {
1288 program_id: *token_program_id,
1289 accounts,
1290 data,
1291 })
1292}
1293
1294pub fn mint_to_checked(
1296 token_program_id: &Pubkey,
1297 mint_pubkey: &Pubkey,
1298 account_pubkey: &Pubkey,
1299 owner_pubkey: &Pubkey,
1300 signer_pubkeys: &[&Pubkey],
1301 amount: u64,
1302 decimals: u8,
1303) -> Result<Instruction, ProgramError> {
1304 check_program_account(token_program_id)?;
1305 let data = TokenInstruction::MintToChecked { amount, decimals }.pack();
1306
1307 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1308 accounts.push(AccountMeta::new(*mint_pubkey, false));
1309 accounts.push(AccountMeta::new(*account_pubkey, false));
1310 accounts.push(AccountMeta::new_readonly(
1311 *owner_pubkey,
1312 signer_pubkeys.is_empty(),
1313 ));
1314 for signer_pubkey in signer_pubkeys.iter() {
1315 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1316 }
1317
1318 Ok(Instruction {
1319 program_id: *token_program_id,
1320 accounts,
1321 data,
1322 })
1323}
1324
1325pub fn burn_checked(
1327 token_program_id: &Pubkey,
1328 account_pubkey: &Pubkey,
1329 mint_pubkey: &Pubkey,
1330 authority_pubkey: &Pubkey,
1331 signer_pubkeys: &[&Pubkey],
1332 amount: u64,
1333 decimals: u8,
1334) -> Result<Instruction, ProgramError> {
1335 check_program_account(token_program_id)?;
1336 let data = TokenInstruction::BurnChecked { amount, decimals }.pack();
1337
1338 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1339 accounts.push(AccountMeta::new(*account_pubkey, false));
1340 accounts.push(AccountMeta::new(*mint_pubkey, false));
1341 accounts.push(AccountMeta::new_readonly(
1342 *authority_pubkey,
1343 signer_pubkeys.is_empty(),
1344 ));
1345 for signer_pubkey in signer_pubkeys.iter() {
1346 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1347 }
1348
1349 Ok(Instruction {
1350 program_id: *token_program_id,
1351 accounts,
1352 data,
1353 })
1354}
1355
1356pub fn sync_native(
1358 token_program_id: &Pubkey,
1359 account_pubkey: &Pubkey,
1360) -> Result<Instruction, ProgramError> {
1361 check_program_account(token_program_id)?;
1362
1363 Ok(Instruction {
1364 program_id: *token_program_id,
1365 accounts: vec![AccountMeta::new(*account_pubkey, false)],
1366 data: TokenInstruction::SyncNative.pack(),
1367 })
1368}
1369
1370pub fn get_account_data_size(
1372 token_program_id: &Pubkey,
1373 mint_pubkey: &Pubkey,
1374) -> Result<Instruction, ProgramError> {
1375 check_program_account(token_program_id)?;
1376
1377 Ok(Instruction {
1378 program_id: *token_program_id,
1379 accounts: vec![AccountMeta::new_readonly(*mint_pubkey, false)],
1380 data: TokenInstruction::GetAccountDataSize.pack(),
1381 })
1382}
1383
1384pub fn initialize_immutable_owner(
1386 token_program_id: &Pubkey,
1387 account_pubkey: &Pubkey,
1388) -> Result<Instruction, ProgramError> {
1389 check_program_account(token_program_id)?;
1390 Ok(Instruction {
1391 program_id: *token_program_id,
1392 accounts: vec![AccountMeta::new(*account_pubkey, false)],
1393 data: TokenInstruction::InitializeImmutableOwner.pack(),
1394 })
1395}
1396
1397pub fn amount_to_ui_amount(
1399 token_program_id: &Pubkey,
1400 mint_pubkey: &Pubkey,
1401 amount: u64,
1402) -> Result<Instruction, ProgramError> {
1403 check_program_account(token_program_id)?;
1404
1405 Ok(Instruction {
1406 program_id: *token_program_id,
1407 accounts: vec![AccountMeta::new_readonly(*mint_pubkey, false)],
1408 data: TokenInstruction::AmountToUiAmount { amount }.pack(),
1409 })
1410}
1411
1412pub fn ui_amount_to_amount(
1414 token_program_id: &Pubkey,
1415 mint_pubkey: &Pubkey,
1416 ui_amount: &str,
1417) -> Result<Instruction, ProgramError> {
1418 check_program_account(token_program_id)?;
1419
1420 Ok(Instruction {
1421 program_id: *token_program_id,
1422 accounts: vec![AccountMeta::new_readonly(*mint_pubkey, false)],
1423 data: TokenInstruction::UiAmountToAmount { ui_amount }.pack(),
1424 })
1425}
1426
1427pub fn is_valid_signer_index(index: usize) -> bool {
1429 (MIN_SIGNERS..=MAX_SIGNERS).contains(&index)
1430}
1431
1432#[cfg(test)]
1433mod test {
1434 use {super::*, proptest::prelude::*};
1435
1436 #[test]
1437 fn test_instruction_packing() {
1438 let check = TokenInstruction::InitializeMint {
1439 decimals: 2,
1440 mint_authority: Pubkey::new(&[1u8; 32]),
1441 freeze_authority: COption::None,
1442 };
1443 let packed = check.pack();
1444 let mut expect = Vec::from([0u8, 2]);
1445 expect.extend_from_slice(&[1u8; 32]);
1446 expect.extend_from_slice(&[0]);
1447 assert_eq!(packed, expect);
1448 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1449 assert_eq!(unpacked, check);
1450
1451 let check = TokenInstruction::InitializeMint {
1452 decimals: 2,
1453 mint_authority: Pubkey::new(&[2u8; 32]),
1454 freeze_authority: COption::Some(Pubkey::new(&[3u8; 32])),
1455 };
1456 let packed = check.pack();
1457 let mut expect = vec![0u8, 2];
1458 expect.extend_from_slice(&[2u8; 32]);
1459 expect.extend_from_slice(&[1]);
1460 expect.extend_from_slice(&[3u8; 32]);
1461 assert_eq!(packed, expect);
1462 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1463 assert_eq!(unpacked, check);
1464
1465 let check = TokenInstruction::InitializeAccount;
1466 let packed = check.pack();
1467 let expect = Vec::from([1u8]);
1468 assert_eq!(packed, expect);
1469 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1470 assert_eq!(unpacked, check);
1471
1472 let check = TokenInstruction::InitializeMultisig { m: 1 };
1473 let packed = check.pack();
1474 let expect = Vec::from([2u8, 1]);
1475 assert_eq!(packed, expect);
1476 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1477 assert_eq!(unpacked, check);
1478
1479 let check = TokenInstruction::Transfer { amount: 1 };
1480 let packed = check.pack();
1481 let expect = Vec::from([3u8, 1, 0, 0, 0, 0, 0, 0, 0]);
1482 assert_eq!(packed, expect);
1483 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1484 assert_eq!(unpacked, check);
1485
1486 let check = TokenInstruction::Approve { amount: 1 };
1487 let packed = check.pack();
1488 let expect = Vec::from([4u8, 1, 0, 0, 0, 0, 0, 0, 0]);
1489 assert_eq!(packed, expect);
1490 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1491 assert_eq!(unpacked, check);
1492
1493 let check = TokenInstruction::Revoke;
1494 let packed = check.pack();
1495 let expect = Vec::from([5u8]);
1496 assert_eq!(packed, expect);
1497 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1498 assert_eq!(unpacked, check);
1499
1500 let check = TokenInstruction::SetAuthority {
1501 authority_type: AuthorityType::FreezeAccount,
1502 new_authority: COption::Some(Pubkey::new(&[4u8; 32])),
1503 };
1504 let packed = check.pack();
1505 let mut expect = Vec::from([6u8, 1]);
1506 expect.extend_from_slice(&[1]);
1507 expect.extend_from_slice(&[4u8; 32]);
1508 assert_eq!(packed, expect);
1509 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1510 assert_eq!(unpacked, check);
1511
1512 let check = TokenInstruction::MintTo { amount: 1 };
1513 let packed = check.pack();
1514 let expect = Vec::from([7u8, 1, 0, 0, 0, 0, 0, 0, 0]);
1515 assert_eq!(packed, expect);
1516 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1517 assert_eq!(unpacked, check);
1518
1519 let check = TokenInstruction::Burn { amount: 1 };
1520 let packed = check.pack();
1521 let expect = Vec::from([8u8, 1, 0, 0, 0, 0, 0, 0, 0]);
1522 assert_eq!(packed, expect);
1523 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1524 assert_eq!(unpacked, check);
1525
1526 let check = TokenInstruction::CloseAccount;
1527 let packed = check.pack();
1528 let expect = Vec::from([9u8]);
1529 assert_eq!(packed, expect);
1530 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1531 assert_eq!(unpacked, check);
1532
1533 let check = TokenInstruction::FreezeAccount;
1534 let packed = check.pack();
1535 let expect = Vec::from([10u8]);
1536 assert_eq!(packed, expect);
1537 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1538 assert_eq!(unpacked, check);
1539
1540 let check = TokenInstruction::ThawAccount;
1541 let packed = check.pack();
1542 let expect = Vec::from([11u8]);
1543 assert_eq!(packed, expect);
1544 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1545 assert_eq!(unpacked, check);
1546
1547 let check = TokenInstruction::TransferChecked {
1548 amount: 1,
1549 decimals: 2,
1550 };
1551 let packed = check.pack();
1552 let expect = Vec::from([12u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
1553 assert_eq!(packed, expect);
1554 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1555 assert_eq!(unpacked, check);
1556
1557 let check = TokenInstruction::ApproveChecked {
1558 amount: 1,
1559 decimals: 2,
1560 };
1561 let packed = check.pack();
1562 let expect = Vec::from([13u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
1563 assert_eq!(packed, expect);
1564 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1565 assert_eq!(unpacked, check);
1566
1567 let check = TokenInstruction::MintToChecked {
1568 amount: 1,
1569 decimals: 2,
1570 };
1571 let packed = check.pack();
1572 let expect = Vec::from([14u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
1573 assert_eq!(packed, expect);
1574 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1575 assert_eq!(unpacked, check);
1576
1577 let check = TokenInstruction::BurnChecked {
1578 amount: 1,
1579 decimals: 2,
1580 };
1581 let packed = check.pack();
1582 let expect = Vec::from([15u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
1583 assert_eq!(packed, expect);
1584 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1585 assert_eq!(unpacked, check);
1586
1587 let check = TokenInstruction::InitializeAccount2 {
1588 owner: Pubkey::new(&[2u8; 32]),
1589 };
1590 let packed = check.pack();
1591 let mut expect = vec![16u8];
1592 expect.extend_from_slice(&[2u8; 32]);
1593 assert_eq!(packed, expect);
1594 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1595 assert_eq!(unpacked, check);
1596
1597 let check = TokenInstruction::SyncNative;
1598 let packed = check.pack();
1599 let expect = vec![17u8];
1600 assert_eq!(packed, expect);
1601 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1602 assert_eq!(unpacked, check);
1603
1604 let check = TokenInstruction::InitializeAccount3 {
1605 owner: Pubkey::new(&[2u8; 32]),
1606 };
1607 let packed = check.pack();
1608 let mut expect = vec![18u8];
1609 expect.extend_from_slice(&[2u8; 32]);
1610 assert_eq!(packed, expect);
1611 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1612 assert_eq!(unpacked, check);
1613
1614 let check = TokenInstruction::InitializeMultisig2 { m: 1 };
1615 let packed = check.pack();
1616 let expect = Vec::from([19u8, 1]);
1617 assert_eq!(packed, expect);
1618 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1619 assert_eq!(unpacked, check);
1620
1621 let check = TokenInstruction::InitializeMint2 {
1622 decimals: 2,
1623 mint_authority: Pubkey::new(&[1u8; 32]),
1624 freeze_authority: COption::None,
1625 };
1626 let packed = check.pack();
1627 let mut expect = Vec::from([20u8, 2]);
1628 expect.extend_from_slice(&[1u8; 32]);
1629 expect.extend_from_slice(&[0]);
1630 assert_eq!(packed, expect);
1631 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1632 assert_eq!(unpacked, check);
1633
1634 let check = TokenInstruction::InitializeMint2 {
1635 decimals: 2,
1636 mint_authority: Pubkey::new(&[2u8; 32]),
1637 freeze_authority: COption::Some(Pubkey::new(&[3u8; 32])),
1638 };
1639 let packed = check.pack();
1640 let mut expect = vec![20u8, 2];
1641 expect.extend_from_slice(&[2u8; 32]);
1642 expect.extend_from_slice(&[1]);
1643 expect.extend_from_slice(&[3u8; 32]);
1644 assert_eq!(packed, expect);
1645 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1646 assert_eq!(unpacked, check);
1647
1648 let check = TokenInstruction::GetAccountDataSize;
1649 let packed = check.pack();
1650 let expect = vec![21u8];
1651 assert_eq!(packed, expect);
1652 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1653 assert_eq!(unpacked, check);
1654
1655 let check = TokenInstruction::InitializeImmutableOwner;
1656 let packed = check.pack();
1657 let expect = vec![22u8];
1658 assert_eq!(packed, expect);
1659 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1660 assert_eq!(unpacked, check);
1661
1662 let check = TokenInstruction::AmountToUiAmount { amount: 42 };
1663 let packed = check.pack();
1664 let expect = vec![23u8, 42, 0, 0, 0, 0, 0, 0, 0];
1665 assert_eq!(packed, expect);
1666 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1667 assert_eq!(unpacked, check);
1668
1669 let check = TokenInstruction::UiAmountToAmount { ui_amount: "0.42" };
1670 let packed = check.pack();
1671 let expect = vec![24u8, 48, 46, 52, 50];
1672 assert_eq!(packed, expect);
1673 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1674 assert_eq!(unpacked, check);
1675 }
1676
1677 #[test]
1678 fn test_instruction_unpack_panic() {
1679 for i in 0..255u8 {
1680 for j in 1..10 {
1681 let mut data = vec![0; j];
1682 data[0] = i;
1683 let _no_panic = TokenInstruction::unpack(&data);
1684 }
1685 }
1686 }
1687
1688 proptest! {
1689 #![proptest_config(ProptestConfig::with_cases(1024))]
1690 #[test]
1691 fn test_instruction_unpack_proptest(
1692 data in prop::collection::vec(any::<u8>(), 0..255)
1693 ) {
1694 let _no_panic = TokenInstruction::unpack(&data);
1695 }
1696 }
1697}