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