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