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