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