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