use crate::{error::TokenError, option::COption};
use solana_sdk::{
instruction::{AccountMeta, Instruction},
program_error::ProgramError,
pubkey::Pubkey,
sysvar,
};
use std::mem::size_of;
pub const MIN_SIGNERS: usize = 1;
pub const MAX_SIGNERS: usize = 11;
#[repr(C)]
#[derive(Clone, Debug, PartialEq)]
pub enum TokenInstruction {
InitializeMint {
decimals: u8,
mint_authority: Pubkey,
freeze_authority: COption<Pubkey>,
},
InitializeAccount,
InitializeMultisig {
m: u8,
},
Transfer {
amount: u64,
},
Approve {
amount: u64,
},
Revoke,
SetAuthority {
authority_type: AuthorityType,
new_authority: COption<Pubkey>,
},
MintTo {
amount: u64,
},
Burn {
amount: u64,
},
CloseAccount,
FreezeAccount,
ThawAccount,
Transfer2 {
amount: u64,
decimals: u8,
},
Approve2 {
amount: u64,
decimals: u8,
},
MintTo2 {
amount: u64,
decimals: u8,
},
Burn2 {
amount: u64,
decimals: u8,
},
}
impl TokenInstruction {
pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
if input.len() < size_of::<u8>() {
return Err(TokenError::InvalidInstruction.into());
}
Ok(match input[0] {
0 => {
if input.len() < size_of::<u8>() + size_of::<Pubkey>() + size_of::<bool>() {
return Err(TokenError::InvalidInstruction.into());
}
let mut input_len = 0;
input_len += size_of::<u8>();
let decimals = unsafe { *(&input[input_len] as *const u8) };
input_len += size_of::<u8>();
let mint_authority = unsafe { *(&input[input_len] as *const u8 as *const Pubkey) };
input_len += size_of::<Pubkey>();
let freeze_authority = COption::unpack_or(
input,
&mut input_len,
Into::<ProgramError>::into(TokenError::InvalidInstruction),
)?;
Self::InitializeMint {
mint_authority,
freeze_authority,
decimals,
}
}
1 => Self::InitializeAccount,
2 => {
if input.len() < size_of::<u8>() + size_of::<u8>() {
return Err(TokenError::InvalidInstruction.into());
}
#[allow(clippy::cast_ptr_alignment)]
let m = unsafe { *(&input[1] as *const u8) };
Self::InitializeMultisig { m }
}
3 => {
if input.len() < size_of::<u8>() + size_of::<u64>() {
return Err(TokenError::InvalidInstruction.into());
}
#[allow(clippy::cast_ptr_alignment)]
let amount = unsafe { *(&input[size_of::<u8>()] as *const u8 as *const u64) };
Self::Transfer { amount }
}
4 => {
if input.len() < size_of::<u8>() + size_of::<u64>() {
return Err(TokenError::InvalidInstruction.into());
}
#[allow(clippy::cast_ptr_alignment)]
let amount = unsafe { *(&input[size_of::<u8>()] as *const u8 as *const u64) };
Self::Approve { amount }
}
5 => Self::Revoke,
6 => {
if input.len() < size_of::<u8>() + size_of::<u8>() {
return Err(TokenError::InvalidInstruction.into());
}
let mut input_len = 0;
input_len += size_of::<u8>();
let authority_type = AuthorityType::from(input[1])?;
input_len += size_of::<u8>();
let new_authority = COption::unpack_or(
input,
&mut input_len,
Into::<ProgramError>::into(TokenError::InvalidInstruction),
)?;
Self::SetAuthority {
authority_type,
new_authority,
}
}
7 => {
if input.len() < size_of::<u8>() + size_of::<u64>() {
return Err(TokenError::InvalidInstruction.into());
}
#[allow(clippy::cast_ptr_alignment)]
let amount = unsafe { *(&input[size_of::<u8>()] as *const u8 as *const u64) };
Self::MintTo { amount }
}
8 => {
if input.len() < size_of::<u8>() + size_of::<u64>() {
return Err(TokenError::InvalidInstruction.into());
}
#[allow(clippy::cast_ptr_alignment)]
let amount = unsafe { *(&input[size_of::<u8>()] as *const u8 as *const u64) };
Self::Burn { amount }
}
9 => Self::CloseAccount,
10 => Self::FreezeAccount,
11 => Self::ThawAccount,
12 => {
if input.len() < size_of::<u8>() + size_of::<u64>() + size_of::<u8>() {
return Err(TokenError::InvalidInstruction.into());
}
let mut input_len = 0;
input_len += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let amount = unsafe { *(&input[input_len] as *const u8 as *const u64) };
input_len += size_of::<u64>();
let decimals = unsafe { *(&input[input_len] as *const u8) };
Self::Transfer2 { amount, decimals }
}
13 => {
if input.len() < size_of::<u8>() + size_of::<u64>() + size_of::<u8>() {
return Err(TokenError::InvalidInstruction.into());
}
let mut input_len = 0;
input_len += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let amount = unsafe { *(&input[input_len] as *const u8 as *const u64) };
input_len += size_of::<u64>();
let decimals = unsafe { *(&input[input_len] as *const u8) };
Self::Approve2 { amount, decimals }
}
14 => {
if input.len() < size_of::<u8>() + size_of::<u64>() + size_of::<u8>() {
return Err(TokenError::InvalidInstruction.into());
}
let mut input_len = 0;
input_len += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let amount = unsafe { *(&input[input_len] as *const u8 as *const u64) };
input_len += size_of::<u64>();
let decimals = unsafe { *(&input[input_len] as *const u8) };
Self::MintTo2 { amount, decimals }
}
15 => {
if input.len() < size_of::<u8>() + size_of::<u64>() + size_of::<u8>() {
return Err(TokenError::InvalidInstruction.into());
}
let mut input_len = 0;
input_len += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let amount = unsafe { *(&input[input_len] as *const u8 as *const u64) };
input_len += size_of::<u64>();
let decimals = unsafe { *(&input[input_len] as *const u8) };
Self::Burn2 { amount, decimals }
}
_ => return Err(TokenError::InvalidInstruction.into()),
})
}
pub fn pack(&self) -> Result<Vec<u8>, ProgramError> {
let mut output = vec![0u8; size_of::<TokenInstruction>()];
let mut output_len = 0;
match self {
Self::InitializeMint {
mint_authority,
freeze_authority,
decimals,
} => {
output[output_len] = 0;
output_len += size_of::<u8>();
let value = unsafe { &mut *(&mut output[output_len] as *mut u8) };
*value = *decimals;
output_len += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut Pubkey) };
*value = *mint_authority;
output_len += size_of::<Pubkey>();
freeze_authority.pack(&mut output, &mut output_len);
}
Self::InitializeAccount => {
output[output_len] = 1;
output_len += size_of::<u8>();
}
Self::InitializeMultisig { m } => {
output[output_len] = 2;
output_len += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u8) };
*value = *m;
output_len += size_of::<u8>();
}
Self::Transfer { amount } => {
output[output_len] = 3;
output_len += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) };
*value = *amount;
output_len += size_of::<u64>();
}
Self::Approve { amount } => {
output[output_len] = 4;
output_len += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) };
*value = *amount;
output_len += size_of::<u64>();
}
Self::Revoke => {
output[output_len] = 5;
output_len += size_of::<u8>();
}
Self::SetAuthority {
authority_type,
new_authority,
} => {
output[output_len] = 6;
output_len += size_of::<u8>();
output[output_len] = authority_type.into();
output_len += size_of::<u8>();
new_authority.pack(&mut output, &mut output_len);
}
Self::MintTo { amount } => {
output[output_len] = 7;
output_len += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) };
*value = *amount;
output_len += size_of::<u64>();
}
Self::Burn { amount } => {
output[output_len] = 8;
output_len += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) };
*value = *amount;
output_len += size_of::<u64>();
}
Self::CloseAccount => {
output[output_len] = 9;
output_len += size_of::<u8>();
}
Self::FreezeAccount => {
output[output_len] = 10;
output_len += size_of::<u8>();
}
Self::ThawAccount => {
output[output_len] = 11;
output_len += size_of::<u8>();
}
Self::Transfer2 { amount, decimals } => {
output[output_len] = 12;
output_len += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) };
*value = *amount;
output_len += size_of::<u64>();
let value = unsafe { &mut *(&mut output[output_len] as *mut u8) };
*value = *decimals;
output_len += size_of::<u8>();
}
Self::Approve2 { amount, decimals } => {
output[output_len] = 13;
output_len += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) };
*value = *amount;
output_len += size_of::<u64>();
let value = unsafe { &mut *(&mut output[output_len] as *mut u8) };
*value = *decimals;
output_len += size_of::<u8>();
}
Self::MintTo2 { amount, decimals } => {
output[output_len] = 14;
output_len += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) };
*value = *amount;
output_len += size_of::<u64>();
let value = unsafe { &mut *(&mut output[output_len] as *mut u8) };
*value = *decimals;
output_len += size_of::<u8>();
}
Self::Burn2 { amount, decimals } => {
output[output_len] = 15;
output_len += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) };
*value = *amount;
output_len += size_of::<u64>();
let value = unsafe { &mut *(&mut output[output_len] as *mut u8) };
*value = *decimals;
output_len += size_of::<u8>();
}
}
output.truncate(output_len);
Ok(output)
}
}
#[repr(u8)]
#[derive(Clone, Debug, PartialEq)]
pub enum AuthorityType {
MintTokens,
FreezeAccount,
AccountHolder,
CloseAccount,
}
impl AuthorityType {
fn into(&self) -> u8 {
match self {
AuthorityType::MintTokens => 0,
AuthorityType::FreezeAccount => 1,
AuthorityType::AccountHolder => 2,
AuthorityType::CloseAccount => 3,
}
}
fn from(index: u8) -> Result<Self, ProgramError> {
match index {
0 => Ok(AuthorityType::MintTokens),
1 => Ok(AuthorityType::FreezeAccount),
2 => Ok(AuthorityType::AccountHolder),
3 => Ok(AuthorityType::CloseAccount),
_ => Err(TokenError::InvalidInstruction.into()),
}
}
}
pub fn initialize_mint(
token_program_id: &Pubkey,
mint_pubkey: &Pubkey,
mint_authority_pubkey: &Pubkey,
freeze_authority_pubkey: Option<&Pubkey>,
decimals: u8,
) -> Result<Instruction, ProgramError> {
let freeze_authority = freeze_authority_pubkey.cloned().into();
let data = TokenInstruction::InitializeMint {
mint_authority: *mint_authority_pubkey,
freeze_authority,
decimals,
}
.pack()?;
let accounts = vec![
AccountMeta::new(*mint_pubkey, false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];
Ok(Instruction {
program_id: *token_program_id,
accounts,
data,
})
}
pub fn initialize_account(
token_program_id: &Pubkey,
account_pubkey: &Pubkey,
mint_pubkey: &Pubkey,
owner_pubkey: &Pubkey,
) -> Result<Instruction, ProgramError> {
let data = TokenInstruction::InitializeAccount.pack()?;
let accounts = vec![
AccountMeta::new(*account_pubkey, false),
AccountMeta::new_readonly(*mint_pubkey, false),
AccountMeta::new_readonly(*owner_pubkey, false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];
Ok(Instruction {
program_id: *token_program_id,
accounts,
data,
})
}
pub fn initialize_multisig(
token_program_id: &Pubkey,
multisig_pubkey: &Pubkey,
signer_pubkeys: &[&Pubkey],
m: u8,
) -> Result<Instruction, ProgramError> {
if !is_valid_signer_index(m as usize)
|| !is_valid_signer_index(signer_pubkeys.len())
|| m as usize > signer_pubkeys.len()
{
return Err(ProgramError::MissingRequiredSignature);
}
let data = TokenInstruction::InitializeMultisig { m }.pack()?;
let mut accounts = Vec::with_capacity(1 + 1 + signer_pubkeys.len());
accounts.push(AccountMeta::new(*multisig_pubkey, false));
accounts.push(AccountMeta::new_readonly(sysvar::rent::id(), false));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(AccountMeta::new_readonly(**signer_pubkey, false));
}
Ok(Instruction {
program_id: *token_program_id,
accounts,
data,
})
}
pub fn transfer(
token_program_id: &Pubkey,
source_pubkey: &Pubkey,
destination_pubkey: &Pubkey,
authority_pubkey: &Pubkey,
signer_pubkeys: &[&Pubkey],
amount: u64,
) -> Result<Instruction, ProgramError> {
let data = TokenInstruction::Transfer { amount }.pack()?;
let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
accounts.push(AccountMeta::new(*source_pubkey, false));
accounts.push(AccountMeta::new(*destination_pubkey, false));
accounts.push(AccountMeta::new_readonly(
*authority_pubkey,
signer_pubkeys.is_empty(),
));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(AccountMeta::new(**signer_pubkey, true));
}
Ok(Instruction {
program_id: *token_program_id,
accounts,
data,
})
}
pub fn approve(
token_program_id: &Pubkey,
source_pubkey: &Pubkey,
delegate_pubkey: &Pubkey,
owner_pubkey: &Pubkey,
signer_pubkeys: &[&Pubkey],
amount: u64,
) -> Result<Instruction, ProgramError> {
let data = TokenInstruction::Approve { amount }.pack()?;
let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
accounts.push(AccountMeta::new(*source_pubkey, false));
accounts.push(AccountMeta::new_readonly(*delegate_pubkey, false));
accounts.push(AccountMeta::new_readonly(
*owner_pubkey,
signer_pubkeys.is_empty(),
));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(AccountMeta::new(**signer_pubkey, true));
}
Ok(Instruction {
program_id: *token_program_id,
accounts,
data,
})
}
pub fn revoke(
token_program_id: &Pubkey,
source_pubkey: &Pubkey,
owner_pubkey: &Pubkey,
signer_pubkeys: &[&Pubkey],
) -> Result<Instruction, ProgramError> {
let data = TokenInstruction::Revoke.pack()?;
let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len());
accounts.push(AccountMeta::new_readonly(*source_pubkey, false));
accounts.push(AccountMeta::new_readonly(
*owner_pubkey,
signer_pubkeys.is_empty(),
));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(AccountMeta::new(**signer_pubkey, true));
}
Ok(Instruction {
program_id: *token_program_id,
accounts,
data,
})
}
pub fn set_authority(
token_program_id: &Pubkey,
owned_pubkey: &Pubkey,
new_authority_pubkey: Option<&Pubkey>,
authority_type: AuthorityType,
owner_pubkey: &Pubkey,
signer_pubkeys: &[&Pubkey],
) -> Result<Instruction, ProgramError> {
let new_authority = new_authority_pubkey.cloned().into();
let data = TokenInstruction::SetAuthority {
authority_type,
new_authority,
}
.pack()?;
let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
accounts.push(AccountMeta::new(*owned_pubkey, false));
accounts.push(AccountMeta::new_readonly(
*owner_pubkey,
signer_pubkeys.is_empty(),
));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(AccountMeta::new(**signer_pubkey, true));
}
Ok(Instruction {
program_id: *token_program_id,
accounts,
data,
})
}
pub fn mint_to(
token_program_id: &Pubkey,
mint_pubkey: &Pubkey,
account_pubkey: &Pubkey,
owner_pubkey: &Pubkey,
signer_pubkeys: &[&Pubkey],
amount: u64,
) -> Result<Instruction, ProgramError> {
let data = TokenInstruction::MintTo { amount }.pack()?;
let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
accounts.push(AccountMeta::new(*mint_pubkey, false));
accounts.push(AccountMeta::new(*account_pubkey, false));
accounts.push(AccountMeta::new_readonly(
*owner_pubkey,
signer_pubkeys.is_empty(),
));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(AccountMeta::new(**signer_pubkey, true));
}
Ok(Instruction {
program_id: *token_program_id,
accounts,
data,
})
}
pub fn burn(
token_program_id: &Pubkey,
account_pubkey: &Pubkey,
mint_pubkey: &Pubkey,
authority_pubkey: &Pubkey,
signer_pubkeys: &[&Pubkey],
amount: u64,
) -> Result<Instruction, ProgramError> {
let data = TokenInstruction::Burn { amount }.pack()?;
let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
accounts.push(AccountMeta::new(*account_pubkey, false));
accounts.push(AccountMeta::new(*mint_pubkey, false));
accounts.push(AccountMeta::new_readonly(
*authority_pubkey,
signer_pubkeys.is_empty(),
));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(AccountMeta::new(**signer_pubkey, true));
}
Ok(Instruction {
program_id: *token_program_id,
accounts,
data,
})
}
pub fn close_account(
token_program_id: &Pubkey,
account_pubkey: &Pubkey,
destination_pubkey: &Pubkey,
owner_pubkey: &Pubkey,
signer_pubkeys: &[&Pubkey],
) -> Result<Instruction, ProgramError> {
let data = TokenInstruction::CloseAccount.pack()?;
let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
accounts.push(AccountMeta::new(*account_pubkey, false));
accounts.push(AccountMeta::new(*destination_pubkey, false));
accounts.push(AccountMeta::new_readonly(
*owner_pubkey,
signer_pubkeys.is_empty(),
));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(AccountMeta::new(**signer_pubkey, true));
}
Ok(Instruction {
program_id: *token_program_id,
accounts,
data,
})
}
pub fn freeze_account(
token_program_id: &Pubkey,
account_pubkey: &Pubkey,
mint_pubkey: &Pubkey,
owner_pubkey: &Pubkey,
signer_pubkeys: &[&Pubkey],
) -> Result<Instruction, ProgramError> {
let data = TokenInstruction::FreezeAccount.pack()?;
let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
accounts.push(AccountMeta::new(*account_pubkey, false));
accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
accounts.push(AccountMeta::new_readonly(
*owner_pubkey,
signer_pubkeys.is_empty(),
));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(AccountMeta::new(**signer_pubkey, true));
}
Ok(Instruction {
program_id: *token_program_id,
accounts,
data,
})
}
pub fn thaw_account(
token_program_id: &Pubkey,
account_pubkey: &Pubkey,
mint_pubkey: &Pubkey,
owner_pubkey: &Pubkey,
signer_pubkeys: &[&Pubkey],
) -> Result<Instruction, ProgramError> {
let data = TokenInstruction::ThawAccount.pack()?;
let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
accounts.push(AccountMeta::new(*account_pubkey, false));
accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
accounts.push(AccountMeta::new_readonly(
*owner_pubkey,
signer_pubkeys.is_empty(),
));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(AccountMeta::new(**signer_pubkey, true));
}
Ok(Instruction {
program_id: *token_program_id,
accounts,
data,
})
}
#[allow(clippy::too_many_arguments)]
pub fn transfer2(
token_program_id: &Pubkey,
source_pubkey: &Pubkey,
mint_pubkey: &Pubkey,
destination_pubkey: &Pubkey,
authority_pubkey: &Pubkey,
signer_pubkeys: &[&Pubkey],
amount: u64,
decimals: u8,
) -> Result<Instruction, ProgramError> {
let data = TokenInstruction::Transfer2 { amount, decimals }.pack()?;
let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len());
accounts.push(AccountMeta::new(*source_pubkey, false));
accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
accounts.push(AccountMeta::new(*destination_pubkey, false));
accounts.push(AccountMeta::new_readonly(
*authority_pubkey,
signer_pubkeys.is_empty(),
));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(AccountMeta::new(**signer_pubkey, true));
}
Ok(Instruction {
program_id: *token_program_id,
accounts,
data,
})
}
#[allow(clippy::too_many_arguments)]
pub fn approve2(
token_program_id: &Pubkey,
source_pubkey: &Pubkey,
mint_pubkey: &Pubkey,
delegate_pubkey: &Pubkey,
owner_pubkey: &Pubkey,
signer_pubkeys: &[&Pubkey],
amount: u64,
decimals: u8,
) -> Result<Instruction, ProgramError> {
let data = TokenInstruction::Approve2 { amount, decimals }.pack()?;
let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len());
accounts.push(AccountMeta::new(*source_pubkey, false));
accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
accounts.push(AccountMeta::new_readonly(*delegate_pubkey, false));
accounts.push(AccountMeta::new_readonly(
*owner_pubkey,
signer_pubkeys.is_empty(),
));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(AccountMeta::new(**signer_pubkey, true));
}
Ok(Instruction {
program_id: *token_program_id,
accounts,
data,
})
}
pub fn mint_to2(
token_program_id: &Pubkey,
mint_pubkey: &Pubkey,
account_pubkey: &Pubkey,
owner_pubkey: &Pubkey,
signer_pubkeys: &[&Pubkey],
amount: u64,
decimals: u8,
) -> Result<Instruction, ProgramError> {
let data = TokenInstruction::MintTo2 { amount, decimals }.pack()?;
let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
accounts.push(AccountMeta::new(*mint_pubkey, false));
accounts.push(AccountMeta::new(*account_pubkey, false));
accounts.push(AccountMeta::new_readonly(
*owner_pubkey,
signer_pubkeys.is_empty(),
));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(AccountMeta::new(**signer_pubkey, true));
}
Ok(Instruction {
program_id: *token_program_id,
accounts,
data,
})
}
pub fn burn2(
token_program_id: &Pubkey,
account_pubkey: &Pubkey,
mint_pubkey: &Pubkey,
authority_pubkey: &Pubkey,
signer_pubkeys: &[&Pubkey],
amount: u64,
decimals: u8,
) -> Result<Instruction, ProgramError> {
let data = TokenInstruction::Burn2 { amount, decimals }.pack()?;
let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
accounts.push(AccountMeta::new(*account_pubkey, false));
accounts.push(AccountMeta::new(*mint_pubkey, false));
accounts.push(AccountMeta::new_readonly(
*authority_pubkey,
signer_pubkeys.is_empty(),
));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(AccountMeta::new(**signer_pubkey, true));
}
Ok(Instruction {
program_id: *token_program_id,
accounts,
data,
})
}
pub fn is_valid_signer_index(index: usize) -> bool {
!(index < MIN_SIGNERS || index > MAX_SIGNERS)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_instruction_packing() {
let check = TokenInstruction::InitializeMint {
decimals: 2,
mint_authority: Pubkey::new(&[1u8; 32]),
freeze_authority: COption::None,
};
let packed = check.pack().unwrap();
let mut expect = Vec::from([0u8, 2]);
expect.extend_from_slice(&[1u8; 32]);
expect.extend_from_slice(&[0]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
let check = TokenInstruction::InitializeMint {
decimals: 2,
mint_authority: Pubkey::new(&[2u8; 32]),
freeze_authority: COption::Some(Pubkey::new(&[3u8; 32])),
};
let packed = check.pack().unwrap();
let mut expect = vec![0u8, 2];
expect.extend_from_slice(&[2u8; 32]);
expect.extend_from_slice(&[1]);
expect.extend_from_slice(&[3u8; 32]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
let check = TokenInstruction::InitializeAccount;
let packed = check.pack().unwrap();
let expect = Vec::from([1u8]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
let check = TokenInstruction::InitializeMultisig { m: 1 };
let packed = check.pack().unwrap();
let expect = Vec::from([2u8, 1]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
let check = TokenInstruction::Transfer { amount: 1 };
let packed = check.pack().unwrap();
let expect = Vec::from([3u8, 1, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
let check = TokenInstruction::Approve { amount: 1 };
let packed = check.pack().unwrap();
let expect = Vec::from([4u8, 1, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
let check = TokenInstruction::Revoke;
let packed = check.pack().unwrap();
let expect = Vec::from([5u8]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
let check = TokenInstruction::SetAuthority {
authority_type: AuthorityType::FreezeAccount,
new_authority: COption::Some(Pubkey::new(&[4u8; 32])),
};
let packed = check.pack().unwrap();
let mut expect = Vec::from([6u8, 1]);
expect.extend_from_slice(&[1]);
expect.extend_from_slice(&[4u8; 32]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
let check = TokenInstruction::MintTo { amount: 1 };
let packed = check.pack().unwrap();
let expect = Vec::from([7u8, 1, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
let check = TokenInstruction::Burn { amount: 1 };
let packed = check.pack().unwrap();
let expect = Vec::from([8u8, 1, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
let check = TokenInstruction::CloseAccount;
let packed = check.pack().unwrap();
let expect = Vec::from([9u8]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
let check = TokenInstruction::FreezeAccount;
let packed = check.pack().unwrap();
let expect = Vec::from([10u8]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
let check = TokenInstruction::ThawAccount;
let packed = check.pack().unwrap();
let expect = Vec::from([11u8]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
let check = TokenInstruction::Transfer2 {
amount: 1,
decimals: 2,
};
let packed = check.pack().unwrap();
let expect = Vec::from([12u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
let check = TokenInstruction::Approve2 {
amount: 1,
decimals: 2,
};
let packed = check.pack().unwrap();
let expect = Vec::from([13u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
let check = TokenInstruction::MintTo2 {
amount: 1,
decimals: 2,
};
let packed = check.pack().unwrap();
let expect = Vec::from([14u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
let check = TokenInstruction::Burn2 {
amount: 1,
decimals: 2,
};
let packed = check.pack().unwrap();
let expect = Vec::from([15u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
assert_eq!(packed, expect);
let unpacked = TokenInstruction::unpack(&expect).unwrap();
assert_eq!(unpacked, check);
}
}