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 {
463 txid: [u8; 32],
465 input_to_sign: InputToSign,
467 },
468}
469impl<'a> TokenInstruction<'a> {
470 pub fn unpack(input: &'a [u8]) -> Result<Self, ProgramError> {
473 use TokenError::InvalidInstruction;
474
475 let (&tag, rest) = input.split_first().ok_or(InvalidInstruction)?;
476 Ok(match tag {
477 0 => {
478 let (&decimals, rest) = rest.split_first().ok_or(InvalidInstruction)?;
479 let (mint_authority, rest) = Self::unpack_pubkey(rest)?;
480 let (freeze_authority, _rest) = Self::unpack_pubkey_option(rest)?;
481 Self::InitializeMint {
482 mint_authority,
483 freeze_authority,
484 decimals,
485 }
486 }
487 1 => Self::InitializeAccount,
488 2 => {
489 let &m = rest.first().ok_or(InvalidInstruction)?;
490 Self::InitializeMultisig { m }
491 }
492 3 | 4 | 7 | 8 => {
493 let amount = rest
494 .get(..8)
495 .and_then(|slice| slice.try_into().ok())
496 .map(u64::from_le_bytes)
497 .ok_or(InvalidInstruction)?;
498 match tag {
499 3 => Self::Transfer { amount },
500 4 => Self::Approve { amount },
501 7 => Self::MintTo { amount },
502 8 => Self::Burn { amount },
503 _ => unreachable!(),
504 }
505 }
506 5 => Self::Revoke,
507 6 => {
508 let (authority_type, rest) = rest
509 .split_first()
510 .ok_or_else(|| ProgramError::from(InvalidInstruction))
511 .and_then(|(&t, rest)| Ok((AuthorityType::from(t)?, rest)))?;
512 let (new_authority, _rest) = Self::unpack_pubkey_option(rest)?;
513
514 Self::SetAuthority {
515 authority_type,
516 new_authority,
517 }
518 }
519 9 => Self::CloseAccount,
520 10 => Self::FreezeAccount,
521 11 => Self::ThawAccount,
522 12 => {
523 let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
524 Self::TransferChecked { amount, decimals }
525 }
526 13 => {
527 let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
528 Self::ApproveChecked { amount, decimals }
529 }
530 14 => {
531 let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
532 Self::MintToChecked { amount, decimals }
533 }
534 15 => {
535 let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
536 Self::BurnChecked { amount, decimals }
537 }
538 16 => {
539 let (owner, _rest) = Self::unpack_pubkey(rest)?;
540 Self::InitializeAccount2 { owner }
541 }
542 17 => Self::SyncNative,
543 18 => {
544 let (owner, _rest) = Self::unpack_pubkey(rest)?;
545 Self::InitializeAccount3 { owner }
546 }
547 19 => {
548 let (&decimals, rest) = rest.split_first().ok_or(InvalidInstruction)?;
549 let (mint_authority, rest) = Self::unpack_pubkey(rest)?;
550 let (freeze_authority, _rest) = Self::unpack_pubkey_option(rest)?;
551 Self::InitializeMint2 {
552 mint_authority,
553 freeze_authority,
554 decimals,
555 }
556 }
557 20 => Self::GetAccountDataSize,
558 21 => Self::InitializeImmutableOwner,
559 22 => {
560 let (amount, _rest) = Self::unpack_u64(rest)?;
561 Self::AmountToUiAmount { amount }
562 }
563 23 => {
564 let ui_amount = std::str::from_utf8(rest).map_err(|_| InvalidInstruction)?;
565 Self::UiAmountToAmount { ui_amount }
566 }
567 24 => {
568 let (txid, input_to_sign) = Self::unpack_txid(rest)?;
569 let input_to_sign = InputToSign::from_slice(input_to_sign)?;
570 Self::Anchor {
571 txid,
572 input_to_sign,
573 }
574 }
575 _ => return Err(TokenError::InvalidInstruction.into()),
576 })
577 }
578
579 pub fn pack(&self) -> Vec<u8> {
582 let mut buf = Vec::with_capacity(size_of::<Self>());
583 match self {
584 &Self::InitializeMint {
585 ref mint_authority,
586 ref freeze_authority,
587 decimals,
588 } => {
589 buf.push(0);
590 buf.push(decimals);
591 buf.extend_from_slice(mint_authority.as_ref());
592 Self::pack_pubkey_option(freeze_authority, &mut buf);
593 }
594 Self::InitializeAccount => buf.push(1),
595 &Self::InitializeMultisig { m } => {
596 buf.push(2);
597 buf.push(m);
598 }
599 &Self::Transfer { amount } => {
600 buf.push(3);
601 buf.extend_from_slice(&amount.to_le_bytes());
602 }
603 &Self::Approve { amount } => {
604 buf.push(4);
605 buf.extend_from_slice(&amount.to_le_bytes());
606 }
607 &Self::MintTo { amount } => {
608 buf.push(7);
609 buf.extend_from_slice(&amount.to_le_bytes());
610 }
611 &Self::Burn { amount } => {
612 buf.push(8);
613 buf.extend_from_slice(&amount.to_le_bytes());
614 }
615 Self::Revoke => buf.push(5),
616 Self::SetAuthority {
617 authority_type,
618 ref new_authority,
619 } => {
620 buf.push(6);
621 buf.push(authority_type.into());
622 Self::pack_pubkey_option(new_authority, &mut buf);
623 }
624 Self::CloseAccount => buf.push(9),
625 Self::FreezeAccount => buf.push(10),
626 Self::ThawAccount => buf.push(11),
627 &Self::TransferChecked { amount, decimals } => {
628 buf.push(12);
629 buf.extend_from_slice(&amount.to_le_bytes());
630 buf.push(decimals);
631 }
632 &Self::ApproveChecked { amount, decimals } => {
633 buf.push(13);
634 buf.extend_from_slice(&amount.to_le_bytes());
635 buf.push(decimals);
636 }
637 &Self::MintToChecked { amount, decimals } => {
638 buf.push(14);
639 buf.extend_from_slice(&amount.to_le_bytes());
640 buf.push(decimals);
641 }
642 &Self::BurnChecked { amount, decimals } => {
643 buf.push(15);
644 buf.extend_from_slice(&amount.to_le_bytes());
645 buf.push(decimals);
646 }
647 &Self::InitializeAccount2 { owner } => {
648 buf.push(16);
649 buf.extend_from_slice(owner.as_ref());
650 }
651 &Self::SyncNative => {
652 buf.push(17);
653 }
654 &Self::InitializeAccount3 { owner } => {
655 buf.push(18);
656 buf.extend_from_slice(owner.as_ref());
657 }
658 &Self::InitializeMint2 {
659 ref mint_authority,
660 ref freeze_authority,
661 decimals,
662 } => {
663 buf.push(19);
664 buf.push(decimals);
665 buf.extend_from_slice(mint_authority.as_ref());
666 Self::pack_pubkey_option(freeze_authority, &mut buf);
667 }
668 &Self::GetAccountDataSize => {
669 buf.push(20);
670 }
671 &Self::InitializeImmutableOwner => {
672 buf.push(21);
673 }
674 &Self::AmountToUiAmount { amount } => {
675 buf.push(22);
676 buf.extend_from_slice(&amount.to_le_bytes());
677 }
678 Self::UiAmountToAmount { ui_amount } => {
679 buf.push(23);
680 buf.extend_from_slice(ui_amount.as_bytes());
681 }
682 Self::Anchor {
683 txid,
684 input_to_sign,
685 } => {
686 buf.push(24);
687 buf.extend_from_slice(txid);
688 buf.extend_from_slice(&input_to_sign.serialise());
689 }
690 };
691 buf
692 }
693
694 fn unpack_pubkey(input: &[u8]) -> Result<(Pubkey, &[u8]), ProgramError> {
695 if input.len() >= 32 {
696 let (key, rest) = input.split_at(32);
697 let pk = Pubkey::from_slice(key);
698 Ok((pk, rest))
699 } else {
700 Err(TokenError::InvalidInstruction.into())
701 }
702 }
703
704 fn unpack_txid(input: &[u8]) -> Result<([u8; 32], &[u8]), ProgramError> {
705 if input.len() >= 32 {
706 let (key, rest) = input.split_at(32);
707 let txid = key.try_into().map_err(|_| TokenError::InvalidInstruction)?;
708 Ok((txid, rest))
709 } else {
710 Err(TokenError::InvalidInstruction.into())
711 }
712 }
713
714 fn unpack_pubkey_option(input: &[u8]) -> Result<(COption<Pubkey>, &[u8]), ProgramError> {
715 match input.split_first() {
716 Option::Some((&0, rest)) => Ok((COption::None, rest)),
717 Option::Some((&1, rest)) if rest.len() >= 32 => {
718 let (key, rest) = rest.split_at(32);
719 let pk = Pubkey::from_slice(key);
720 Ok((COption::Some(pk), rest))
721 }
722 _ => Err(TokenError::InvalidInstruction.into()),
723 }
724 }
725
726 fn pack_pubkey_option(value: &COption<Pubkey>, buf: &mut Vec<u8>) {
727 match *value {
728 COption::Some(ref key) => {
729 buf.push(1);
730 buf.extend_from_slice(&key.serialize());
731 }
732 COption::None => buf.push(0),
733 }
734 }
735
736 fn unpack_u64(input: &[u8]) -> Result<(u64, &[u8]), ProgramError> {
737 let value = input
738 .get(..U64_BYTES)
739 .and_then(|slice| slice.try_into().ok())
740 .map(u64::from_le_bytes)
741 .ok_or(TokenError::InvalidInstruction)?;
742 Ok((value, &input[U64_BYTES..]))
743 }
744
745 fn unpack_amount_decimals(input: &[u8]) -> Result<(u64, u8, &[u8]), ProgramError> {
746 let (amount, rest) = Self::unpack_u64(input)?;
747 let (&decimals, rest) = rest.split_first().ok_or(TokenError::InvalidInstruction)?;
748 Ok((amount, decimals, rest))
749 }
750}
751
752#[repr(u8)]
754#[derive(Clone, Debug, PartialEq)]
755pub enum AuthorityType {
756 MintTokens,
758 FreezeAccount,
760 AccountOwner,
762 CloseAccount,
764}
765
766impl AuthorityType {
767 fn into(&self) -> u8 {
768 match self {
769 AuthorityType::MintTokens => 0,
770 AuthorityType::FreezeAccount => 1,
771 AuthorityType::AccountOwner => 2,
772 AuthorityType::CloseAccount => 3,
773 }
774 }
775
776 fn from(index: u8) -> Result<Self, ProgramError> {
777 match index {
778 0 => Ok(AuthorityType::MintTokens),
779 1 => Ok(AuthorityType::FreezeAccount),
780 2 => Ok(AuthorityType::AccountOwner),
781 3 => Ok(AuthorityType::CloseAccount),
782 _ => Err(TokenError::InvalidInstruction.into()),
783 }
784 }
785}
786
787pub fn initialize_mint(
789 token_program_id: &Pubkey,
790 mint_pubkey: &Pubkey,
791 mint_authority_pubkey: &Pubkey,
792 freeze_authority_pubkey: Option<&Pubkey>,
793 decimals: u8,
794) -> Result<Instruction, ProgramError> {
795 check_program_account(token_program_id)?;
796 let freeze_authority = freeze_authority_pubkey.cloned().into();
797 let data = TokenInstruction::InitializeMint {
798 mint_authority: *mint_authority_pubkey,
799 freeze_authority,
800 decimals,
801 }
802 .pack();
803
804 let accounts = vec![AccountMeta::new(*mint_pubkey, false)];
805
806 Ok(Instruction {
807 program_id: *token_program_id,
808 accounts,
809 data,
810 })
811}
812
813pub fn initialize_mint2(
815 token_program_id: &Pubkey,
816 mint_pubkey: &Pubkey,
817 mint_authority_pubkey: &Pubkey,
818 freeze_authority_pubkey: Option<&Pubkey>,
819 decimals: u8,
820) -> Result<Instruction, ProgramError> {
821 check_program_account(token_program_id)?;
822 let freeze_authority = freeze_authority_pubkey.cloned().into();
823 let data = TokenInstruction::InitializeMint2 {
824 mint_authority: *mint_authority_pubkey,
825 freeze_authority,
826 decimals,
827 }
828 .pack();
829
830 let accounts = vec![AccountMeta::new(*mint_pubkey, false)];
831
832 Ok(Instruction {
833 program_id: *token_program_id,
834 accounts,
835 data,
836 })
837}
838
839pub fn initialize_account(
841 token_program_id: &Pubkey,
842 account_pubkey: &Pubkey,
843 mint_pubkey: &Pubkey,
844 owner_pubkey: &Pubkey,
845) -> Result<Instruction, ProgramError> {
846 check_program_account(token_program_id)?;
847 let data = TokenInstruction::InitializeAccount.pack();
848
849 let accounts = vec![
850 AccountMeta::new(*account_pubkey, false),
851 AccountMeta::new_readonly(*mint_pubkey, false),
852 AccountMeta::new_readonly(*owner_pubkey, false),
853 ];
854
855 Ok(Instruction {
856 program_id: *token_program_id,
857 accounts,
858 data,
859 })
860}
861
862pub fn initialize_account2(
864 token_program_id: &Pubkey,
865 account_pubkey: &Pubkey,
866 mint_pubkey: &Pubkey,
867 owner_pubkey: &Pubkey,
868) -> Result<Instruction, ProgramError> {
869 check_program_account(token_program_id)?;
870 let data = TokenInstruction::InitializeAccount2 {
871 owner: *owner_pubkey,
872 }
873 .pack();
874
875 let accounts = vec![
876 AccountMeta::new(*account_pubkey, false),
877 AccountMeta::new_readonly(*mint_pubkey, false),
878 ];
879
880 Ok(Instruction {
881 program_id: *token_program_id,
882 accounts,
883 data,
884 })
885}
886
887pub fn initialize_account3(
889 token_program_id: &Pubkey,
890 account_pubkey: &Pubkey,
891 mint_pubkey: &Pubkey,
892 owner_pubkey: &Pubkey,
893) -> Result<Instruction, ProgramError> {
894 check_program_account(token_program_id)?;
895 let data = TokenInstruction::InitializeAccount3 {
896 owner: *owner_pubkey,
897 }
898 .pack();
899
900 let accounts = vec![
901 AccountMeta::new(*account_pubkey, false),
902 AccountMeta::new_readonly(*mint_pubkey, false),
903 ];
904
905 Ok(Instruction {
906 program_id: *token_program_id,
907 accounts,
908 data,
909 })
910}
911
912pub fn initialize_multisig(
914 token_program_id: &Pubkey,
915 multisig_pubkey: &Pubkey,
916 signer_pubkeys: &[&Pubkey],
917 m: u8,
918) -> Result<Instruction, ProgramError> {
919 check_program_account(token_program_id)?;
920 if !is_valid_signer_index(m as usize)
921 || !is_valid_signer_index(signer_pubkeys.len())
922 || m as usize > signer_pubkeys.len()
923 {
924 return Err(ProgramError::MissingRequiredSignature);
925 }
926 let data = TokenInstruction::InitializeMultisig { m }.pack();
927
928 let mut accounts = Vec::with_capacity(1 + 1 + signer_pubkeys.len());
929 accounts.push(AccountMeta::new(*multisig_pubkey, false));
930 for signer_pubkey in signer_pubkeys.iter() {
931 accounts.push(AccountMeta::new_readonly(**signer_pubkey, false));
932 }
933
934 Ok(Instruction {
935 program_id: *token_program_id,
936 accounts,
937 data,
938 })
939}
940
941pub fn transfer(
943 token_program_id: &Pubkey,
944 source_pubkey: &Pubkey,
945 destination_pubkey: &Pubkey,
946 authority_pubkey: &Pubkey,
947 signer_pubkeys: &[&Pubkey],
948 amount: u64,
949) -> Result<Instruction, ProgramError> {
950 check_program_account(token_program_id)?;
951 let data = TokenInstruction::Transfer { amount }.pack();
952
953 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
954 accounts.push(AccountMeta::new(*source_pubkey, false));
955 accounts.push(AccountMeta::new(*destination_pubkey, false));
956 accounts.push(AccountMeta::new_readonly(
957 *authority_pubkey,
958 signer_pubkeys.is_empty(),
959 ));
960 for signer_pubkey in signer_pubkeys.iter() {
961 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
962 }
963
964 Ok(Instruction {
965 program_id: *token_program_id,
966 accounts,
967 data,
968 })
969}
970
971pub fn approve(
973 token_program_id: &Pubkey,
974 source_pubkey: &Pubkey,
975 delegate_pubkey: &Pubkey,
976 owner_pubkey: &Pubkey,
977 signer_pubkeys: &[&Pubkey],
978 amount: u64,
979) -> Result<Instruction, ProgramError> {
980 check_program_account(token_program_id)?;
981 let data = TokenInstruction::Approve { amount }.pack();
982
983 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
984 accounts.push(AccountMeta::new(*source_pubkey, false));
985 accounts.push(AccountMeta::new_readonly(*delegate_pubkey, false));
986 accounts.push(AccountMeta::new_readonly(
987 *owner_pubkey,
988 signer_pubkeys.is_empty(),
989 ));
990 for signer_pubkey in signer_pubkeys.iter() {
991 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
992 }
993
994 Ok(Instruction {
995 program_id: *token_program_id,
996 accounts,
997 data,
998 })
999}
1000
1001pub fn revoke(
1003 token_program_id: &Pubkey,
1004 source_pubkey: &Pubkey,
1005 owner_pubkey: &Pubkey,
1006 signer_pubkeys: &[&Pubkey],
1007) -> Result<Instruction, ProgramError> {
1008 check_program_account(token_program_id)?;
1009 let data = TokenInstruction::Revoke.pack();
1010
1011 let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len());
1012 accounts.push(AccountMeta::new(*source_pubkey, false));
1013 accounts.push(AccountMeta::new_readonly(
1014 *owner_pubkey,
1015 signer_pubkeys.is_empty(),
1016 ));
1017 for signer_pubkey in signer_pubkeys.iter() {
1018 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1019 }
1020
1021 Ok(Instruction {
1022 program_id: *token_program_id,
1023 accounts,
1024 data,
1025 })
1026}
1027
1028pub fn set_authority(
1030 token_program_id: &Pubkey,
1031 owned_pubkey: &Pubkey,
1032 new_authority_pubkey: Option<&Pubkey>,
1033 authority_type: AuthorityType,
1034 owner_pubkey: &Pubkey,
1035 signer_pubkeys: &[&Pubkey],
1036) -> Result<Instruction, ProgramError> {
1037 check_program_account(token_program_id)?;
1038 let new_authority = new_authority_pubkey.cloned().into();
1039 let data = TokenInstruction::SetAuthority {
1040 authority_type,
1041 new_authority,
1042 }
1043 .pack();
1044
1045 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1046 accounts.push(AccountMeta::new(*owned_pubkey, false));
1047 accounts.push(AccountMeta::new_readonly(
1048 *owner_pubkey,
1049 signer_pubkeys.is_empty(),
1050 ));
1051 for signer_pubkey in signer_pubkeys.iter() {
1052 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1053 }
1054
1055 Ok(Instruction {
1056 program_id: *token_program_id,
1057 accounts,
1058 data,
1059 })
1060}
1061
1062pub fn mint_to(
1064 token_program_id: &Pubkey,
1065 mint_pubkey: &Pubkey,
1066 account_pubkey: &Pubkey,
1067 owner_pubkey: &Pubkey,
1068 signer_pubkeys: &[&Pubkey],
1069 amount: u64,
1070) -> Result<Instruction, ProgramError> {
1071 check_program_account(token_program_id)?;
1072 let data = TokenInstruction::MintTo { amount }.pack();
1073
1074 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1075 accounts.push(AccountMeta::new(*mint_pubkey, false));
1076 accounts.push(AccountMeta::new(*account_pubkey, false));
1077 accounts.push(AccountMeta::new_readonly(
1078 *owner_pubkey,
1079 signer_pubkeys.is_empty(),
1080 ));
1081 for signer_pubkey in signer_pubkeys.iter() {
1082 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1083 }
1084
1085 Ok(Instruction {
1086 program_id: *token_program_id,
1087 accounts,
1088 data,
1089 })
1090}
1091
1092pub fn burn(
1094 token_program_id: &Pubkey,
1095 account_pubkey: &Pubkey,
1096 mint_pubkey: &Pubkey,
1097 authority_pubkey: &Pubkey,
1098 signer_pubkeys: &[&Pubkey],
1099 amount: u64,
1100) -> Result<Instruction, ProgramError> {
1101 check_program_account(token_program_id)?;
1102 let data = TokenInstruction::Burn { amount }.pack();
1103
1104 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1105 accounts.push(AccountMeta::new(*account_pubkey, false));
1106 accounts.push(AccountMeta::new(*mint_pubkey, false));
1107 accounts.push(AccountMeta::new_readonly(
1108 *authority_pubkey,
1109 signer_pubkeys.is_empty(),
1110 ));
1111 for signer_pubkey in signer_pubkeys.iter() {
1112 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1113 }
1114
1115 Ok(Instruction {
1116 program_id: *token_program_id,
1117 accounts,
1118 data,
1119 })
1120}
1121
1122pub fn close_account(
1124 token_program_id: &Pubkey,
1125 account_pubkey: &Pubkey,
1126 destination_pubkey: &Pubkey,
1127 owner_pubkey: &Pubkey,
1128 signer_pubkeys: &[&Pubkey],
1129) -> Result<Instruction, ProgramError> {
1130 check_program_account(token_program_id)?;
1131 let data = TokenInstruction::CloseAccount.pack();
1132
1133 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1134 accounts.push(AccountMeta::new(*account_pubkey, false));
1135 accounts.push(AccountMeta::new(*destination_pubkey, false));
1136 accounts.push(AccountMeta::new_readonly(
1137 *owner_pubkey,
1138 signer_pubkeys.is_empty(),
1139 ));
1140 for signer_pubkey in signer_pubkeys.iter() {
1141 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1142 }
1143
1144 Ok(Instruction {
1145 program_id: *token_program_id,
1146 accounts,
1147 data,
1148 })
1149}
1150
1151pub fn freeze_account(
1153 token_program_id: &Pubkey,
1154 account_pubkey: &Pubkey,
1155 mint_pubkey: &Pubkey,
1156 owner_pubkey: &Pubkey,
1157 signer_pubkeys: &[&Pubkey],
1158) -> Result<Instruction, ProgramError> {
1159 check_program_account(token_program_id)?;
1160 let data = TokenInstruction::FreezeAccount.pack();
1161
1162 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1163 accounts.push(AccountMeta::new(*account_pubkey, false));
1164 accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
1165 accounts.push(AccountMeta::new_readonly(
1166 *owner_pubkey,
1167 signer_pubkeys.is_empty(),
1168 ));
1169 for signer_pubkey in signer_pubkeys.iter() {
1170 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1171 }
1172
1173 Ok(Instruction {
1174 program_id: *token_program_id,
1175 accounts,
1176 data,
1177 })
1178}
1179
1180pub fn thaw_account(
1182 token_program_id: &Pubkey,
1183 account_pubkey: &Pubkey,
1184 mint_pubkey: &Pubkey,
1185 owner_pubkey: &Pubkey,
1186 signer_pubkeys: &[&Pubkey],
1187) -> Result<Instruction, ProgramError> {
1188 check_program_account(token_program_id)?;
1189 let data = TokenInstruction::ThawAccount.pack();
1190
1191 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1192 accounts.push(AccountMeta::new(*account_pubkey, false));
1193 accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
1194 accounts.push(AccountMeta::new_readonly(
1195 *owner_pubkey,
1196 signer_pubkeys.is_empty(),
1197 ));
1198 for signer_pubkey in signer_pubkeys.iter() {
1199 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1200 }
1201
1202 Ok(Instruction {
1203 program_id: *token_program_id,
1204 accounts,
1205 data,
1206 })
1207}
1208
1209#[allow(clippy::too_many_arguments)]
1211pub fn transfer_checked(
1212 token_program_id: &Pubkey,
1213 source_pubkey: &Pubkey,
1214 mint_pubkey: &Pubkey,
1215 destination_pubkey: &Pubkey,
1216 authority_pubkey: &Pubkey,
1217 signer_pubkeys: &[&Pubkey],
1218 amount: u64,
1219 decimals: u8,
1220) -> Result<Instruction, ProgramError> {
1221 check_program_account(token_program_id)?;
1222 let data = TokenInstruction::TransferChecked { amount, decimals }.pack();
1223
1224 let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len());
1225 accounts.push(AccountMeta::new(*source_pubkey, false));
1226 accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
1227 accounts.push(AccountMeta::new(*destination_pubkey, false));
1228 accounts.push(AccountMeta::new_readonly(
1229 *authority_pubkey,
1230 signer_pubkeys.is_empty(),
1231 ));
1232 for signer_pubkey in signer_pubkeys.iter() {
1233 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1234 }
1235
1236 Ok(Instruction {
1237 program_id: *token_program_id,
1238 accounts,
1239 data,
1240 })
1241}
1242
1243#[allow(clippy::too_many_arguments)]
1245pub fn approve_checked(
1246 token_program_id: &Pubkey,
1247 source_pubkey: &Pubkey,
1248 mint_pubkey: &Pubkey,
1249 delegate_pubkey: &Pubkey,
1250 owner_pubkey: &Pubkey,
1251 signer_pubkeys: &[&Pubkey],
1252 amount: u64,
1253 decimals: u8,
1254) -> Result<Instruction, ProgramError> {
1255 check_program_account(token_program_id)?;
1256 let data = TokenInstruction::ApproveChecked { amount, decimals }.pack();
1257
1258 let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len());
1259 accounts.push(AccountMeta::new(*source_pubkey, false));
1260 accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
1261 accounts.push(AccountMeta::new_readonly(*delegate_pubkey, false));
1262 accounts.push(AccountMeta::new_readonly(
1263 *owner_pubkey,
1264 signer_pubkeys.is_empty(),
1265 ));
1266 for signer_pubkey in signer_pubkeys.iter() {
1267 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1268 }
1269
1270 Ok(Instruction {
1271 program_id: *token_program_id,
1272 accounts,
1273 data,
1274 })
1275}
1276
1277pub fn mint_to_checked(
1279 token_program_id: &Pubkey,
1280 mint_pubkey: &Pubkey,
1281 account_pubkey: &Pubkey,
1282 owner_pubkey: &Pubkey,
1283 signer_pubkeys: &[&Pubkey],
1284 amount: u64,
1285 decimals: u8,
1286) -> Result<Instruction, ProgramError> {
1287 check_program_account(token_program_id)?;
1288 let data = TokenInstruction::MintToChecked { amount, decimals }.pack();
1289
1290 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1291 accounts.push(AccountMeta::new(*mint_pubkey, false));
1292 accounts.push(AccountMeta::new(*account_pubkey, false));
1293 accounts.push(AccountMeta::new_readonly(
1294 *owner_pubkey,
1295 signer_pubkeys.is_empty(),
1296 ));
1297 for signer_pubkey in signer_pubkeys.iter() {
1298 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1299 }
1300
1301 Ok(Instruction {
1302 program_id: *token_program_id,
1303 accounts,
1304 data,
1305 })
1306}
1307
1308pub fn burn_checked(
1310 token_program_id: &Pubkey,
1311 account_pubkey: &Pubkey,
1312 mint_pubkey: &Pubkey,
1313 authority_pubkey: &Pubkey,
1314 signer_pubkeys: &[&Pubkey],
1315 amount: u64,
1316 decimals: u8,
1317) -> Result<Instruction, ProgramError> {
1318 check_program_account(token_program_id)?;
1319 let data = TokenInstruction::BurnChecked { amount, decimals }.pack();
1320
1321 let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1322 accounts.push(AccountMeta::new(*account_pubkey, false));
1323 accounts.push(AccountMeta::new(*mint_pubkey, false));
1324 accounts.push(AccountMeta::new_readonly(
1325 *authority_pubkey,
1326 signer_pubkeys.is_empty(),
1327 ));
1328 for signer_pubkey in signer_pubkeys.iter() {
1329 accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1330 }
1331
1332 Ok(Instruction {
1333 program_id: *token_program_id,
1334 accounts,
1335 data,
1336 })
1337}
1338
1339pub fn sync_native(
1341 token_program_id: &Pubkey,
1342 account_pubkey: &Pubkey,
1343) -> Result<Instruction, ProgramError> {
1344 check_program_account(token_program_id)?;
1345
1346 Ok(Instruction {
1347 program_id: *token_program_id,
1348 accounts: vec![AccountMeta::new(*account_pubkey, false)],
1349 data: TokenInstruction::SyncNative.pack(),
1350 })
1351}
1352
1353pub fn get_account_data_size(
1355 token_program_id: &Pubkey,
1356 mint_pubkey: &Pubkey,
1357) -> Result<Instruction, ProgramError> {
1358 check_program_account(token_program_id)?;
1359
1360 Ok(Instruction {
1361 program_id: *token_program_id,
1362 accounts: vec![AccountMeta::new_readonly(*mint_pubkey, false)],
1363 data: TokenInstruction::GetAccountDataSize.pack(),
1364 })
1365}
1366
1367pub fn initialize_immutable_owner(
1369 token_program_id: &Pubkey,
1370 account_pubkey: &Pubkey,
1371) -> Result<Instruction, ProgramError> {
1372 check_program_account(token_program_id)?;
1373 Ok(Instruction {
1374 program_id: *token_program_id,
1375 accounts: vec![AccountMeta::new(*account_pubkey, false)],
1376 data: TokenInstruction::InitializeImmutableOwner.pack(),
1377 })
1378}
1379
1380pub fn amount_to_ui_amount(
1382 token_program_id: &Pubkey,
1383 mint_pubkey: &Pubkey,
1384 amount: u64,
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::AmountToUiAmount { amount }.pack(),
1392 })
1393}
1394
1395pub fn ui_amount_to_amount(
1397 token_program_id: &Pubkey,
1398 mint_pubkey: &Pubkey,
1399 ui_amount: &str,
1400) -> Result<Instruction, ProgramError> {
1401 check_program_account(token_program_id)?;
1402
1403 Ok(Instruction {
1404 program_id: *token_program_id,
1405 accounts: vec![AccountMeta::new_readonly(*mint_pubkey, false)],
1406 data: TokenInstruction::UiAmountToAmount { ui_amount }.pack(),
1407 })
1408}
1409
1410pub fn anchor(
1412 token_program_id: &Pubkey,
1413 account_pubkey: &Pubkey,
1414 owner_pubkey: &Pubkey,
1415 txid: [u8; 32],
1416 input_to_sign: InputToSign,
1417) -> Result<Instruction, ProgramError> {
1418 check_program_account(token_program_id)?;
1419
1420 Ok(Instruction {
1421 program_id: *token_program_id,
1422 accounts: vec![
1423 AccountMeta::new(*account_pubkey, false),
1424 AccountMeta::new_readonly(*owner_pubkey, true),
1425 ],
1426 data: TokenInstruction::Anchor {
1427 txid,
1428 input_to_sign,
1429 }
1430 .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::from_slice(&[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::from_slice(&[2u8; 32]),
1462 freeze_authority: COption::Some(Pubkey::from_slice(&[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::from_slice(&[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::from_slice(&[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::from_slice(&[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::InitializeMint2 {
1623 decimals: 2,
1624 mint_authority: Pubkey::from_slice(&[1u8; 32]),
1625 freeze_authority: COption::None,
1626 };
1627 let packed = check.pack();
1628 let mut expect = Vec::from([19u8, 2]);
1629 expect.extend_from_slice(&[1u8; 32]);
1630 expect.extend_from_slice(&[0]);
1631 assert_eq!(packed, expect);
1632 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1633 assert_eq!(unpacked, check);
1634
1635 let check = TokenInstruction::InitializeMint2 {
1636 decimals: 2,
1637 mint_authority: Pubkey::from_slice(&[2u8; 32]),
1638 freeze_authority: COption::Some(Pubkey::from_slice(&[3u8; 32])),
1639 };
1640 let packed = check.pack();
1641 let mut expect = vec![19u8, 2];
1642 expect.extend_from_slice(&[2u8; 32]);
1643 expect.extend_from_slice(&[1]);
1644 expect.extend_from_slice(&[3u8; 32]);
1645 assert_eq!(packed, expect);
1646 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1647 assert_eq!(unpacked, check);
1648
1649 let check = TokenInstruction::GetAccountDataSize;
1650 let packed = check.pack();
1651 let expect = vec![20u8];
1652 assert_eq!(packed, expect);
1653 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1654 assert_eq!(unpacked, check);
1655
1656 let check = TokenInstruction::InitializeImmutableOwner;
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::AmountToUiAmount { amount: 42 };
1664 let packed = check.pack();
1665 let expect = vec![22u8, 42, 0, 0, 0, 0, 0, 0, 0];
1666 assert_eq!(packed, expect);
1667 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1668 assert_eq!(unpacked, check);
1669
1670 let check = TokenInstruction::UiAmountToAmount { ui_amount: "0.42" };
1671 let packed = check.pack();
1672 let expect = vec![23u8, 48, 46, 52, 50];
1673 assert_eq!(packed, expect);
1674 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1675 assert_eq!(unpacked, check);
1676
1677 let check = TokenInstruction::Anchor {
1678 txid: [1u8; 32],
1679 input_to_sign: InputToSign {
1680 index: 0,
1681 signer: Pubkey::from_slice(&[2u8; 32]),
1682 },
1683 };
1684 let packed = check.pack();
1685 let mut expect = vec![24u8];
1686 expect.extend_from_slice(&[1u8; 32]);
1687 expect.extend_from_slice(&[0, 0, 0, 0]);
1689 expect.extend_from_slice(&[2u8; 32]);
1690 assert_eq!(packed, expect);
1691 let unpacked = TokenInstruction::unpack(&expect).unwrap();
1692 assert_eq!(unpacked, check);
1693 }
1694
1695 #[test]
1696 fn test_instruction_unpack_panic() {
1697 for i in 0..255u8 {
1698 for j in 1..10 {
1699 let mut data = vec![0; j];
1700 data[0] = i;
1701 let _no_panic = TokenInstruction::unpack(&data);
1702 }
1703 }
1704 }
1705
1706 proptest! {
1707 #![proptest_config(ProptestConfig::with_cases(1024))]
1708 #[test]
1709 fn test_instruction_unpack_proptest(
1710 data in prop::collection::vec(any::<u8>(), 0..255)
1711 ) {
1712 let _no_panic = TokenInstruction::unpack(&data);
1713 }
1714 }
1715}