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