use {
crate::{
check_program_account,
error::TokenError,
extension::{
confidential_transfer::{self, ConfidentialTransferAccount, ConfidentialTransferMint},
confidential_transfer_fee::{
self, ConfidentialTransferFeeAmount, ConfidentialTransferFeeConfig,
},
cpi_guard::{self, in_cpi, CpiGuard},
default_account_state::{self, DefaultAccountState},
group_member_pointer::{self, GroupMemberPointer},
group_pointer::{self, GroupPointer},
immutable_owner::ImmutableOwner,
interest_bearing_mint::{self, InterestBearingConfig},
memo_transfer::{self, check_previous_sibling_instruction_is_memo, memo_required},
metadata_pointer::{self, MetadataPointer},
mint_close_authority::MintCloseAuthority,
non_transferable::{NonTransferable, NonTransferableAccount},
permanent_delegate::{get_permanent_delegate, PermanentDelegate},
reallocate, token_group, token_metadata,
transfer_fee::{self, TransferFeeAmount, TransferFeeConfig},
transfer_hook::{self, TransferHook, TransferHookAccount},
AccountType, BaseStateWithExtensions, BaseStateWithExtensionsMut, ExtensionType,
PodStateWithExtensions, PodStateWithExtensionsMut,
},
instruction::{
decode_instruction_data, decode_instruction_type, is_valid_signer_index, AuthorityType,
MAX_SIGNERS,
},
native_mint,
pod::{PodAccount, PodCOption, PodMint, PodMultisig},
pod_instruction::{
decode_instruction_data_with_coption_pubkey, AmountCheckedData, AmountData,
InitializeMintData, InitializeMultisigData, PodTokenInstruction, SetAuthorityData,
},
state::{Account, AccountState, Mint, PackedSizeOf},
},
solana_program::{
account_info::{next_account_info, AccountInfo},
clock::Clock,
entrypoint::ProgramResult,
msg,
program::{invoke, invoke_signed, set_return_data},
program_error::ProgramError,
program_pack::Pack,
pubkey::Pubkey,
system_instruction, system_program,
sysvar::{rent::Rent, Sysvar},
},
spl_pod::{
bytemuck::{pod_from_bytes, pod_from_bytes_mut},
primitives::{PodBool, PodU64},
},
spl_token_group_interface::instruction::TokenGroupInstruction,
spl_token_metadata_interface::instruction::TokenMetadataInstruction,
std::convert::{TryFrom, TryInto},
};
pub struct Processor {}
impl Processor {
fn _process_initialize_mint(
accounts: &[AccountInfo],
decimals: u8,
mint_authority: &Pubkey,
freeze_authority: PodCOption<Pubkey>,
rent_sysvar_account: bool,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let mint_info = next_account_info(account_info_iter)?;
let mint_data_len = mint_info.data_len();
let mut mint_data = mint_info.data.borrow_mut();
let rent = if rent_sysvar_account {
Rent::from_account_info(next_account_info(account_info_iter)?)?
} else {
Rent::get()?
};
if !rent.is_exempt(mint_info.lamports(), mint_data_len) {
return Err(TokenError::NotRentExempt.into());
}
let mut mint = PodStateWithExtensionsMut::<PodMint>::unpack_uninitialized(&mut mint_data)?;
let extension_types = mint.get_extension_types()?;
if ExtensionType::try_calculate_account_len::<Mint>(&extension_types)? != mint_data_len {
return Err(ProgramError::InvalidAccountData);
}
ExtensionType::check_for_invalid_mint_extension_combinations(&extension_types)?;
if let Ok(default_account_state) = mint.get_extension_mut::<DefaultAccountState>() {
let default_account_state = AccountState::try_from(default_account_state.state)
.or(Err(ProgramError::InvalidAccountData))?;
if default_account_state == AccountState::Frozen && freeze_authority.is_none() {
return Err(TokenError::MintCannotFreeze.into());
}
}
mint.base.mint_authority = PodCOption::some(*mint_authority);
mint.base.decimals = decimals;
mint.base.is_initialized = PodBool::from_bool(true);
mint.base.freeze_authority = freeze_authority;
mint.init_account_type()?;
Ok(())
}
pub fn process_initialize_mint(
accounts: &[AccountInfo],
decimals: u8,
mint_authority: &Pubkey,
freeze_authority: PodCOption<Pubkey>,
) -> ProgramResult {
Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority, true)
}
pub fn process_initialize_mint2(
accounts: &[AccountInfo],
decimals: u8,
mint_authority: &Pubkey,
freeze_authority: PodCOption<Pubkey>,
) -> ProgramResult {
Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority, false)
}
fn _process_initialize_account(
accounts: &[AccountInfo],
owner: Option<&Pubkey>,
rent_sysvar_account: bool,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let new_account_info = next_account_info(account_info_iter)?;
let mint_info = next_account_info(account_info_iter)?;
let owner = if let Some(owner) = owner {
owner
} else {
next_account_info(account_info_iter)?.key
};
let new_account_info_data_len = new_account_info.data_len();
let rent = if rent_sysvar_account {
Rent::from_account_info(next_account_info(account_info_iter)?)?
} else {
Rent::get()?
};
let mut account_data = new_account_info.data.borrow_mut();
let mut account =
PodStateWithExtensionsMut::<PodAccount>::unpack_uninitialized(&mut account_data)?;
if !rent.is_exempt(new_account_info.lamports(), new_account_info_data_len) {
return Err(TokenError::NotRentExempt.into());
}
let mint_data = mint_info.data.borrow();
let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data)
.map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
if mint
.get_extension::<PermanentDelegate>()
.map(|e| Option::<Pubkey>::from(e.delegate).is_some())
.unwrap_or(false)
{
msg!("Warning: Mint has a permanent delegate, so tokens in this account may be seized at any time");
}
let required_extensions =
Self::get_required_account_extensions_from_unpacked_mint(mint_info.owner, &mint)?;
if ExtensionType::try_calculate_account_len::<Account>(&required_extensions)?
> new_account_info_data_len
{
return Err(ProgramError::InvalidAccountData);
}
for extension in required_extensions {
account.init_account_extension_from_type(extension)?;
}
let starting_state =
if let Ok(default_account_state) = mint.get_extension::<DefaultAccountState>() {
AccountState::try_from(default_account_state.state)
.or(Err(ProgramError::InvalidAccountData))?
} else {
AccountState::Initialized
};
account.base.mint = *mint_info.key;
account.base.owner = *owner;
account.base.close_authority = PodCOption::none();
account.base.delegate = PodCOption::none();
account.base.delegated_amount = 0.into();
account.base.state = starting_state.into();
if mint_info.key == &native_mint::id() {
let rent_exempt_reserve = rent.minimum_balance(new_account_info_data_len);
account.base.is_native = PodCOption::some(rent_exempt_reserve.into());
account.base.amount = new_account_info
.lamports()
.checked_sub(rent_exempt_reserve)
.ok_or(TokenError::Overflow)?
.into();
} else {
account.base.is_native = PodCOption::none();
account.base.amount = 0.into();
};
account.init_account_type()?;
Ok(())
}
pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult {
Self::_process_initialize_account(accounts, None, true)
}
pub fn process_initialize_account2(accounts: &[AccountInfo], owner: &Pubkey) -> ProgramResult {
Self::_process_initialize_account(accounts, Some(owner), true)
}
pub fn process_initialize_account3(accounts: &[AccountInfo], owner: &Pubkey) -> ProgramResult {
Self::_process_initialize_account(accounts, Some(owner), false)
}
fn _process_initialize_multisig(
accounts: &[AccountInfo],
m: u8,
rent_sysvar_account: bool,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let multisig_info = next_account_info(account_info_iter)?;
let multisig_info_data_len = multisig_info.data_len();
let rent = if rent_sysvar_account {
Rent::from_account_info(next_account_info(account_info_iter)?)?
} else {
Rent::get()?
};
let mut multisig_data = multisig_info.data.borrow_mut();
let multisig = pod_from_bytes_mut::<PodMultisig>(&mut multisig_data)?;
if bool::from(multisig.is_initialized) {
return Err(TokenError::AlreadyInUse.into());
}
if !rent.is_exempt(multisig_info.lamports(), multisig_info_data_len) {
return Err(TokenError::NotRentExempt.into());
}
let signer_infos = account_info_iter.as_slice();
multisig.m = m;
multisig.n = signer_infos.len() as u8;
if !is_valid_signer_index(multisig.n as usize) {
return Err(TokenError::InvalidNumberOfProvidedSigners.into());
}
if !is_valid_signer_index(multisig.m as usize) {
return Err(TokenError::InvalidNumberOfRequiredSigners.into());
}
for (i, signer_info) in signer_infos.iter().enumerate() {
multisig.signers[i] = *signer_info.key;
}
multisig.is_initialized = true.into();
Ok(())
}
pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult {
Self::_process_initialize_multisig(accounts, m, true)
}
pub fn process_initialize_multisig2(accounts: &[AccountInfo], m: u8) -> ProgramResult {
Self::_process_initialize_multisig(accounts, m, false)
}
pub fn process_transfer(
program_id: &Pubkey,
accounts: &[AccountInfo],
amount: u64,
expected_decimals: Option<u8>,
expected_fee: Option<u64>,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let source_account_info = next_account_info(account_info_iter)?;
let expected_mint_info = if let Some(expected_decimals) = expected_decimals {
Some((next_account_info(account_info_iter)?, expected_decimals))
} else {
assert!(expected_fee.is_none());
None
};
let destination_account_info = next_account_info(account_info_iter)?;
let authority_info = next_account_info(account_info_iter)?;
let authority_info_data_len = authority_info.data_len();
let mut source_account_data = source_account_info.data.borrow_mut();
let mut source_account =
PodStateWithExtensionsMut::<PodAccount>::unpack(&mut source_account_data)?;
if source_account.base.is_frozen() {
return Err(TokenError::AccountFrozen.into());
}
let source_amount = u64::from(source_account.base.amount);
if source_amount < amount {
return Err(TokenError::InsufficientFunds.into());
}
if source_account
.get_extension::<NonTransferableAccount>()
.is_ok()
{
return Err(TokenError::NonTransferable.into());
}
let (fee, maybe_permanent_delegate, maybe_transfer_hook_program_id) =
if let Some((mint_info, expected_decimals)) = expected_mint_info {
if &source_account.base.mint != mint_info.key {
return Err(TokenError::MintMismatch.into());
}
let mint_data = mint_info.try_borrow_data()?;
let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data)?;
if expected_decimals != mint.base.decimals {
return Err(TokenError::MintDecimalsMismatch.into());
}
let fee = if let Ok(transfer_fee_config) = mint.get_extension::<TransferFeeConfig>()
{
transfer_fee_config
.calculate_epoch_fee(Clock::get()?.epoch, amount)
.ok_or(TokenError::Overflow)?
} else {
0
};
let maybe_permanent_delegate = get_permanent_delegate(&mint);
let maybe_transfer_hook_program_id = transfer_hook::get_program_id(&mint);
(
fee,
maybe_permanent_delegate,
maybe_transfer_hook_program_id,
)
} else {
if source_account
.get_extension::<TransferHookAccount>()
.is_ok()
{
return Err(TokenError::MintRequiredForTransfer.into());
}
if source_account
.get_extension_mut::<TransferFeeAmount>()
.is_ok()
{
return Err(TokenError::MintRequiredForTransfer.into());
} else {
(0, None, None)
}
};
if let Some(expected_fee) = expected_fee {
if expected_fee != fee {
msg!("Calculated fee {}, received {}", fee, expected_fee);
return Err(TokenError::FeeMismatch.into());
}
}
let self_transfer = source_account_info.key == destination_account_info.key;
if let Ok(cpi_guard) = source_account.get_extension::<CpiGuard>() {
if *authority_info.key == source_account.base.owner
&& cpi_guard.lock_cpi.into()
&& in_cpi()
{
return Err(TokenError::CpiGuardTransferBlocked.into());
}
}
match (source_account.base.delegate, maybe_permanent_delegate) {
(_, Some(ref delegate)) if authority_info.key == delegate => Self::validate_owner(
program_id,
delegate,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?,
(
PodCOption {
option: PodCOption::<Pubkey>::SOME,
value: delegate,
},
_,
) if authority_info.key == &delegate => {
Self::validate_owner(
program_id,
&delegate,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
let delegated_amount = u64::from(source_account.base.delegated_amount);
if delegated_amount < amount {
return Err(TokenError::InsufficientFunds.into());
}
if !self_transfer {
source_account.base.delegated_amount = delegated_amount
.checked_sub(amount)
.ok_or(TokenError::Overflow)?
.into();
if u64::from(source_account.base.delegated_amount) == 0 {
source_account.base.delegate = PodCOption::none();
}
}
}
_ => {
Self::validate_owner(
program_id,
&source_account.base.owner,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
}
}
check_program_account(source_account_info.owner)?;
check_program_account(destination_account_info.owner)?;
if self_transfer {
if memo_required(&source_account) {
check_previous_sibling_instruction_is_memo()?;
}
return Ok(());
}
let mut destination_account_data = destination_account_info.data.borrow_mut();
let mut destination_account =
PodStateWithExtensionsMut::<PodAccount>::unpack(&mut destination_account_data)?;
if destination_account.base.is_frozen() {
return Err(TokenError::AccountFrozen.into());
}
if source_account.base.mint != destination_account.base.mint {
return Err(TokenError::MintMismatch.into());
}
if memo_required(&destination_account) {
check_previous_sibling_instruction_is_memo()?;
}
if let Ok(confidential_transfer_state) =
destination_account.get_extension::<ConfidentialTransferAccount>()
{
confidential_transfer_state.non_confidential_transfer_allowed()?
}
source_account.base.amount = source_amount
.checked_sub(amount)
.ok_or(TokenError::Overflow)?
.into();
let credited_amount = amount.checked_sub(fee).ok_or(TokenError::Overflow)?;
destination_account.base.amount = u64::from(destination_account.base.amount)
.checked_add(credited_amount)
.ok_or(TokenError::Overflow)?
.into();
if fee > 0 {
if let Ok(extension) = destination_account.get_extension_mut::<TransferFeeAmount>() {
let new_withheld_amount = u64::from(extension.withheld_amount)
.checked_add(fee)
.ok_or(TokenError::Overflow)?;
extension.withheld_amount = new_withheld_amount.into();
} else {
return Err(TokenError::InvalidState.into());
}
}
if source_account.base.is_native() {
let source_starting_lamports = source_account_info.lamports();
**source_account_info.lamports.borrow_mut() = source_starting_lamports
.checked_sub(amount)
.ok_or(TokenError::Overflow)?;
let destination_starting_lamports = destination_account_info.lamports();
**destination_account_info.lamports.borrow_mut() = destination_starting_lamports
.checked_add(amount)
.ok_or(TokenError::Overflow)?;
}
if let Some(program_id) = maybe_transfer_hook_program_id {
if let Some((mint_info, _)) = expected_mint_info {
transfer_hook::set_transferring(&mut source_account)?;
transfer_hook::set_transferring(&mut destination_account)?;
drop(source_account_data);
drop(destination_account_data);
spl_transfer_hook_interface::onchain::invoke_execute(
&program_id,
source_account_info.clone(),
mint_info.clone(),
destination_account_info.clone(),
authority_info.clone(),
account_info_iter.as_slice(),
amount,
)?;
transfer_hook::unset_transferring(source_account_info)?;
transfer_hook::unset_transferring(destination_account_info)?;
} else {
return Err(TokenError::MintRequiredForTransfer.into());
}
}
Ok(())
}
pub fn process_approve(
program_id: &Pubkey,
accounts: &[AccountInfo],
amount: u64,
expected_decimals: Option<u8>,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let source_account_info = next_account_info(account_info_iter)?;
let expected_mint_info = if let Some(expected_decimals) = expected_decimals {
Some((next_account_info(account_info_iter)?, expected_decimals))
} else {
None
};
let delegate_info = next_account_info(account_info_iter)?;
let owner_info = next_account_info(account_info_iter)?;
let owner_info_data_len = owner_info.data_len();
let mut source_account_data = source_account_info.data.borrow_mut();
let source_account =
PodStateWithExtensionsMut::<PodAccount>::unpack(&mut source_account_data)?;
if source_account.base.is_frozen() {
return Err(TokenError::AccountFrozen.into());
}
if let Some((mint_info, expected_decimals)) = expected_mint_info {
if &source_account.base.mint != mint_info.key {
return Err(TokenError::MintMismatch.into());
}
let mint_data = mint_info.data.borrow();
let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data)?;
if expected_decimals != mint.base.decimals {
return Err(TokenError::MintDecimalsMismatch.into());
}
}
Self::validate_owner(
program_id,
&source_account.base.owner,
owner_info,
owner_info_data_len,
account_info_iter.as_slice(),
)?;
if let Ok(cpi_guard) = source_account.get_extension::<CpiGuard>() {
if cpi_guard.lock_cpi.into() && in_cpi() {
return Err(TokenError::CpiGuardApproveBlocked.into());
}
}
source_account.base.delegate = PodCOption::some(*delegate_info.key);
source_account.base.delegated_amount = amount.into();
Ok(())
}
pub fn process_revoke(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let source_account_info = next_account_info(account_info_iter)?;
let authority_info = next_account_info(account_info_iter)?;
let authority_info_data_len = authority_info.data_len();
let mut source_account_data = source_account_info.data.borrow_mut();
let source_account =
PodStateWithExtensionsMut::<PodAccount>::unpack(&mut source_account_data)?;
if source_account.base.is_frozen() {
return Err(TokenError::AccountFrozen.into());
}
Self::validate_owner(
program_id,
match &source_account.base.delegate {
PodCOption {
option: PodCOption::<Pubkey>::SOME,
value: delegate,
} if authority_info.key == delegate => delegate,
_ => &source_account.base.owner,
},
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
source_account.base.delegate = PodCOption::none();
source_account.base.delegated_amount = 0.into();
Ok(())
}
pub fn process_set_authority(
program_id: &Pubkey,
accounts: &[AccountInfo],
authority_type: AuthorityType,
new_authority: PodCOption<Pubkey>,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let account_info = next_account_info(account_info_iter)?;
let authority_info = next_account_info(account_info_iter)?;
let authority_info_data_len = authority_info.data_len();
let mut account_data = account_info.data.borrow_mut();
if let Ok(mut account) = PodStateWithExtensionsMut::<PodAccount>::unpack(&mut account_data)
{
if account.base.is_frozen() {
return Err(TokenError::AccountFrozen.into());
}
match authority_type {
AuthorityType::AccountOwner => {
Self::validate_owner(
program_id,
&account.base.owner,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
if account.get_extension_mut::<ImmutableOwner>().is_ok() {
return Err(TokenError::ImmutableOwner.into());
}
if let Ok(cpi_guard) = account.get_extension::<CpiGuard>() {
if cpi_guard.lock_cpi.into() && in_cpi() {
return Err(TokenError::CpiGuardSetAuthorityBlocked.into());
} else if cpi_guard.lock_cpi.into() {
return Err(TokenError::CpiGuardOwnerChangeBlocked.into());
}
}
if let PodCOption {
option: PodCOption::<Pubkey>::SOME,
value: authority,
} = new_authority
{
account.base.owner = authority;
} else {
return Err(TokenError::InvalidInstruction.into());
}
account.base.delegate = PodCOption::none();
account.base.delegated_amount = 0.into();
if account.base.is_native() {
account.base.close_authority = PodCOption::none();
}
}
AuthorityType::CloseAccount => {
let authority = account.base.close_authority.unwrap_or(account.base.owner);
Self::validate_owner(
program_id,
&authority,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
if let Ok(cpi_guard) = account.get_extension::<CpiGuard>() {
if cpi_guard.lock_cpi.into() && in_cpi() && new_authority.is_some() {
return Err(TokenError::CpiGuardSetAuthorityBlocked.into());
}
}
account.base.close_authority = new_authority;
}
_ => {
return Err(TokenError::AuthorityTypeNotSupported.into());
}
}
} else if let Ok(mut mint) = PodStateWithExtensionsMut::<PodMint>::unpack(&mut account_data)
{
match authority_type {
AuthorityType::MintTokens => {
let mint_authority = mint
.base
.mint_authority
.ok_or(Into::<ProgramError>::into(TokenError::FixedSupply))?;
Self::validate_owner(
program_id,
&mint_authority,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
mint.base.mint_authority = new_authority;
}
AuthorityType::FreezeAccount => {
let freeze_authority = mint
.base
.freeze_authority
.ok_or(Into::<ProgramError>::into(TokenError::MintCannotFreeze))?;
Self::validate_owner(
program_id,
&freeze_authority,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
mint.base.freeze_authority = new_authority;
}
AuthorityType::CloseMint => {
let extension = mint.get_extension_mut::<MintCloseAuthority>()?;
let maybe_close_authority: Option<Pubkey> = extension.close_authority.into();
let close_authority =
maybe_close_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
Self::validate_owner(
program_id,
&close_authority,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
extension.close_authority = new_authority.try_into()?;
}
AuthorityType::TransferFeeConfig => {
let extension = mint.get_extension_mut::<TransferFeeConfig>()?;
let maybe_transfer_fee_config_authority: Option<Pubkey> =
extension.transfer_fee_config_authority.into();
let transfer_fee_config_authority = maybe_transfer_fee_config_authority
.ok_or(TokenError::AuthorityTypeNotSupported)?;
Self::validate_owner(
program_id,
&transfer_fee_config_authority,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
extension.transfer_fee_config_authority = new_authority.try_into()?;
}
AuthorityType::WithheldWithdraw => {
let extension = mint.get_extension_mut::<TransferFeeConfig>()?;
let maybe_withdraw_withheld_authority: Option<Pubkey> =
extension.withdraw_withheld_authority.into();
let withdraw_withheld_authority = maybe_withdraw_withheld_authority
.ok_or(TokenError::AuthorityTypeNotSupported)?;
Self::validate_owner(
program_id,
&withdraw_withheld_authority,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
extension.withdraw_withheld_authority = new_authority.try_into()?;
}
AuthorityType::InterestRate => {
let extension = mint.get_extension_mut::<InterestBearingConfig>()?;
let maybe_rate_authority: Option<Pubkey> = extension.rate_authority.into();
let rate_authority =
maybe_rate_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
Self::validate_owner(
program_id,
&rate_authority,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
extension.rate_authority = new_authority.try_into()?;
}
AuthorityType::PermanentDelegate => {
let extension = mint.get_extension_mut::<PermanentDelegate>()?;
let maybe_delegate: Option<Pubkey> = extension.delegate.into();
let delegate = maybe_delegate.ok_or(TokenError::AuthorityTypeNotSupported)?;
Self::validate_owner(
program_id,
&delegate,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
extension.delegate = new_authority.try_into()?;
}
AuthorityType::ConfidentialTransferMint => {
let extension = mint.get_extension_mut::<ConfidentialTransferMint>()?;
let maybe_confidential_transfer_mint_authority: Option<Pubkey> =
extension.authority.into();
let confidential_transfer_mint_authority =
maybe_confidential_transfer_mint_authority
.ok_or(TokenError::AuthorityTypeNotSupported)?;
Self::validate_owner(
program_id,
&confidential_transfer_mint_authority,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
extension.authority = new_authority.try_into()?;
}
AuthorityType::TransferHookProgramId => {
let extension = mint.get_extension_mut::<TransferHook>()?;
let maybe_authority: Option<Pubkey> = extension.authority.into();
let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
Self::validate_owner(
program_id,
&authority,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
extension.authority = new_authority.try_into()?;
}
AuthorityType::ConfidentialTransferFeeConfig => {
let extension = mint.get_extension_mut::<ConfidentialTransferFeeConfig>()?;
let maybe_authority: Option<Pubkey> = extension.authority.into();
let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
Self::validate_owner(
program_id,
&authority,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
extension.authority = new_authority.try_into()?;
}
AuthorityType::MetadataPointer => {
let extension = mint.get_extension_mut::<MetadataPointer>()?;
let maybe_authority: Option<Pubkey> = extension.authority.into();
let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
Self::validate_owner(
program_id,
&authority,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
extension.authority = new_authority.try_into()?;
}
AuthorityType::GroupPointer => {
let extension = mint.get_extension_mut::<GroupPointer>()?;
let maybe_authority: Option<Pubkey> = extension.authority.into();
let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
Self::validate_owner(
program_id,
&authority,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
extension.authority = new_authority.try_into()?;
}
AuthorityType::GroupMemberPointer => {
let extension = mint.get_extension_mut::<GroupMemberPointer>()?;
let maybe_authority: Option<Pubkey> = extension.authority.into();
let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
Self::validate_owner(
program_id,
&authority,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
extension.authority = new_authority.try_into()?;
}
_ => {
return Err(TokenError::AuthorityTypeNotSupported.into());
}
}
} else {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
pub fn process_mint_to(
program_id: &Pubkey,
accounts: &[AccountInfo],
amount: u64,
expected_decimals: Option<u8>,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let mint_info = next_account_info(account_info_iter)?;
let destination_account_info = next_account_info(account_info_iter)?;
let owner_info = next_account_info(account_info_iter)?;
let owner_info_data_len = owner_info.data_len();
let mut destination_account_data = destination_account_info.data.borrow_mut();
let destination_account =
PodStateWithExtensionsMut::<PodAccount>::unpack(&mut destination_account_data)?;
if destination_account.base.is_frozen() {
return Err(TokenError::AccountFrozen.into());
}
if destination_account.base.is_native() {
return Err(TokenError::NativeNotSupported.into());
}
if mint_info.key != &destination_account.base.mint {
return Err(TokenError::MintMismatch.into());
}
let mut mint_data = mint_info.data.borrow_mut();
let mint = PodStateWithExtensionsMut::<PodMint>::unpack(&mut mint_data)?;
if mint.get_extension::<NonTransferable>().is_ok()
&& destination_account
.get_extension::<ImmutableOwner>()
.is_err()
{
return Err(TokenError::NonTransferableNeedsImmutableOwnership.into());
}
if let Some(expected_decimals) = expected_decimals {
if expected_decimals != mint.base.decimals {
return Err(TokenError::MintDecimalsMismatch.into());
}
}
match &mint.base.mint_authority {
PodCOption {
option: PodCOption::<Pubkey>::SOME,
value: mint_authority,
} => Self::validate_owner(
program_id,
mint_authority,
owner_info,
owner_info_data_len,
account_info_iter.as_slice(),
)?,
_ => return Err(TokenError::FixedSupply.into()),
}
check_program_account(mint_info.owner)?;
check_program_account(destination_account_info.owner)?;
destination_account.base.amount = u64::from(destination_account.base.amount)
.checked_add(amount)
.ok_or(TokenError::Overflow)?
.into();
mint.base.supply = u64::from(mint.base.supply)
.checked_add(amount)
.ok_or(TokenError::Overflow)?
.into();
Ok(())
}
pub fn process_burn(
program_id: &Pubkey,
accounts: &[AccountInfo],
amount: u64,
expected_decimals: Option<u8>,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let source_account_info = next_account_info(account_info_iter)?;
let mint_info = next_account_info(account_info_iter)?;
let authority_info = next_account_info(account_info_iter)?;
let authority_info_data_len = authority_info.data_len();
let mut source_account_data = source_account_info.data.borrow_mut();
let source_account =
PodStateWithExtensionsMut::<PodAccount>::unpack(&mut source_account_data)?;
let mut mint_data = mint_info.data.borrow_mut();
let mint = PodStateWithExtensionsMut::<PodMint>::unpack(&mut mint_data)?;
if source_account.base.is_frozen() {
return Err(TokenError::AccountFrozen.into());
}
if source_account.base.is_native() {
return Err(TokenError::NativeNotSupported.into());
}
if u64::from(source_account.base.amount) < amount {
return Err(TokenError::InsufficientFunds.into());
}
if mint_info.key != &source_account.base.mint {
return Err(TokenError::MintMismatch.into());
}
if let Some(expected_decimals) = expected_decimals {
if expected_decimals != mint.base.decimals {
return Err(TokenError::MintDecimalsMismatch.into());
}
}
let maybe_permanent_delegate = get_permanent_delegate(&mint);
if let Ok(cpi_guard) = source_account.get_extension::<CpiGuard>() {
if *authority_info.key == source_account.base.owner
&& cpi_guard.lock_cpi.into()
&& in_cpi()
{
return Err(TokenError::CpiGuardBurnBlocked.into());
}
}
if !source_account
.base
.is_owned_by_system_program_or_incinerator()
{
match (&source_account.base.delegate, maybe_permanent_delegate) {
(_, Some(ref delegate)) if authority_info.key == delegate => Self::validate_owner(
program_id,
delegate,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?,
(
PodCOption {
option: PodCOption::<Pubkey>::SOME,
value: delegate,
},
_,
) if authority_info.key == delegate => {
Self::validate_owner(
program_id,
delegate,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
if u64::from(source_account.base.delegated_amount) < amount {
return Err(TokenError::InsufficientFunds.into());
}
source_account.base.delegated_amount =
u64::from(source_account.base.delegated_amount)
.checked_sub(amount)
.ok_or(TokenError::Overflow)?
.into();
if u64::from(source_account.base.delegated_amount) == 0 {
source_account.base.delegate = PodCOption::none();
}
}
_ => {
Self::validate_owner(
program_id,
&source_account.base.owner,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
}
}
}
check_program_account(source_account_info.owner)?;
check_program_account(mint_info.owner)?;
source_account.base.amount = u64::from(source_account.base.amount)
.checked_sub(amount)
.ok_or(TokenError::Overflow)?
.into();
mint.base.supply = u64::from(mint.base.supply)
.checked_sub(amount)
.ok_or(TokenError::Overflow)?
.into();
Ok(())
}
pub fn process_close_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let source_account_info = next_account_info(account_info_iter)?;
let destination_account_info = next_account_info(account_info_iter)?;
let authority_info = next_account_info(account_info_iter)?;
let authority_info_data_len = authority_info.data_len();
if source_account_info.key == destination_account_info.key {
return Err(ProgramError::InvalidAccountData);
}
let source_account_data = source_account_info.data.borrow();
if let Ok(source_account) =
PodStateWithExtensions::<PodAccount>::unpack(&source_account_data)
{
if !source_account.base.is_native() && u64::from(source_account.base.amount) != 0 {
return Err(TokenError::NonNativeHasBalance.into());
}
let authority = source_account
.base
.close_authority
.unwrap_or(source_account.base.owner);
if !source_account
.base
.is_owned_by_system_program_or_incinerator()
{
if let Ok(cpi_guard) = source_account.get_extension::<CpiGuard>() {
if cpi_guard.lock_cpi.into()
&& in_cpi()
&& destination_account_info.key != &source_account.base.owner
{
return Err(TokenError::CpiGuardCloseAccountBlocked.into());
}
}
Self::validate_owner(
program_id,
&authority,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
} else if !solana_program::incinerator::check_id(destination_account_info.key) {
return Err(ProgramError::InvalidAccountData);
}
if let Ok(confidential_transfer_state) =
source_account.get_extension::<ConfidentialTransferAccount>()
{
confidential_transfer_state.closable()?
}
if let Ok(confidential_transfer_fee_state) =
source_account.get_extension::<ConfidentialTransferFeeAmount>()
{
confidential_transfer_fee_state.closable()?
}
if let Ok(transfer_fee_state) = source_account.get_extension::<TransferFeeAmount>() {
transfer_fee_state.closable()?
}
} else if let Ok(mint) = PodStateWithExtensions::<PodMint>::unpack(&source_account_data) {
let extension = mint.get_extension::<MintCloseAuthority>()?;
let maybe_authority: Option<Pubkey> = extension.close_authority.into();
let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
Self::validate_owner(
program_id,
&authority,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
if u64::from(mint.base.supply) != 0 {
return Err(TokenError::MintHasSupply.into());
}
} else {
return Err(ProgramError::UninitializedAccount);
}
let destination_starting_lamports = destination_account_info.lamports();
**destination_account_info.lamports.borrow_mut() = destination_starting_lamports
.checked_add(source_account_info.lamports())
.ok_or(TokenError::Overflow)?;
**source_account_info.lamports.borrow_mut() = 0;
drop(source_account_data);
delete_account(source_account_info)?;
Ok(())
}
pub fn process_toggle_freeze_account(
program_id: &Pubkey,
accounts: &[AccountInfo],
freeze: bool,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let source_account_info = next_account_info(account_info_iter)?;
let mint_info = next_account_info(account_info_iter)?;
let authority_info = next_account_info(account_info_iter)?;
let authority_info_data_len = authority_info.data_len();
let mut source_account_data = source_account_info.data.borrow_mut();
let source_account =
PodStateWithExtensionsMut::<PodAccount>::unpack(&mut source_account_data)?;
if freeze && source_account.base.is_frozen() || !freeze && !source_account.base.is_frozen()
{
return Err(TokenError::InvalidState.into());
}
if source_account.base.is_native() {
return Err(TokenError::NativeNotSupported.into());
}
if mint_info.key != &source_account.base.mint {
return Err(TokenError::MintMismatch.into());
}
let mint_data = mint_info.data.borrow();
let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data)?;
match &mint.base.freeze_authority {
PodCOption {
option: PodCOption::<Pubkey>::SOME,
value: authority,
} => Self::validate_owner(
program_id,
authority,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
),
_ => Err(TokenError::MintCannotFreeze.into()),
}?;
source_account.base.state = if freeze {
AccountState::Frozen.into()
} else {
AccountState::Initialized.into()
};
Ok(())
}
pub fn process_sync_native(accounts: &[AccountInfo]) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let native_account_info = next_account_info(account_info_iter)?;
check_program_account(native_account_info.owner)?;
let mut native_account_data = native_account_info.data.borrow_mut();
let native_account =
PodStateWithExtensionsMut::<PodAccount>::unpack(&mut native_account_data)?;
match native_account.base.is_native {
PodCOption {
option: PodCOption::<PodU64>::SOME,
value: amount,
} => {
let new_amount = native_account_info
.lamports()
.checked_sub(u64::from(amount))
.ok_or(TokenError::Overflow)?;
if new_amount < u64::from(native_account.base.amount) {
return Err(TokenError::InvalidState.into());
}
native_account.base.amount = new_amount.into();
}
_ => return Err(TokenError::NonNativeNotSupported.into()),
}
Ok(())
}
pub fn process_initialize_mint_close_authority(
accounts: &[AccountInfo],
close_authority: PodCOption<Pubkey>,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let mint_account_info = next_account_info(account_info_iter)?;
let mut mint_data = mint_account_info.data.borrow_mut();
let mut mint = PodStateWithExtensionsMut::<PodMint>::unpack_uninitialized(&mut mint_data)?;
let extension = mint.init_extension::<MintCloseAuthority>(true)?;
extension.close_authority = close_authority.try_into()?;
Ok(())
}
pub fn process_get_account_data_size(
accounts: &[AccountInfo],
new_extension_types: &[ExtensionType],
) -> ProgramResult {
if new_extension_types
.iter()
.any(|&t| t.get_account_type() != AccountType::Account)
{
return Err(TokenError::ExtensionTypeMismatch.into());
}
let account_info_iter = &mut accounts.iter();
let mint_account_info = next_account_info(account_info_iter)?;
let mut account_extensions = Self::get_required_account_extensions(mint_account_info)?;
account_extensions.extend_from_slice(new_extension_types);
let account_len = ExtensionType::try_calculate_account_len::<Account>(&account_extensions)?;
set_return_data(&account_len.to_le_bytes());
Ok(())
}
pub fn process_initialize_immutable_owner(accounts: &[AccountInfo]) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let token_account_info = next_account_info(account_info_iter)?;
let token_account_data = &mut token_account_info.data.borrow_mut();
let mut token_account =
PodStateWithExtensionsMut::<PodAccount>::unpack_uninitialized(token_account_data)?;
token_account
.init_extension::<ImmutableOwner>(true)
.map(|_| ())
}
pub fn process_amount_to_ui_amount(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let mint_info = next_account_info(account_info_iter)?;
check_program_account(mint_info.owner)?;
let mint_data = mint_info.data.borrow();
let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data)
.map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
let ui_amount = if let Ok(extension) = mint.get_extension::<InterestBearingConfig>() {
let unix_timestamp = Clock::get()?.unix_timestamp;
extension
.amount_to_ui_amount(amount, mint.base.decimals, unix_timestamp)
.ok_or(ProgramError::InvalidArgument)?
} else {
crate::amount_to_ui_amount_string_trimmed(amount, mint.base.decimals)
};
set_return_data(&ui_amount.into_bytes());
Ok(())
}
pub fn process_ui_amount_to_amount(accounts: &[AccountInfo], ui_amount: &str) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let mint_info = next_account_info(account_info_iter)?;
check_program_account(mint_info.owner)?;
let mint_data = mint_info.data.borrow();
let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data)
.map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
let amount = if let Ok(extension) = mint.get_extension::<InterestBearingConfig>() {
let unix_timestamp = Clock::get()?.unix_timestamp;
extension.try_ui_amount_into_amount(ui_amount, mint.base.decimals, unix_timestamp)?
} else {
crate::try_ui_amount_into_amount(ui_amount.to_string(), mint.base.decimals)?
};
set_return_data(&amount.to_le_bytes());
Ok(())
}
pub fn process_create_native_mint(accounts: &[AccountInfo]) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let payer_info = next_account_info(account_info_iter)?;
let native_mint_info = next_account_info(account_info_iter)?;
let system_program_info = next_account_info(account_info_iter)?;
if *native_mint_info.key != native_mint::id() {
return Err(TokenError::InvalidMint.into());
}
let rent = Rent::get()?;
let new_minimum_balance = rent.minimum_balance(Mint::get_packed_len());
let lamports_diff = new_minimum_balance.saturating_sub(native_mint_info.lamports());
invoke(
&system_instruction::transfer(payer_info.key, native_mint_info.key, lamports_diff),
&[
payer_info.clone(),
native_mint_info.clone(),
system_program_info.clone(),
],
)?;
invoke_signed(
&system_instruction::allocate(native_mint_info.key, Mint::get_packed_len() as u64),
&[native_mint_info.clone(), system_program_info.clone()],
&[native_mint::PROGRAM_ADDRESS_SEEDS],
)?;
invoke_signed(
&system_instruction::assign(native_mint_info.key, &crate::id()),
&[native_mint_info.clone(), system_program_info.clone()],
&[native_mint::PROGRAM_ADDRESS_SEEDS],
)?;
Mint::pack(
Mint {
decimals: native_mint::DECIMALS,
is_initialized: true,
..Mint::default()
},
&mut native_mint_info.data.borrow_mut(),
)
}
pub fn process_initialize_non_transferable_mint(accounts: &[AccountInfo]) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let mint_account_info = next_account_info(account_info_iter)?;
let mut mint_data = mint_account_info.data.borrow_mut();
let mut mint = PodStateWithExtensionsMut::<PodMint>::unpack_uninitialized(&mut mint_data)?;
mint.init_extension::<NonTransferable>(true)?;
Ok(())
}
pub fn process_initialize_permanent_delegate(
accounts: &[AccountInfo],
delegate: &Pubkey,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let mint_account_info = next_account_info(account_info_iter)?;
let mut mint_data = mint_account_info.data.borrow_mut();
let mut mint = PodStateWithExtensionsMut::<PodMint>::unpack_uninitialized(&mut mint_data)?;
let extension = mint.init_extension::<PermanentDelegate>(true)?;
extension.delegate = Some(*delegate).try_into()?;
Ok(())
}
pub fn process_withdraw_excess_lamports(
program_id: &Pubkey,
accounts: &[AccountInfo],
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let source_info = next_account_info(account_info_iter)?;
let destination_info = next_account_info(account_info_iter)?;
let authority_info = next_account_info(account_info_iter)?;
let source_data = source_info.data.borrow();
if let Ok(account) = PodStateWithExtensions::<PodAccount>::unpack(&source_data) {
if account.base.is_native() {
return Err(TokenError::NativeNotSupported.into());
}
Self::validate_owner(
program_id,
&account.base.owner,
authority_info,
authority_info.data_len(),
account_info_iter.as_slice(),
)?;
} else if let Ok(mint) = PodStateWithExtensions::<PodMint>::unpack(&source_data) {
match &mint.base.mint_authority {
PodCOption {
option: PodCOption::<Pubkey>::SOME,
value: mint_authority,
} => {
Self::validate_owner(
program_id,
mint_authority,
authority_info,
authority_info.data_len(),
account_info_iter.as_slice(),
)?;
}
_ => return Err(TokenError::AuthorityTypeNotSupported.into()),
}
} else if source_data.len() == PodMultisig::SIZE_OF {
Self::validate_owner(
program_id,
source_info.key,
authority_info,
authority_info.data_len(),
account_info_iter.as_slice(),
)?;
} else {
return Err(TokenError::InvalidState.into());
}
let source_rent_exempt_reserve = Rent::get()?.minimum_balance(source_info.data_len());
let transfer_amount = source_info
.lamports()
.checked_sub(source_rent_exempt_reserve)
.ok_or(TokenError::NotRentExempt)?;
let source_starting_lamports = source_info.lamports();
**source_info.lamports.borrow_mut() = source_starting_lamports
.checked_sub(transfer_amount)
.ok_or(TokenError::Overflow)?;
let destination_starting_lamports = destination_info.lamports();
**destination_info.lamports.borrow_mut() = destination_starting_lamports
.checked_add(transfer_amount)
.ok_or(TokenError::Overflow)?;
Ok(())
}
pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
if let Ok(instruction_type) = decode_instruction_type(input) {
match instruction_type {
PodTokenInstruction::InitializeMint => {
msg!("Instruction: InitializeMint");
let (data, freeze_authority) =
decode_instruction_data_with_coption_pubkey::<InitializeMintData>(input)?;
Self::process_initialize_mint(
accounts,
data.decimals,
&data.mint_authority,
freeze_authority,
)
}
PodTokenInstruction::InitializeMint2 => {
msg!("Instruction: InitializeMint2");
let (data, freeze_authority) =
decode_instruction_data_with_coption_pubkey::<InitializeMintData>(input)?;
Self::process_initialize_mint2(
accounts,
data.decimals,
&data.mint_authority,
freeze_authority,
)
}
PodTokenInstruction::InitializeAccount => {
msg!("Instruction: InitializeAccount");
Self::process_initialize_account(accounts)
}
PodTokenInstruction::InitializeAccount2 => {
msg!("Instruction: InitializeAccount2");
let owner = decode_instruction_data::<Pubkey>(input)?;
Self::process_initialize_account2(accounts, owner)
}
PodTokenInstruction::InitializeAccount3 => {
msg!("Instruction: InitializeAccount3");
let owner = decode_instruction_data::<Pubkey>(input)?;
Self::process_initialize_account3(accounts, owner)
}
PodTokenInstruction::InitializeMultisig => {
msg!("Instruction: InitializeMultisig");
let data = decode_instruction_data::<InitializeMultisigData>(input)?;
Self::process_initialize_multisig(accounts, data.m)
}
PodTokenInstruction::InitializeMultisig2 => {
msg!("Instruction: InitializeMultisig2");
let data = decode_instruction_data::<InitializeMultisigData>(input)?;
Self::process_initialize_multisig2(accounts, data.m)
}
#[allow(deprecated)]
PodTokenInstruction::Transfer => {
msg!("Instruction: Transfer");
let data = decode_instruction_data::<AmountData>(input)?;
Self::process_transfer(program_id, accounts, data.amount.into(), None, None)
}
PodTokenInstruction::Approve => {
msg!("Instruction: Approve");
let data = decode_instruction_data::<AmountData>(input)?;
Self::process_approve(program_id, accounts, data.amount.into(), None)
}
PodTokenInstruction::Revoke => {
msg!("Instruction: Revoke");
Self::process_revoke(program_id, accounts)
}
PodTokenInstruction::SetAuthority => {
msg!("Instruction: SetAuthority");
let (data, new_authority) =
decode_instruction_data_with_coption_pubkey::<SetAuthorityData>(input)?;
Self::process_set_authority(
program_id,
accounts,
AuthorityType::from(data.authority_type)?,
new_authority,
)
}
PodTokenInstruction::MintTo => {
msg!("Instruction: MintTo");
let data = decode_instruction_data::<AmountData>(input)?;
Self::process_mint_to(program_id, accounts, data.amount.into(), None)
}
PodTokenInstruction::Burn => {
msg!("Instruction: Burn");
let data = decode_instruction_data::<AmountData>(input)?;
Self::process_burn(program_id, accounts, data.amount.into(), None)
}
PodTokenInstruction::CloseAccount => {
msg!("Instruction: CloseAccount");
Self::process_close_account(program_id, accounts)
}
PodTokenInstruction::FreezeAccount => {
msg!("Instruction: FreezeAccount");
Self::process_toggle_freeze_account(program_id, accounts, true)
}
PodTokenInstruction::ThawAccount => {
msg!("Instruction: ThawAccount");
Self::process_toggle_freeze_account(program_id, accounts, false)
}
PodTokenInstruction::TransferChecked => {
msg!("Instruction: TransferChecked");
let data = decode_instruction_data::<AmountCheckedData>(input)?;
Self::process_transfer(
program_id,
accounts,
data.amount.into(),
Some(data.decimals),
None,
)
}
PodTokenInstruction::ApproveChecked => {
msg!("Instruction: ApproveChecked");
let data = decode_instruction_data::<AmountCheckedData>(input)?;
Self::process_approve(
program_id,
accounts,
data.amount.into(),
Some(data.decimals),
)
}
PodTokenInstruction::MintToChecked => {
msg!("Instruction: MintToChecked");
let data = decode_instruction_data::<AmountCheckedData>(input)?;
Self::process_mint_to(
program_id,
accounts,
data.amount.into(),
Some(data.decimals),
)
}
PodTokenInstruction::BurnChecked => {
msg!("Instruction: BurnChecked");
let data = decode_instruction_data::<AmountCheckedData>(input)?;
Self::process_burn(
program_id,
accounts,
data.amount.into(),
Some(data.decimals),
)
}
PodTokenInstruction::SyncNative => {
msg!("Instruction: SyncNative");
Self::process_sync_native(accounts)
}
PodTokenInstruction::GetAccountDataSize => {
msg!("Instruction: GetAccountDataSize");
let extension_types = input[1..]
.chunks(std::mem::size_of::<ExtensionType>())
.map(ExtensionType::try_from)
.collect::<Result<Vec<_>, _>>()?;
Self::process_get_account_data_size(accounts, &extension_types)
}
PodTokenInstruction::InitializeMintCloseAuthority => {
msg!("Instruction: InitializeMintCloseAuthority");
let (_, close_authority) =
decode_instruction_data_with_coption_pubkey::<()>(input)?;
Self::process_initialize_mint_close_authority(accounts, close_authority)
}
PodTokenInstruction::TransferFeeExtension => {
transfer_fee::processor::process_instruction(program_id, accounts, &input[1..])
}
PodTokenInstruction::ConfidentialTransferExtension => {
confidential_transfer::processor::process_instruction(
program_id,
accounts,
&input[1..],
)
}
PodTokenInstruction::DefaultAccountStateExtension => {
default_account_state::processor::process_instruction(
program_id,
accounts,
&input[1..],
)
}
PodTokenInstruction::InitializeImmutableOwner => {
msg!("Instruction: InitializeImmutableOwner");
Self::process_initialize_immutable_owner(accounts)
}
PodTokenInstruction::AmountToUiAmount => {
msg!("Instruction: AmountToUiAmount");
let data = decode_instruction_data::<AmountData>(input)?;
Self::process_amount_to_ui_amount(accounts, data.amount.into())
}
PodTokenInstruction::UiAmountToAmount => {
msg!("Instruction: UiAmountToAmount");
let ui_amount = std::str::from_utf8(&input[1..])
.map_err(|_| TokenError::InvalidInstruction)?;
Self::process_ui_amount_to_amount(accounts, ui_amount)
}
PodTokenInstruction::Reallocate => {
msg!("Instruction: Reallocate");
let extension_types = input[1..]
.chunks(std::mem::size_of::<ExtensionType>())
.map(ExtensionType::try_from)
.collect::<Result<Vec<_>, _>>()?;
reallocate::process_reallocate(program_id, accounts, extension_types)
}
PodTokenInstruction::MemoTransferExtension => {
memo_transfer::processor::process_instruction(program_id, accounts, &input[1..])
}
PodTokenInstruction::CreateNativeMint => {
msg!("Instruction: CreateNativeMint");
Self::process_create_native_mint(accounts)
}
PodTokenInstruction::InitializeNonTransferableMint => {
msg!("Instruction: InitializeNonTransferableMint");
Self::process_initialize_non_transferable_mint(accounts)
}
PodTokenInstruction::InterestBearingMintExtension => {
interest_bearing_mint::processor::process_instruction(
program_id,
accounts,
&input[1..],
)
}
PodTokenInstruction::CpiGuardExtension => {
cpi_guard::processor::process_instruction(program_id, accounts, &input[1..])
}
PodTokenInstruction::InitializePermanentDelegate => {
msg!("Instruction: InitializePermanentDelegate");
let delegate = decode_instruction_data::<Pubkey>(input)?;
Self::process_initialize_permanent_delegate(accounts, delegate)
}
PodTokenInstruction::TransferHookExtension => {
transfer_hook::processor::process_instruction(program_id, accounts, &input[1..])
}
PodTokenInstruction::ConfidentialTransferFeeExtension => {
confidential_transfer_fee::processor::process_instruction(
program_id,
accounts,
&input[1..],
)
}
PodTokenInstruction::WithdrawExcessLamports => {
msg!("Instruction: WithdrawExcessLamports");
Self::process_withdraw_excess_lamports(program_id, accounts)
}
PodTokenInstruction::MetadataPointerExtension => {
metadata_pointer::processor::process_instruction(
program_id,
accounts,
&input[1..],
)
}
PodTokenInstruction::GroupPointerExtension => {
group_pointer::processor::process_instruction(program_id, accounts, &input[1..])
}
PodTokenInstruction::GroupMemberPointerExtension => {
group_member_pointer::processor::process_instruction(
program_id,
accounts,
&input[1..],
)
}
}
} else if let Ok(instruction) = TokenMetadataInstruction::unpack(input) {
token_metadata::processor::process_instruction(program_id, accounts, instruction)
} else if let Ok(instruction) = TokenGroupInstruction::unpack(input) {
token_group::processor::process_instruction(program_id, accounts, instruction)
} else {
Err(TokenError::InvalidInstruction.into())
}
}
pub fn validate_owner(
program_id: &Pubkey,
expected_owner: &Pubkey,
owner_account_info: &AccountInfo,
owner_account_data_len: usize,
signers: &[AccountInfo],
) -> ProgramResult {
if expected_owner != owner_account_info.key {
return Err(TokenError::OwnerMismatch.into());
}
if program_id == owner_account_info.owner && owner_account_data_len == PodMultisig::SIZE_OF
{
let multisig_data = &owner_account_info.data.borrow();
let multisig = pod_from_bytes::<PodMultisig>(multisig_data)?;
let mut num_signers = 0;
let mut matched = [false; MAX_SIGNERS];
for signer in signers.iter() {
for (position, key) in multisig.signers[0..multisig.n as usize].iter().enumerate() {
if key == signer.key && !matched[position] {
if !signer.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
matched[position] = true;
num_signers += 1;
}
}
}
if num_signers < multisig.m {
return Err(ProgramError::MissingRequiredSignature);
}
return Ok(());
} else if !owner_account_info.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
Ok(())
}
fn get_required_account_extensions(
mint_account_info: &AccountInfo,
) -> Result<Vec<ExtensionType>, ProgramError> {
let mint_data = mint_account_info.data.borrow();
let state = PodStateWithExtensions::<PodMint>::unpack(&mint_data)
.map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
Self::get_required_account_extensions_from_unpacked_mint(mint_account_info.owner, &state)
}
fn get_required_account_extensions_from_unpacked_mint(
token_program_id: &Pubkey,
state: &PodStateWithExtensions<PodMint>,
) -> Result<Vec<ExtensionType>, ProgramError> {
check_program_account(token_program_id)?;
let mint_extensions = state.get_extension_types()?;
Ok(ExtensionType::get_required_init_account_extensions(
&mint_extensions,
))
}
}
#[cfg(not(target_os = "solana"))]
fn delete_account(account_info: &AccountInfo) -> Result<(), ProgramError> {
account_info.assign(&system_program::id());
let mut account_data = account_info.data.borrow_mut();
let data_len = account_data.len();
solana_program::program_memory::sol_memset(*account_data, 0, data_len);
Ok(())
}
#[cfg(target_os = "solana")]
fn delete_account(account_info: &AccountInfo) -> Result<(), ProgramError> {
account_info.assign(&system_program::id());
account_info.realloc(0, false)
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::{
extension::transfer_fee::instruction::initialize_transfer_fee_config, instruction::*,
state::Multisig,
},
serial_test::serial,
solana_program::{
account_info::IntoAccountInfo,
clock::Epoch,
instruction::Instruction,
program_error::{self, PrintProgramError},
program_option::COption,
sysvar::{clock::Clock, rent},
},
solana_sdk::account::{
create_account_for_test, create_is_signer_account_infos, Account as SolanaAccount,
},
std::sync::{Arc, RwLock},
};
lazy_static::lazy_static! {
static ref EXPECTED_DATA: Arc<RwLock<Vec<u8>>> = Arc::new(RwLock::new(Vec::new()));
}
fn set_expected_data(expected_data: Vec<u8>) {
*EXPECTED_DATA.write().unwrap() = expected_data;
}
struct SyscallStubs {}
impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
fn sol_log(&self, _message: &str) {}
fn sol_invoke_signed(
&self,
_instruction: &Instruction,
_account_infos: &[AccountInfo],
_signers_seeds: &[&[&[u8]]],
) -> ProgramResult {
Err(ProgramError::Custom(42)) }
fn sol_get_clock_sysvar(&self, var_addr: *mut u8) -> u64 {
unsafe {
*(var_addr as *mut _ as *mut Clock) = Clock::default();
}
solana_program::entrypoint::SUCCESS
}
fn sol_get_epoch_schedule_sysvar(&self, _var_addr: *mut u8) -> u64 {
program_error::UNSUPPORTED_SYSVAR
}
#[allow(deprecated)]
fn sol_get_fees_sysvar(&self, _var_addr: *mut u8) -> u64 {
program_error::UNSUPPORTED_SYSVAR
}
fn sol_get_rent_sysvar(&self, var_addr: *mut u8) -> u64 {
unsafe {
*(var_addr as *mut _ as *mut Rent) = Rent::default();
}
solana_program::entrypoint::SUCCESS
}
fn sol_set_return_data(&self, data: &[u8]) {
assert_eq!(&*EXPECTED_DATA.read().unwrap(), data)
}
}
fn do_process_instruction(
instruction: Instruction,
accounts: Vec<&mut SolanaAccount>,
) -> ProgramResult {
{
use std::sync::Once;
static ONCE: Once = Once::new();
ONCE.call_once(|| {
solana_sdk::program_stubs::set_syscall_stubs(Box::new(SyscallStubs {}));
});
}
let mut meta = instruction
.accounts
.iter()
.zip(accounts)
.map(|(account_meta, account)| (&account_meta.pubkey, account_meta.is_signer, account))
.collect::<Vec<_>>();
let account_infos = create_is_signer_account_infos(&mut meta);
Processor::process(&instruction.program_id, &account_infos, &instruction.data)
}
fn do_process_instruction_dups(
instruction: Instruction,
account_infos: Vec<AccountInfo>,
) -> ProgramResult {
Processor::process(&instruction.program_id, &account_infos, &instruction.data)
}
fn return_token_error_as_program_error() -> ProgramError {
TokenError::MintMismatch.into()
}
fn rent_sysvar() -> SolanaAccount {
create_account_for_test(&Rent::default())
}
fn mint_minimum_balance() -> u64 {
Rent::default().minimum_balance(Mint::get_packed_len())
}
fn account_minimum_balance() -> u64 {
Rent::default().minimum_balance(Account::get_packed_len())
}
fn multisig_minimum_balance() -> u64 {
Rent::default().minimum_balance(Multisig::get_packed_len())
}
fn native_mint() -> SolanaAccount {
let mut rent_sysvar = rent_sysvar();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &crate::id());
do_process_instruction(
initialize_mint(
&crate::id(),
&crate::native_mint::id(),
&Pubkey::default(),
None,
crate::native_mint::DECIMALS,
)
.unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
mint_account
}
#[test]
fn test_print_error() {
let error = return_token_error_as_program_error();
error.print::<TokenError>();
}
#[test]
fn test_error_as_custom() {
assert_eq!(
return_token_error_as_program_error(),
ProgramError::Custom(3)
);
}
#[test]
fn test_unique_account_sizes() {
assert_ne!(Mint::get_packed_len(), 0);
assert_ne!(Mint::get_packed_len(), Account::get_packed_len());
assert_ne!(Mint::get_packed_len(), Multisig::get_packed_len());
assert_ne!(Account::get_packed_len(), 0);
assert_ne!(Account::get_packed_len(), Multisig::get_packed_len());
assert_ne!(Multisig::get_packed_len(), 0);
}
#[test]
fn test_initialize_mint() {
let program_id = crate::id();
let owner_key = Pubkey::new_unique();
let mint_key = Pubkey::new_unique();
let mut mint_account = SolanaAccount::new(42, Mint::get_packed_len(), &program_id);
let mint2_key = Pubkey::new_unique();
let mut mint2_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mut rent_sysvar = rent_sysvar();
assert_eq!(
Err(TokenError::NotRentExempt.into()),
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar]
)
);
mint_account.lamports = mint_minimum_balance();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
assert_eq!(
Err(TokenError::AlreadyInUse.into()),
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2,).unwrap(),
vec![&mut mint_account, &mut rent_sysvar]
)
);
do_process_instruction(
initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(),
vec![&mut mint2_account, &mut rent_sysvar],
)
.unwrap();
let mint = Mint::unpack_unchecked(&mint2_account.data).unwrap();
assert_eq!(mint.freeze_authority, COption::Some(owner_key));
}
#[test]
fn test_initialize_mint2() {
let program_id = crate::id();
let owner_key = Pubkey::new_unique();
let mint_key = Pubkey::new_unique();
let mut mint_account = SolanaAccount::new(42, Mint::get_packed_len(), &program_id);
let mint2_key = Pubkey::new_unique();
let mut mint2_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
assert_eq!(
Err(TokenError::NotRentExempt.into()),
do_process_instruction(
initialize_mint2(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account]
)
);
mint_account.lamports = mint_minimum_balance();
do_process_instruction(
initialize_mint2(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account],
)
.unwrap();
assert_eq!(
Err(TokenError::AlreadyInUse.into()),
do_process_instruction(
initialize_mint2(&program_id, &mint_key, &owner_key, None, 2,).unwrap(),
vec![&mut mint_account]
)
);
do_process_instruction(
initialize_mint2(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(),
vec![&mut mint2_account],
)
.unwrap();
let mint = Mint::unpack_unchecked(&mint2_account.data).unwrap();
assert_eq!(mint.freeze_authority, COption::Some(owner_key));
}
#[test]
fn test_initialize_mint_account() {
let program_id = crate::id();
let account_key = Pubkey::new_unique();
let mut account_account = SolanaAccount::new(42, Account::get_packed_len(), &program_id);
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mut rent_sysvar = rent_sysvar();
assert_eq!(
Err(TokenError::NotRentExempt.into()),
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar
],
)
);
account_account.lamports = account_minimum_balance();
assert_eq!(
Err(TokenError::InvalidMint.into()),
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar
],
)
);
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
let not_program_id = Pubkey::new_unique();
mint_account.owner = not_program_id;
assert_eq!(
Err(ProgramError::IncorrectProgramId),
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar
],
)
);
mint_account.owner = program_id;
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
assert_eq!(
Err(TokenError::AlreadyInUse.into()),
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar
],
)
);
}
#[test]
fn test_transfer_dups() {
let program_id = crate::id();
let account1_key = Pubkey::new_unique();
let mut account1_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let mut account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into();
let account2_key = Pubkey::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let mut account2_info: AccountInfo = (&account2_key, false, &mut account2_account).into();
let account3_key = Pubkey::new_unique();
let mut account3_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account3_info: AccountInfo = (&account3_key, false, &mut account3_account).into();
let account4_key = Pubkey::new_unique();
let mut account4_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account4_info: AccountInfo = (&account4_key, true, &mut account4_account).into();
let multisig_key = Pubkey::new_unique();
let mut multisig_account = SolanaAccount::new(
multisig_minimum_balance(),
Multisig::get_packed_len(),
&program_id,
);
let multisig_info: AccountInfo = (&multisig_key, true, &mut multisig_account).into();
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into();
let rent_key = rent::id();
let mut rent_sysvar = rent_sysvar();
let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into();
do_process_instruction_dups(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![mint_info.clone(), rent_info.clone()],
)
.unwrap();
do_process_instruction_dups(
initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(),
vec![
account1_info.clone(),
mint_info.clone(),
account1_info.clone(),
rent_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(),
vec![
account2_info.clone(),
mint_info.clone(),
owner_info.clone(),
rent_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(),
vec![mint_info.clone(), account1_info.clone(), owner_info.clone()],
)
.unwrap();
do_process_instruction_dups(
#[allow(deprecated)]
transfer(
&program_id,
&account1_key,
&account2_key,
&account1_key,
&[],
500,
)
.unwrap(),
vec![
account1_info.clone(),
account2_info.clone(),
account1_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
transfer_checked(
&program_id,
&account1_key,
&mint_key,
&account2_key,
&account1_key,
&[],
500,
2,
)
.unwrap(),
vec![
account1_info.clone(),
mint_info.clone(),
account2_info.clone(),
account1_info.clone(),
],
)
.unwrap();
let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap();
account.amount = 1000;
account.delegated_amount = 1000;
account.delegate = COption::Some(account1_key);
account.owner = owner_key;
Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap();
do_process_instruction_dups(
#[allow(deprecated)]
transfer(
&program_id,
&account1_key,
&account2_key,
&account1_key,
&[],
500,
)
.unwrap(),
vec![
account1_info.clone(),
account2_info.clone(),
account1_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
transfer_checked(
&program_id,
&account1_key,
&mint_key,
&account2_key,
&account1_key,
&[],
500,
2,
)
.unwrap(),
vec![
account1_info.clone(),
mint_info.clone(),
account2_info.clone(),
account1_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
initialize_account(&program_id, &account3_key, &mint_key, &account2_key).unwrap(),
vec![
account3_info.clone(),
mint_info.clone(),
account2_info.clone(),
rent_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
mint_to(&program_id, &mint_key, &account3_key, &owner_key, &[], 1000).unwrap(),
vec![mint_info.clone(), account3_info.clone(), owner_info.clone()],
)
.unwrap();
account1_info.is_signer = false;
account2_info.is_signer = true;
do_process_instruction_dups(
#[allow(deprecated)]
transfer(
&program_id,
&account3_key,
&account2_key,
&account2_key,
&[],
500,
)
.unwrap(),
vec![
account3_info.clone(),
account2_info.clone(),
account2_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
transfer_checked(
&program_id,
&account3_key,
&mint_key,
&account2_key,
&account2_key,
&[],
500,
2,
)
.unwrap(),
vec![
account3_info.clone(),
mint_info.clone(),
account2_info.clone(),
account2_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
initialize_multisig(&program_id, &multisig_key, &[&account4_key], 1).unwrap(),
vec![
multisig_info.clone(),
rent_info.clone(),
account4_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
initialize_account(&program_id, &account4_key, &mint_key, &multisig_key).unwrap(),
vec![
account4_info.clone(),
mint_info.clone(),
multisig_info.clone(),
rent_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
mint_to(&program_id, &mint_key, &account4_key, &owner_key, &[], 1000).unwrap(),
vec![mint_info.clone(), account4_info.clone(), owner_info.clone()],
)
.unwrap();
do_process_instruction_dups(
#[allow(deprecated)]
transfer(
&program_id,
&account4_key,
&account2_key,
&multisig_key,
&[&account4_key],
500,
)
.unwrap(),
vec![
account4_info.clone(),
account2_info.clone(),
multisig_info.clone(),
account4_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
transfer_checked(
&program_id,
&account4_key,
&mint_key,
&account2_key,
&multisig_key,
&[&account4_key],
500,
2,
)
.unwrap(),
vec![
account4_info.clone(),
mint_info.clone(),
account2_info.clone(),
multisig_info.clone(),
account4_info.clone(),
],
)
.unwrap();
}
#[test]
fn test_transfer() {
let program_id = crate::id();
let account_key = Pubkey::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Pubkey::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account3_key = Pubkey::new_unique();
let mut account3_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let delegate_key = Pubkey::new_unique();
let mut delegate_account = SolanaAccount::default();
let mismatch_key = Pubkey::new_unique();
let mut mismatch_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Pubkey::new_unique();
let mut owner2_account = SolanaAccount::default();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint2_key = Pubkey::new_unique();
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account2_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account3_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(),
vec![
&mut mismatch_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap();
account.mint = mint2_key;
Account::pack(account, &mut mismatch_account.data).unwrap();
do_process_instruction(
mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(),
vec![&mut mint_account, &mut account_account, &mut owner_account],
)
.unwrap();
#[allow(deprecated)]
let mut instruction = transfer(
&program_id,
&account_key,
&account2_key,
&owner_key,
&[],
1000,
)
.unwrap();
instruction.accounts[2].is_signer = false;
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
do_process_instruction(
instruction,
vec![
&mut account_account,
&mut account2_account,
&mut owner_account,
],
)
);
assert_eq!(
Err(TokenError::MintMismatch.into()),
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account_key,
&mismatch_key,
&owner_key,
&[],
1000
)
.unwrap(),
vec![
&mut account_account,
&mut mismatch_account,
&mut owner_account,
],
)
);
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account_key,
&account2_key,
&owner2_key,
&[],
1000
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut owner2_account,
],
)
);
let not_program_id = Pubkey::new_unique();
account_account.owner = not_program_id;
assert_eq!(
Err(ProgramError::IncorrectProgramId),
do_process_instruction(
#[allow(deprecated)]
transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 0,).unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut owner2_account,
],
)
);
account_account.owner = program_id;
let not_program_id = Pubkey::new_unique();
account2_account.owner = not_program_id;
assert_eq!(
Err(ProgramError::IncorrectProgramId),
do_process_instruction(
#[allow(deprecated)]
transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 0,).unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut owner2_account,
],
)
);
account2_account.owner = program_id;
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account_key,
&account2_key,
&owner_key,
&[],
1000,
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut owner_account,
],
)
.unwrap();
assert_eq!(
Err(TokenError::InsufficientFunds.into()),
do_process_instruction(
#[allow(deprecated)]
transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 1).unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut owner_account,
],
)
);
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account2_key,
&account_key,
&owner_key,
&[],
500,
)
.unwrap(),
vec![
&mut account2_account,
&mut account_account,
&mut owner_account,
],
)
.unwrap();
assert_eq!(
Err(TokenError::MintDecimalsMismatch.into()),
do_process_instruction(
transfer_checked(
&program_id,
&account2_key,
&mint_key,
&account_key,
&owner_key,
&[],
1,
10 )
.unwrap(),
vec![
&mut account2_account,
&mut mint_account,
&mut account_account,
&mut owner_account,
],
)
);
assert_eq!(
Err(TokenError::MintMismatch.into()),
do_process_instruction(
transfer_checked(
&program_id,
&account2_key,
&account3_key, &account_key,
&owner_key,
&[],
1,
2
)
.unwrap(),
vec![
&mut account2_account,
&mut account3_account, &mut account_account,
&mut owner_account,
],
)
);
do_process_instruction(
transfer_checked(
&program_id,
&account2_key,
&mint_key,
&account_key,
&owner_key,
&[],
500,
2,
)
.unwrap(),
vec![
&mut account2_account,
&mut mint_account,
&mut account_account,
&mut owner_account,
],
)
.unwrap();
assert_eq!(
Err(TokenError::InsufficientFunds.into()),
do_process_instruction(
#[allow(deprecated)]
transfer(&program_id, &account2_key, &account_key, &owner_key, &[], 1).unwrap(),
vec![
&mut account2_account,
&mut account_account,
&mut owner_account,
],
)
);
do_process_instruction(
approve(
&program_id,
&account_key,
&delegate_key,
&owner_key,
&[],
100,
)
.unwrap(),
vec![
&mut account_account,
&mut delegate_account,
&mut owner_account,
],
)
.unwrap();
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account_key,
&account2_key,
&owner2_key, &[],
1,
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut owner2_account,
],
)
);
assert_eq!(
Err(TokenError::InsufficientFunds.into()),
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account_key,
&account2_key,
&delegate_key,
&[],
101
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut delegate_account,
],
)
);
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account_key,
&account2_key,
&delegate_key,
&[],
100,
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut delegate_account,
],
)
.unwrap();
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account_key,
&account2_key,
&delegate_key,
&[],
1
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut delegate_account,
],
)
);
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account_key,
&account2_key,
&owner_key,
&[],
900,
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut owner_account,
],
)
.unwrap();
do_process_instruction(
approve(
&program_id,
&account_key,
&delegate_key,
&owner_key,
&[],
100,
)
.unwrap(),
vec![
&mut account_account,
&mut delegate_account,
&mut owner_account,
],
)
.unwrap();
assert_eq!(
Err(TokenError::InsufficientFunds.into()),
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account_key,
&account2_key,
&delegate_key,
&[],
100
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut delegate_account,
],
)
);
}
#[test]
fn test_self_transfer() {
let program_id = crate::id();
let account_key = Pubkey::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Pubkey::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account3_key = Pubkey::new_unique();
let mut account3_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let delegate_key = Pubkey::new_unique();
let mut delegate_account = SolanaAccount::default();
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Pubkey::new_unique();
let mut owner2_account = SolanaAccount::default();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account2_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account3_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(),
vec![&mut mint_account, &mut account_account, &mut owner_account],
)
.unwrap();
let account_info = (&account_key, false, &mut account_account).into_account_info();
let account3_info = (&account3_key, false, &mut account3_account).into_account_info();
let delegate_info = (&delegate_key, true, &mut delegate_account).into_account_info();
let owner_info = (&owner_key, true, &mut owner_account).into_account_info();
let owner2_info = (&owner2_key, true, &mut owner2_account).into_account_info();
let mint_info = (&mint_key, false, &mut mint_account).into_account_info();
#[allow(deprecated)]
let instruction = transfer(
&program_id,
account_info.key,
account_info.key,
owner_info.key,
&[],
1000,
)
.unwrap();
assert_eq!(
Ok(()),
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&instruction.data,
)
);
let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap();
assert_eq!(account.amount, 1000);
let instruction = transfer_checked(
&program_id,
account_info.key,
mint_info.key,
account_info.key,
owner_info.key,
&[],
1000,
2,
)
.unwrap();
assert_eq!(
Ok(()),
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
mint_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&instruction.data,
)
);
let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap();
assert_eq!(account.amount, 1000);
let mut owner_no_sign_info = owner_info.clone();
#[allow(deprecated)]
let mut instruction = transfer(
&program_id,
account_info.key,
account_info.key,
owner_no_sign_info.key,
&[],
1000,
)
.unwrap();
instruction.accounts[2].is_signer = false;
owner_no_sign_info.is_signer = false;
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
account_info.clone(),
owner_no_sign_info.clone(),
],
&instruction.data,
)
);
let mut instruction = transfer_checked(
&program_id,
account_info.key,
mint_info.key,
account_info.key,
owner_no_sign_info.key,
&[],
1000,
2,
)
.unwrap();
instruction.accounts[3].is_signer = false;
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
mint_info.clone(),
account_info.clone(),
owner_no_sign_info,
],
&instruction.data,
)
);
#[allow(deprecated)]
let instruction = transfer(
&program_id,
account_info.key,
account_info.key,
owner2_info.key,
&[],
1000,
)
.unwrap();
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
account_info.clone(),
owner2_info.clone(),
],
&instruction.data,
)
);
let instruction = transfer_checked(
&program_id,
account_info.key,
mint_info.key,
account_info.key,
owner2_info.key,
&[],
1000,
2,
)
.unwrap();
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
mint_info.clone(),
account_info.clone(),
owner2_info.clone(),
],
&instruction.data,
)
);
#[allow(deprecated)]
let instruction = transfer(
&program_id,
account_info.key,
account_info.key,
owner_info.key,
&[],
1001,
)
.unwrap();
assert_eq!(
Err(TokenError::InsufficientFunds.into()),
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&instruction.data,
)
);
let instruction = transfer_checked(
&program_id,
account_info.key,
mint_info.key,
account_info.key,
owner_info.key,
&[],
1001,
2,
)
.unwrap();
assert_eq!(
Err(TokenError::InsufficientFunds.into()),
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
mint_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&instruction.data,
)
);
let instruction = transfer_checked(
&program_id,
account_info.key,
mint_info.key,
account_info.key,
owner_info.key,
&[],
1,
10, )
.unwrap();
assert_eq!(
Err(TokenError::MintDecimalsMismatch.into()),
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
mint_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&instruction.data,
)
);
let instruction = transfer_checked(
&program_id,
account_info.key,
account3_info.key, account_info.key,
owner_info.key,
&[],
1,
2,
)
.unwrap();
assert_eq!(
Err(TokenError::MintMismatch.into()),
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
account3_info.clone(), account_info.clone(),
owner_info.clone(),
],
&instruction.data,
)
);
let instruction = approve(
&program_id,
account_info.key,
delegate_info.key,
owner_info.key,
&[],
100,
)
.unwrap();
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
delegate_info.clone(),
owner_info.clone(),
],
&instruction.data,
)
.unwrap();
#[allow(deprecated)]
let instruction = transfer(
&program_id,
account_info.key,
account_info.key,
delegate_info.key,
&[],
100,
)
.unwrap();
assert_eq!(
Ok(()),
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
account_info.clone(),
delegate_info.clone(),
],
&instruction.data,
)
);
let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap();
assert_eq!(account.amount, 1000);
assert_eq!(account.delegated_amount, 100);
let instruction = transfer_checked(
&program_id,
account_info.key,
mint_info.key,
account_info.key,
delegate_info.key,
&[],
100,
2,
)
.unwrap();
assert_eq!(
Ok(()),
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
mint_info.clone(),
account_info.clone(),
delegate_info.clone(),
],
&instruction.data,
)
);
let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap();
assert_eq!(account.amount, 1000);
assert_eq!(account.delegated_amount, 100);
#[allow(deprecated)]
let instruction = transfer(
&program_id,
account_info.key,
account_info.key,
delegate_info.key,
&[],
101,
)
.unwrap();
assert_eq!(
Err(TokenError::InsufficientFunds.into()),
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
account_info.clone(),
delegate_info.clone(),
],
&instruction.data,
)
);
let instruction = transfer_checked(
&program_id,
account_info.key,
mint_info.key,
account_info.key,
delegate_info.key,
&[],
101,
2,
)
.unwrap();
assert_eq!(
Err(TokenError::InsufficientFunds.into()),
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
mint_info.clone(),
account_info.clone(),
delegate_info.clone(),
],
&instruction.data,
)
);
#[allow(deprecated)]
let instruction = transfer(
&program_id,
account_info.key,
account_info.key,
owner_info.key,
&[],
1000,
)
.unwrap();
assert_eq!(
Ok(()),
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&instruction.data,
)
);
let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap();
assert_eq!(account.amount, 1000);
let instruction = transfer_checked(
&program_id,
account_info.key,
mint_info.key,
account_info.key,
owner_info.key,
&[],
1000,
2,
)
.unwrap();
assert_eq!(
Ok(()),
Processor::process(
&instruction.program_id,
&[
account_info.clone(),
mint_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&instruction.data,
)
);
let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap();
assert_eq!(account.amount, 1000);
}
#[test]
fn test_mintable_token_with_zero_supply() {
let program_id = crate::id();
let account_key = Pubkey::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mut rent_sysvar = rent_sysvar();
let decimals = 2;
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, decimals).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
let mint = Mint::unpack_unchecked(&mint_account.data).unwrap();
assert_eq!(
mint,
Mint {
mint_authority: COption::Some(owner_key),
supply: 0,
decimals,
is_initialized: true,
freeze_authority: COption::None,
}
);
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(),
vec![&mut mint_account, &mut account_account, &mut owner_account],
)
.unwrap();
let _ = Mint::unpack(&mint_account.data).unwrap();
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, 42);
assert_eq!(
Err(TokenError::MintDecimalsMismatch.into()),
do_process_instruction(
mint_to_checked(
&program_id,
&mint_key,
&account_key,
&owner_key,
&[],
42,
decimals + 1
)
.unwrap(),
vec![&mut mint_account, &mut account_account, &mut owner_account],
)
);
let _ = Mint::unpack(&mint_account.data).unwrap();
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, 42);
do_process_instruction(
mint_to_checked(
&program_id,
&mint_key,
&account_key,
&owner_key,
&[],
42,
decimals,
)
.unwrap(),
vec![&mut mint_account, &mut account_account, &mut owner_account],
)
.unwrap();
let _ = Mint::unpack(&mint_account.data).unwrap();
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, 84);
}
#[test]
fn test_approve_dups() {
let program_id = crate::id();
let account1_key = Pubkey::new_unique();
let mut account1_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into();
let account2_key = Pubkey::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_info: AccountInfo = (&account2_key, false, &mut account2_account).into();
let account3_key = Pubkey::new_unique();
let mut account3_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account3_info: AccountInfo = (&account3_key, true, &mut account3_account).into();
let multisig_key = Pubkey::new_unique();
let mut multisig_account = SolanaAccount::new(
multisig_minimum_balance(),
Multisig::get_packed_len(),
&program_id,
);
let multisig_info: AccountInfo = (&multisig_key, true, &mut multisig_account).into();
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into();
let rent_key = rent::id();
let mut rent_sysvar = rent_sysvar();
let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into();
do_process_instruction_dups(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![mint_info.clone(), rent_info.clone()],
)
.unwrap();
do_process_instruction_dups(
initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(),
vec![
account1_info.clone(),
mint_info.clone(),
account1_info.clone(),
rent_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(),
vec![
account2_info.clone(),
mint_info.clone(),
owner_info.clone(),
rent_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(),
vec![mint_info.clone(), account1_info.clone(), owner_info.clone()],
)
.unwrap();
do_process_instruction_dups(
approve(
&program_id,
&account1_key,
&account2_key,
&account1_key,
&[],
500,
)
.unwrap(),
vec![
account1_info.clone(),
account2_info.clone(),
account1_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
approve_checked(
&program_id,
&account1_key,
&mint_key,
&account2_key,
&account1_key,
&[],
500,
2,
)
.unwrap(),
vec![
account1_info.clone(),
mint_info.clone(),
account2_info.clone(),
account1_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
revoke(&program_id, &account1_key, &account1_key, &[]).unwrap(),
vec![account1_info.clone(), account1_info.clone()],
)
.unwrap();
do_process_instruction_dups(
initialize_multisig(&program_id, &multisig_key, &[&account3_key], 1).unwrap(),
vec![
multisig_info.clone(),
rent_info.clone(),
account3_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
initialize_account(&program_id, &account3_key, &mint_key, &multisig_key).unwrap(),
vec![
account3_info.clone(),
mint_info.clone(),
multisig_info.clone(),
rent_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
mint_to(&program_id, &mint_key, &account3_key, &owner_key, &[], 1000).unwrap(),
vec![mint_info.clone(), account3_info.clone(), owner_info.clone()],
)
.unwrap();
do_process_instruction_dups(
approve(
&program_id,
&account3_key,
&account2_key,
&multisig_key,
&[&account3_key],
500,
)
.unwrap(),
vec![
account3_info.clone(),
account2_info.clone(),
multisig_info.clone(),
account3_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
approve_checked(
&program_id,
&account3_key,
&mint_key,
&account2_key,
&multisig_key,
&[&account3_key],
500,
2,
)
.unwrap(),
vec![
account3_info.clone(),
mint_info.clone(),
account2_info.clone(),
multisig_info.clone(),
account3_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
revoke(&program_id, &account3_key, &multisig_key, &[&account3_key]).unwrap(),
vec![
account3_info.clone(),
multisig_info.clone(),
account3_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
approve_checked(
&program_id,
&account2_key,
&mint_key,
&account2_key,
&owner_key,
&[],
500,
2,
)
.unwrap(),
vec![
account2_info.clone(),
mint_info.clone(),
account2_info.clone(),
owner_info.clone(),
],
)
.unwrap();
let account2_info: AccountInfo = (&account2_key, true, &mut account2_account).into();
do_process_instruction_dups(
revoke(&program_id, &account2_key, &account2_key, &[]).unwrap(),
vec![account2_info.clone(), account2_info.clone()],
)
.unwrap();
}
#[test]
fn test_approve() {
let program_id = crate::id();
let account_key = Pubkey::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Pubkey::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let delegate_key = Pubkey::new_unique();
let mut delegate_account = SolanaAccount::default();
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Pubkey::new_unique();
let mut owner2_account = SolanaAccount::default();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account2_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(),
vec![&mut mint_account, &mut account_account, &mut owner_account],
)
.unwrap();
let mut instruction = approve(
&program_id,
&account_key,
&delegate_key,
&owner_key,
&[],
100,
)
.unwrap();
instruction.accounts[2].is_signer = false;
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
do_process_instruction(
instruction,
vec![
&mut account_account,
&mut delegate_account,
&mut owner_account,
],
)
);
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
do_process_instruction(
approve(
&program_id,
&account_key,
&delegate_key,
&owner2_key,
&[],
100
)
.unwrap(),
vec![
&mut account_account,
&mut delegate_account,
&mut owner2_account,
],
)
);
do_process_instruction(
approve(
&program_id,
&account_key,
&delegate_key,
&owner_key,
&[],
100,
)
.unwrap(),
vec![
&mut account_account,
&mut delegate_account,
&mut owner_account,
],
)
.unwrap();
assert_eq!(
Err(TokenError::MintDecimalsMismatch.into()),
do_process_instruction(
approve_checked(
&program_id,
&account_key,
&mint_key,
&delegate_key,
&owner_key,
&[],
100,
0 )
.unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut delegate_account,
&mut owner_account,
],
)
);
assert_eq!(
Err(TokenError::MintMismatch.into()),
do_process_instruction(
approve_checked(
&program_id,
&account_key,
&account2_key, &delegate_key,
&owner_key,
&[],
100,
0
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account, &mut delegate_account,
&mut owner_account,
],
)
);
do_process_instruction(
approve_checked(
&program_id,
&account_key,
&mint_key,
&delegate_key,
&owner_key,
&[],
100,
2,
)
.unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut delegate_account,
&mut owner_account,
],
)
.unwrap();
do_process_instruction(
revoke(&program_id, &account_key, &owner_key, &[]).unwrap(),
vec![&mut account_account, &mut owner_account],
)
.unwrap();
do_process_instruction(
approve_checked(
&program_id,
&account_key,
&mint_key,
&delegate_key,
&owner_key,
&[],
100,
2,
)
.unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut delegate_account,
&mut owner_account,
],
)
.unwrap();
do_process_instruction(
revoke(&program_id, &account_key, &delegate_key, &[]).unwrap(),
vec![&mut account_account, &mut delegate_account],
)
.unwrap();
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
do_process_instruction(
revoke(&program_id, &account_key, &delegate_key, &[]).unwrap(),
vec![&mut account_account, &mut delegate_account],
)
);
}
#[test]
fn test_set_authority_dups() {
let program_id = crate::id();
let account1_key = Pubkey::new_unique();
let mut account1_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into();
let owner_key = Pubkey::new_unique();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into();
let rent_key = rent::id();
let mut rent_sysvar = rent_sysvar();
let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into();
do_process_instruction_dups(
initialize_mint(&program_id, &mint_key, &mint_key, Some(&mint_key), 2).unwrap(),
vec![mint_info.clone(), rent_info.clone()],
)
.unwrap();
do_process_instruction_dups(
initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(),
vec![
account1_info.clone(),
mint_info.clone(),
account1_info.clone(),
rent_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
set_authority(
&program_id,
&mint_key,
Some(&owner_key),
AuthorityType::MintTokens,
&mint_key,
&[],
)
.unwrap(),
vec![mint_info.clone(), mint_info.clone()],
)
.unwrap();
do_process_instruction_dups(
set_authority(
&program_id,
&mint_key,
Some(&owner_key),
AuthorityType::FreezeAccount,
&mint_key,
&[],
)
.unwrap(),
vec![mint_info.clone(), mint_info.clone()],
)
.unwrap();
do_process_instruction_dups(
set_authority(
&program_id,
&account1_key,
Some(&owner_key),
AuthorityType::AccountOwner,
&account1_key,
&[],
)
.unwrap(),
vec![account1_info.clone(), account1_info.clone()],
)
.unwrap();
let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap();
account.close_authority = COption::Some(account1_key);
Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap();
do_process_instruction_dups(
set_authority(
&program_id,
&account1_key,
Some(&owner_key),
AuthorityType::CloseAccount,
&account1_key,
&[],
)
.unwrap(),
vec![account1_info.clone(), account1_info.clone()],
)
.unwrap();
}
#[test]
fn test_set_authority() {
let program_id = crate::id();
let account_key = Pubkey::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Pubkey::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Pubkey::new_unique();
let mut owner2_account = SolanaAccount::default();
let owner3_key = Pubkey::new_unique();
let mut owner3_account = SolanaAccount::default();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint2_key = Pubkey::new_unique();
let mut mint2_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
do_process_instruction(
initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(),
vec![&mut mint2_account, &mut rent_sysvar],
)
.unwrap();
assert_eq!(
Err(ProgramError::InvalidAccountData),
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&owner2_key),
AuthorityType::AccountOwner,
&owner_key,
&[]
)
.unwrap(),
vec![&mut account_account, &mut owner_account],
)
);
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account2_key, &mint2_key, &owner_key).unwrap(),
vec![
&mut account2_account,
&mut mint2_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&owner_key),
AuthorityType::AccountOwner,
&owner2_key,
&[]
)
.unwrap(),
vec![&mut account_account, &mut owner2_account],
)
);
let mut instruction = set_authority(
&program_id,
&account_key,
Some(&owner2_key),
AuthorityType::AccountOwner,
&owner_key,
&[],
)
.unwrap();
instruction.accounts[1].is_signer = false;
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
do_process_instruction(instruction, vec![&mut account_account, &mut owner_account,],)
);
assert_eq!(
Err(TokenError::AuthorityTypeNotSupported.into()),
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&owner2_key),
AuthorityType::FreezeAccount,
&owner_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner_account],
)
);
assert_eq!(
Err(TokenError::InvalidInstruction.into()),
do_process_instruction(
set_authority(
&program_id,
&account_key,
None,
AuthorityType::AccountOwner,
&owner_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner_account],
)
);
do_process_instruction(
approve(
&program_id,
&account_key,
&owner2_key,
&owner_key,
&[],
u64::MAX,
)
.unwrap(),
vec![
&mut account_account,
&mut owner2_account,
&mut owner_account,
],
)
.unwrap();
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.delegate, COption::Some(owner2_key));
assert_eq!(account.delegated_amount, u64::MAX);
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&owner3_key),
AuthorityType::AccountOwner,
&owner_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner_account],
)
.unwrap();
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.delegate, COption::None);
assert_eq!(account.delegated_amount, 0);
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&owner2_key),
AuthorityType::AccountOwner,
&owner3_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner3_account],
)
.unwrap();
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&owner2_key),
AuthorityType::CloseAccount,
&owner2_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner2_account],
)
.unwrap();
do_process_instruction(
set_authority(
&program_id,
&account_key,
None,
AuthorityType::CloseAccount,
&owner2_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner2_account],
)
.unwrap();
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
do_process_instruction(
set_authority(
&program_id,
&mint_key,
Some(&owner3_key),
AuthorityType::MintTokens,
&owner2_key,
&[]
)
.unwrap(),
vec![&mut mint_account, &mut owner2_account],
)
);
let mut instruction = set_authority(
&program_id,
&mint_key,
Some(&owner2_key),
AuthorityType::MintTokens,
&owner_key,
&[],
)
.unwrap();
instruction.accounts[1].is_signer = false;
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
do_process_instruction(instruction, vec![&mut mint_account, &mut owner_account],)
);
assert_eq!(
Err(TokenError::MintCannotFreeze.into()),
do_process_instruction(
set_authority(
&program_id,
&mint_key,
Some(&owner2_key),
AuthorityType::FreezeAccount,
&owner_key,
&[],
)
.unwrap(),
vec![&mut mint_account, &mut owner_account],
)
);
do_process_instruction(
set_authority(
&program_id,
&mint_key,
Some(&owner2_key),
AuthorityType::MintTokens,
&owner_key,
&[],
)
.unwrap(),
vec![&mut mint_account, &mut owner_account],
)
.unwrap();
do_process_instruction(
set_authority(
&program_id,
&mint_key,
None,
AuthorityType::MintTokens,
&owner2_key,
&[],
)
.unwrap(),
vec![&mut mint_account, &mut owner2_account],
)
.unwrap();
assert_eq!(
Err(TokenError::FixedSupply.into()),
do_process_instruction(
set_authority(
&program_id,
&mint2_key,
Some(&owner2_key),
AuthorityType::MintTokens,
&owner_key,
&[]
)
.unwrap(),
vec![&mut mint_account, &mut owner_account],
)
);
do_process_instruction(
set_authority(
&program_id,
&mint2_key,
Some(&owner2_key),
AuthorityType::FreezeAccount,
&owner_key,
&[],
)
.unwrap(),
vec![&mut mint2_account, &mut owner_account],
)
.unwrap();
do_process_instruction(
set_authority(
&program_id,
&mint2_key,
None,
AuthorityType::FreezeAccount,
&owner2_key,
&[],
)
.unwrap(),
vec![&mut mint2_account, &mut owner2_account],
)
.unwrap();
assert_eq!(
Err(TokenError::MintCannotFreeze.into()),
do_process_instruction(
set_authority(
&program_id,
&mint2_key,
Some(&owner2_key),
AuthorityType::FreezeAccount,
&owner_key,
&[],
)
.unwrap(),
vec![&mut mint2_account, &mut owner2_account],
)
);
}
#[test]
fn test_set_authority_with_immutable_owner_extension() {
let program_id = crate::id();
let account_key = Pubkey::new_unique();
let account_len =
ExtensionType::try_calculate_account_len::<Account>(&[ExtensionType::ImmutableOwner])
.unwrap();
let mut account_account = SolanaAccount::new(
Rent::default().minimum_balance(account_len),
account_len,
&program_id,
);
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Pubkey::new_unique();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mut rent_sysvar = rent_sysvar();
assert_eq!(
Ok(()),
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
);
assert_eq!(
Ok(()),
do_process_instruction(
initialize_immutable_owner(&program_id, &account_key).unwrap(),
vec![&mut account_account],
)
);
assert_eq!(
Ok(()),
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
);
assert_eq!(
Err(TokenError::ImmutableOwner.into()),
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&owner2_key),
AuthorityType::AccountOwner,
&owner_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner_account],
)
);
}
#[test]
fn test_mint_to_dups() {
let program_id = crate::id();
let account1_key = Pubkey::new_unique();
let mut account1_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into();
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into();
let rent_key = rent::id();
let mut rent_sysvar = rent_sysvar();
let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into();
do_process_instruction_dups(
initialize_mint(&program_id, &mint_key, &mint_key, None, 2).unwrap(),
vec![mint_info.clone(), rent_info.clone()],
)
.unwrap();
do_process_instruction_dups(
initialize_account(&program_id, &account1_key, &mint_key, &owner_key).unwrap(),
vec![
account1_info.clone(),
mint_info.clone(),
owner_info.clone(),
rent_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
mint_to(&program_id, &mint_key, &account1_key, &mint_key, &[], 42).unwrap(),
vec![mint_info.clone(), account1_info.clone(), mint_info.clone()],
)
.unwrap();
do_process_instruction_dups(
mint_to_checked(&program_id, &mint_key, &account1_key, &mint_key, &[], 42, 2).unwrap(),
vec![mint_info.clone(), account1_info.clone(), mint_info.clone()],
)
.unwrap();
let mut mint = Mint::unpack_unchecked(&mint_info.data.borrow()).unwrap();
mint.mint_authority = COption::Some(account1_key);
Mint::pack(mint, &mut mint_info.data.borrow_mut()).unwrap();
do_process_instruction_dups(
mint_to(
&program_id,
&mint_key,
&account1_key,
&account1_key,
&[],
42,
)
.unwrap(),
vec![
mint_info.clone(),
account1_info.clone(),
account1_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
mint_to(
&program_id,
&mint_key,
&account1_key,
&account1_key,
&[],
42,
)
.unwrap(),
vec![
mint_info.clone(),
account1_info.clone(),
account1_info.clone(),
],
)
.unwrap();
}
#[test]
fn test_mint_to() {
let program_id = crate::id();
let account_key = Pubkey::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Pubkey::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account3_key = Pubkey::new_unique();
let mut account3_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let mismatch_key = Pubkey::new_unique();
let mut mismatch_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Pubkey::new_unique();
let mut owner2_account = SolanaAccount::default();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint2_key = Pubkey::new_unique();
let uninitialized_key = Pubkey::new_unique();
let mut uninitialized_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account2_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account3_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(),
vec![
&mut mismatch_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap();
account.mint = mint2_key;
Account::pack(account, &mut mismatch_account.data).unwrap();
do_process_instruction(
mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(),
vec![&mut mint_account, &mut account_account, &mut owner_account],
)
.unwrap();
let mint = Mint::unpack_unchecked(&mint_account.data).unwrap();
assert_eq!(mint.supply, 42);
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, 42);
do_process_instruction(
mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(),
vec![&mut mint_account, &mut account2_account, &mut owner_account],
)
.unwrap();
let mint = Mint::unpack_unchecked(&mint_account.data).unwrap();
assert_eq!(mint.supply, 84);
let account = Account::unpack_unchecked(&account2_account.data).unwrap();
assert_eq!(account.amount, 42);
let mut instruction =
mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap();
instruction.accounts[2].is_signer = false;
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
do_process_instruction(
instruction,
vec![&mut mint_account, &mut account2_account, &mut owner_account],
)
);
assert_eq!(
Err(TokenError::MintMismatch.into()),
do_process_instruction(
mint_to(&program_id, &mint_key, &mismatch_key, &owner_key, &[], 42).unwrap(),
vec![&mut mint_account, &mut mismatch_account, &mut owner_account],
)
);
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
do_process_instruction(
mint_to(&program_id, &mint_key, &account2_key, &owner2_key, &[], 42).unwrap(),
vec![
&mut mint_account,
&mut account2_account,
&mut owner2_account,
],
)
);
let not_program_id = Pubkey::new_unique();
mint_account.owner = not_program_id;
assert_eq!(
Err(ProgramError::IncorrectProgramId),
do_process_instruction(
mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 0).unwrap(),
vec![&mut mint_account, &mut account_account, &mut owner_account],
)
);
mint_account.owner = program_id;
let not_program_id = Pubkey::new_unique();
account_account.owner = not_program_id;
assert_eq!(
Err(ProgramError::IncorrectProgramId),
do_process_instruction(
mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 0).unwrap(),
vec![&mut mint_account, &mut account_account, &mut owner_account],
)
);
account_account.owner = program_id;
assert_eq!(
Err(ProgramError::UninitializedAccount),
do_process_instruction(
mint_to(
&program_id,
&mint_key,
&uninitialized_key,
&owner_key,
&[],
42
)
.unwrap(),
vec![
&mut mint_account,
&mut uninitialized_account,
&mut owner_account,
],
)
);
do_process_instruction(
set_authority(
&program_id,
&mint_key,
None,
AuthorityType::MintTokens,
&owner_key,
&[],
)
.unwrap(),
vec![&mut mint_account, &mut owner_account],
)
.unwrap();
assert_eq!(
Err(TokenError::FixedSupply.into()),
do_process_instruction(
mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(),
vec![&mut mint_account, &mut account2_account, &mut owner_account],
)
);
}
#[test]
fn test_burn_dups() {
let program_id = crate::id();
let account1_key = Pubkey::new_unique();
let mut account1_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into();
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into();
let rent_key = rent::id();
let mut rent_sysvar = rent_sysvar();
let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into();
do_process_instruction_dups(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![mint_info.clone(), rent_info.clone()],
)
.unwrap();
do_process_instruction_dups(
initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(),
vec![
account1_info.clone(),
mint_info.clone(),
account1_info.clone(),
rent_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(),
vec![mint_info.clone(), account1_info.clone(), owner_info.clone()],
)
.unwrap();
do_process_instruction_dups(
burn(
&program_id,
&mint_key,
&account1_key,
&account1_key,
&[],
500,
)
.unwrap(),
vec![
account1_info.clone(),
mint_info.clone(),
account1_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
burn_checked(
&program_id,
&account1_key,
&mint_key,
&account1_key,
&[],
500,
2,
)
.unwrap(),
vec![
account1_info.clone(),
mint_info.clone(),
account1_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(),
vec![mint_info.clone(), account1_info.clone(), owner_info.clone()],
)
.unwrap();
let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap();
account.owner = mint_key;
Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap();
do_process_instruction_dups(
burn(&program_id, &account1_key, &mint_key, &mint_key, &[], 500).unwrap(),
vec![account1_info.clone(), mint_info.clone(), mint_info.clone()],
)
.unwrap();
do_process_instruction_dups(
burn_checked(
&program_id,
&account1_key,
&mint_key,
&mint_key,
&[],
500,
2,
)
.unwrap(),
vec![account1_info.clone(), mint_info.clone(), mint_info.clone()],
)
.unwrap();
do_process_instruction_dups(
mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(),
vec![mint_info.clone(), account1_info.clone(), owner_info.clone()],
)
.unwrap();
let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap();
account.delegated_amount = 1000;
account.delegate = COption::Some(account1_key);
account.owner = owner_key;
Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap();
do_process_instruction_dups(
burn(
&program_id,
&account1_key,
&mint_key,
&account1_key,
&[],
500,
)
.unwrap(),
vec![
account1_info.clone(),
mint_info.clone(),
account1_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
burn_checked(
&program_id,
&account1_key,
&mint_key,
&account1_key,
&[],
500,
2,
)
.unwrap(),
vec![
account1_info.clone(),
mint_info.clone(),
account1_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(),
vec![mint_info.clone(), account1_info.clone(), owner_info.clone()],
)
.unwrap();
let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap();
account.delegated_amount = 1000;
account.delegate = COption::Some(mint_key);
account.owner = owner_key;
Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap();
do_process_instruction_dups(
burn(&program_id, &account1_key, &mint_key, &mint_key, &[], 500).unwrap(),
vec![account1_info.clone(), mint_info.clone(), mint_info.clone()],
)
.unwrap();
do_process_instruction_dups(
burn_checked(
&program_id,
&account1_key,
&mint_key,
&mint_key,
&[],
500,
2,
)
.unwrap(),
vec![account1_info.clone(), mint_info.clone(), mint_info.clone()],
)
.unwrap();
}
#[test]
fn test_burn() {
let program_id = crate::id();
let account_key = Pubkey::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Pubkey::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account3_key = Pubkey::new_unique();
let mut account3_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let delegate_key = Pubkey::new_unique();
let mut delegate_account = SolanaAccount::default();
let mismatch_key = Pubkey::new_unique();
let mut mismatch_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Pubkey::new_unique();
let mut owner2_account = SolanaAccount::default();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint2_key = Pubkey::new_unique();
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account2_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account3_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(),
vec![
&mut mismatch_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(),
vec![&mut mint_account, &mut account_account, &mut owner_account],
)
.unwrap();
do_process_instruction(
mint_to(&program_id, &mint_key, &mismatch_key, &owner_key, &[], 1000).unwrap(),
vec![&mut mint_account, &mut mismatch_account, &mut owner_account],
)
.unwrap();
let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap();
account.mint = mint2_key;
Account::pack(account, &mut mismatch_account.data).unwrap();
let mut instruction =
burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 42).unwrap();
instruction.accounts[1].is_signer = false;
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
do_process_instruction(
instruction,
vec![
&mut account_account,
&mut mint_account,
&mut delegate_account
],
)
);
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
do_process_instruction(
burn(&program_id, &account_key, &mint_key, &owner2_key, &[], 42).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner2_account],
)
);
let not_program_id = Pubkey::new_unique();
account_account.owner = not_program_id;
assert_eq!(
Err(ProgramError::IncorrectProgramId),
do_process_instruction(
burn(&program_id, &account_key, &mint_key, &owner_key, &[], 0).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
)
);
account_account.owner = program_id;
let not_program_id = Pubkey::new_unique();
mint_account.owner = not_program_id;
assert_eq!(
Err(ProgramError::IncorrectProgramId),
do_process_instruction(
burn(&program_id, &account_key, &mint_key, &owner_key, &[], 0).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
)
);
mint_account.owner = program_id;
assert_eq!(
Err(TokenError::MintMismatch.into()),
do_process_instruction(
burn(&program_id, &mismatch_key, &mint_key, &owner_key, &[], 42).unwrap(),
vec![&mut mismatch_account, &mut mint_account, &mut owner_account],
)
);
do_process_instruction(
burn(&program_id, &account_key, &mint_key, &owner_key, &[], 21).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
)
.unwrap();
assert_eq!(
Err(TokenError::MintDecimalsMismatch.into()),
do_process_instruction(
burn_checked(&program_id, &account_key, &mint_key, &owner_key, &[], 21, 3).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
)
);
do_process_instruction(
burn_checked(&program_id, &account_key, &mint_key, &owner_key, &[], 21, 2).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
)
.unwrap();
let mint = Mint::unpack_unchecked(&mint_account.data).unwrap();
assert_eq!(mint.supply, 2000 - 42);
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, 1000 - 42);
assert_eq!(
Err(TokenError::InsufficientFunds.into()),
do_process_instruction(
burn(
&program_id,
&account_key,
&mint_key,
&owner_key,
&[],
100_000_000
)
.unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
)
);
do_process_instruction(
approve(
&program_id,
&account_key,
&delegate_key,
&owner_key,
&[],
84,
)
.unwrap(),
vec![
&mut account_account,
&mut delegate_account,
&mut owner_account,
],
)
.unwrap();
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
do_process_instruction(
burn(
&program_id,
&account_key,
&mint_key,
&owner2_key, &[],
1,
)
.unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner2_account],
)
);
assert_eq!(
Err(TokenError::InsufficientFunds.into()),
do_process_instruction(
burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 85).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut delegate_account
],
)
);
do_process_instruction(
burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 84).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut delegate_account,
],
)
.unwrap();
let mint = Mint::unpack_unchecked(&mint_account.data).unwrap();
assert_eq!(mint.supply, 2000 - 42 - 84);
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, 1000 - 42 - 84);
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
do_process_instruction(
burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 1).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut delegate_account
],
)
);
}
#[test]
fn test_multisig() {
let program_id = crate::id();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let account_key = Pubkey::new_unique();
let mut account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Pubkey::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let multisig_key = Pubkey::new_unique();
let mut multisig_account = SolanaAccount::new(42, Multisig::get_packed_len(), &program_id);
let multisig_delegate_key = Pubkey::new_unique();
let mut multisig_delegate_account = SolanaAccount::new(
multisig_minimum_balance(),
Multisig::get_packed_len(),
&program_id,
);
let signer_keys = vec![Pubkey::new_unique(); MAX_SIGNERS];
let signer_key_refs: Vec<&Pubkey> = signer_keys.iter().collect();
let mut signer_accounts = vec![SolanaAccount::new(0, 0, &program_id); MAX_SIGNERS];
let mut rent_sysvar = rent_sysvar();
let account_info_iter = &mut signer_accounts.iter_mut();
assert_eq!(
Err(TokenError::NotRentExempt.into()),
do_process_instruction(
initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(),
vec![
&mut multisig_account,
&mut rent_sysvar,
account_info_iter.next().unwrap(),
],
)
);
multisig_account.lamports = multisig_minimum_balance();
let mut multisig_account2 = multisig_account.clone();
let account_info_iter = &mut signer_accounts.iter_mut();
do_process_instruction(
initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(),
vec![
&mut multisig_account,
&mut rent_sysvar,
account_info_iter.next().unwrap(),
],
)
.unwrap();
let account_info_iter = &mut signer_accounts.iter_mut();
do_process_instruction(
initialize_multisig2(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(),
vec![&mut multisig_account2, account_info_iter.next().unwrap()],
)
.unwrap();
let account_info_iter = &mut signer_accounts.iter_mut();
do_process_instruction(
initialize_multisig(
&program_id,
&multisig_delegate_key,
&signer_key_refs,
MAX_SIGNERS as u8,
)
.unwrap(),
vec![
&mut multisig_delegate_account,
&mut rent_sysvar,
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
],
)
.unwrap();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &multisig_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &multisig_key).unwrap(),
vec![
&mut account,
&mut mint_account,
&mut multisig_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account(
&program_id,
&account2_key,
&mint_key,
&multisig_delegate_key,
)
.unwrap(),
vec![
&mut account2_account,
&mut mint_account,
&mut multisig_account,
&mut rent_sysvar,
],
)
.unwrap();
let account_info_iter = &mut signer_accounts.iter_mut();
do_process_instruction(
mint_to(
&program_id,
&mint_key,
&account_key,
&multisig_key,
&[&signer_keys[0]],
1000,
)
.unwrap(),
vec![
&mut mint_account,
&mut account,
&mut multisig_account,
account_info_iter.next().unwrap(),
],
)
.unwrap();
let account_info_iter = &mut signer_accounts.iter_mut();
do_process_instruction(
approve(
&program_id,
&account_key,
&multisig_delegate_key,
&multisig_key,
&[&signer_keys[0]],
100,
)
.unwrap(),
vec![
&mut account,
&mut multisig_delegate_account,
&mut multisig_account,
account_info_iter.next().unwrap(),
],
)
.unwrap();
let account_info_iter = &mut signer_accounts.iter_mut();
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account_key,
&account2_key,
&multisig_key,
&[&signer_keys[0]],
42,
)
.unwrap(),
vec![
&mut account,
&mut account2_account,
&mut multisig_account,
account_info_iter.next().unwrap(),
],
)
.unwrap();
let account_info_iter = &mut signer_accounts.iter_mut();
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account_key,
&account2_key,
&multisig_delegate_key,
&signer_key_refs,
42,
)
.unwrap(),
vec![
&mut account,
&mut account2_account,
&mut multisig_delegate_account,
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
],
)
.unwrap();
let account_info_iter = &mut signer_accounts.iter_mut();
do_process_instruction(
mint_to(
&program_id,
&mint_key,
&account2_key,
&multisig_key,
&[&signer_keys[0]],
42,
)
.unwrap(),
vec![
&mut mint_account,
&mut account2_account,
&mut multisig_account,
account_info_iter.next().unwrap(),
],
)
.unwrap();
let account_info_iter = &mut signer_accounts.iter_mut();
do_process_instruction(
burn(
&program_id,
&account_key,
&mint_key,
&multisig_key,
&[&signer_keys[0]],
42,
)
.unwrap(),
vec![
&mut account,
&mut mint_account,
&mut multisig_account,
account_info_iter.next().unwrap(),
],
)
.unwrap();
let account_info_iter = &mut signer_accounts.iter_mut();
do_process_instruction(
burn(
&program_id,
&account_key,
&mint_key,
&multisig_delegate_key,
&signer_key_refs,
42,
)
.unwrap(),
vec![
&mut account,
&mut mint_account,
&mut multisig_delegate_account,
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
account_info_iter.next().unwrap(),
],
)
.unwrap();
let account3_key = Pubkey::new_unique();
let mut account3_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let mint2_key = Pubkey::new_unique();
let mut mint2_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
do_process_instruction(
initialize_mint(
&program_id,
&mint2_key,
&multisig_key,
Some(&multisig_key),
2,
)
.unwrap(),
vec![&mut mint2_account, &mut rent_sysvar],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account3_key, &mint2_key, &owner_key).unwrap(),
vec![
&mut account3_account,
&mut mint2_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
let account_info_iter = &mut signer_accounts.iter_mut();
do_process_instruction(
mint_to(
&program_id,
&mint2_key,
&account3_key,
&multisig_key,
&[&signer_keys[0]],
1000,
)
.unwrap(),
vec![
&mut mint2_account,
&mut account3_account,
&mut multisig_account,
account_info_iter.next().unwrap(),
],
)
.unwrap();
let account_info_iter = &mut signer_accounts.iter_mut();
do_process_instruction(
freeze_account(
&program_id,
&account3_key,
&mint2_key,
&multisig_key,
&[&signer_keys[0]],
)
.unwrap(),
vec![
&mut account3_account,
&mut mint2_account,
&mut multisig_account,
account_info_iter.next().unwrap(),
],
)
.unwrap();
let account_info_iter = &mut signer_accounts.iter_mut();
do_process_instruction(
set_authority(
&program_id,
&mint_key,
Some(&owner_key),
AuthorityType::MintTokens,
&multisig_key,
&[&signer_keys[0]],
)
.unwrap(),
vec![
&mut mint_account,
&mut multisig_account,
account_info_iter.next().unwrap(),
],
)
.unwrap();
let account_info_iter = &mut signer_accounts.iter_mut();
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&owner_key),
AuthorityType::AccountOwner,
&multisig_key,
&[&signer_keys[0]],
)
.unwrap(),
vec![
&mut account,
&mut multisig_account,
account_info_iter.next().unwrap(),
],
)
.unwrap();
}
#[test]
fn test_validate_owner() {
let program_id = crate::id();
let owner_key = Pubkey::new_unique();
let account_to_validate = Pubkey::new_unique();
let mut signer_keys = [Pubkey::default(); MAX_SIGNERS];
for signer_key in signer_keys.iter_mut().take(MAX_SIGNERS) {
*signer_key = Pubkey::new_unique();
}
let mut signer_lamports = 0;
let mut signer_data = vec![];
let mut signers = vec![
AccountInfo::new(
&owner_key,
true,
false,
&mut signer_lamports,
&mut signer_data,
&program_id,
false,
Epoch::default(),
);
MAX_SIGNERS + 1
];
for (signer, key) in signers.iter_mut().zip(&signer_keys) {
signer.key = key;
}
let mut lamports = 0;
let mut data = vec![0; Multisig::get_packed_len()];
let mut multisig = Multisig::unpack_unchecked(&data).unwrap();
multisig.m = MAX_SIGNERS as u8;
multisig.n = MAX_SIGNERS as u8;
multisig.signers = signer_keys;
multisig.is_initialized = true;
Multisig::pack(multisig, &mut data).unwrap();
let owner_account_info = AccountInfo::new(
&owner_key,
false,
false,
&mut lamports,
&mut data,
&program_id,
false,
Epoch::default(),
);
{
let mut lamports = 0;
let mut data = vec![0; Account::get_packed_len()];
let mut account = Account::unpack_unchecked(&data).unwrap();
account.owner = account_to_validate;
Account::pack(account, &mut data).unwrap();
let account_info = AccountInfo::new(
&account_to_validate,
true,
false,
&mut lamports,
&mut data,
&program_id,
false,
Epoch::default(),
);
let account_info_data_len = account_info.data_len();
let mut borrowed_data = account_info.try_borrow_mut_data().unwrap();
Processor::validate_owner(
&program_id,
&account_to_validate,
&account_info,
account_info_data_len,
&[],
)
.unwrap();
borrowed_data[0] = 1;
}
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_info.data_len(),
&signers,
)
.unwrap();
{
let mut multisig =
Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
multisig.m = 1;
Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
}
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_info.data_len(),
&signers,
)
.unwrap();
{
let mut multisig =
Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
multisig.m = 2;
multisig.n = 1;
Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
}
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_info.data_len(),
&signers
)
);
{
let mut multisig =
Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
multisig.m = 0;
multisig.n = 11;
Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
}
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_info.data_len(),
&signers,
)
.unwrap();
{
let mut multisig =
Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
multisig.m = 2;
multisig.n = 11;
Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
}
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_info.data_len(),
&[]
)
);
{
let mut multisig =
Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
multisig.m = 2;
multisig.n = 11;
Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
}
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_info.data_len(),
&signers[0..1]
)
);
{
let mut multisig =
Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
multisig.m = 2;
multisig.n = 11;
Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
}
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_info.data_len(),
&signers[5..7],
)
.unwrap();
{
let mut multisig =
Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
multisig.m = 11;
multisig.n = 11;
Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
}
signers[5].is_signer = false;
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_info.data_len(),
&signers
)
);
signers[5].is_signer = true;
{
let mut signer_lamports = 0;
let mut signer_data = vec![];
let signers = vec![
AccountInfo::new(
&signer_keys[5],
true,
false,
&mut signer_lamports,
&mut signer_data,
&program_id,
false,
Epoch::default(),
);
MAX_SIGNERS + 1
];
let mut multisig =
Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
multisig.m = 11;
multisig.n = 11;
Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_info.data_len(),
&signers
)
);
}
}
#[test]
fn test_owner_close_account_dups() {
let program_id = crate::id();
let owner_key = Pubkey::new_unique();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into();
let rent_key = rent::id();
let mut rent_sysvar = rent_sysvar();
let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into();
do_process_instruction_dups(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![mint_info.clone(), rent_info.clone()],
)
.unwrap();
let to_close_key = Pubkey::new_unique();
let mut to_close_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let to_close_account_info: AccountInfo =
(&to_close_key, true, &mut to_close_account).into();
let destination_account_key = Pubkey::new_unique();
let mut destination_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let destination_account_info: AccountInfo =
(&destination_account_key, true, &mut destination_account).into();
do_process_instruction_dups(
initialize_account(&program_id, &to_close_key, &mint_key, &to_close_key).unwrap(),
vec![
to_close_account_info.clone(),
mint_info.clone(),
to_close_account_info.clone(),
rent_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
close_account(
&program_id,
&to_close_key,
&destination_account_key,
&to_close_key,
&[],
)
.unwrap(),
vec![
to_close_account_info.clone(),
destination_account_info.clone(),
to_close_account_info.clone(),
],
)
.unwrap();
assert_eq!(*to_close_account_info.data.borrow(), &[0u8; Account::LEN]);
}
#[test]
fn test_close_authority_close_account_dups() {
let program_id = crate::id();
let owner_key = Pubkey::new_unique();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into();
let rent_key = rent::id();
let mut rent_sysvar = rent_sysvar();
let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into();
do_process_instruction_dups(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![mint_info.clone(), rent_info.clone()],
)
.unwrap();
let to_close_key = Pubkey::new_unique();
let mut to_close_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let to_close_account_info: AccountInfo =
(&to_close_key, true, &mut to_close_account).into();
let destination_account_key = Pubkey::new_unique();
let mut destination_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let destination_account_info: AccountInfo =
(&destination_account_key, true, &mut destination_account).into();
do_process_instruction_dups(
initialize_account(&program_id, &to_close_key, &mint_key, &to_close_key).unwrap(),
vec![
to_close_account_info.clone(),
mint_info.clone(),
to_close_account_info.clone(),
rent_info.clone(),
],
)
.unwrap();
let mut account = Account::unpack_unchecked(&to_close_account_info.data.borrow()).unwrap();
account.close_authority = COption::Some(to_close_key);
account.owner = owner_key;
Account::pack(account, &mut to_close_account_info.data.borrow_mut()).unwrap();
do_process_instruction_dups(
close_account(
&program_id,
&to_close_key,
&destination_account_key,
&to_close_key,
&[],
)
.unwrap(),
vec![
to_close_account_info.clone(),
destination_account_info.clone(),
to_close_account_info.clone(),
],
)
.unwrap();
assert_eq!(*to_close_account_info.data.borrow(), &[0u8; Account::LEN]);
}
#[test]
fn test_close_account() {
let program_id = crate::id();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let account_key = Pubkey::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Pubkey::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance() + 42,
Account::get_packed_len(),
&program_id,
);
let account3_key = Pubkey::new_unique();
let mut account3_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Pubkey::new_unique();
let mut owner2_account = SolanaAccount::default();
let mut rent_sysvar = rent_sysvar();
assert_eq!(
Err(ProgramError::UninitializedAccount),
do_process_instruction(
close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(),
vec![
&mut account_account,
&mut account3_account,
&mut owner2_account,
],
)
);
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(),
vec![
&mut mint_account,
&mut account_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, 42);
do_process_instruction(
initialize_account(
&program_id,
&account2_key,
&crate::native_mint::id(),
&owner_key,
)
.unwrap(),
vec![
&mut account2_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
let account = Account::unpack_unchecked(&account2_account.data).unwrap();
assert!(account.is_native());
assert_eq!(account.amount, 42);
assert_eq!(
Err(TokenError::NonNativeHasBalance.into()),
do_process_instruction(
close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(),
vec![
&mut account_account,
&mut account3_account,
&mut owner_account,
],
)
);
assert_eq!(account_account.lamports, account_minimum_balance());
do_process_instruction(
burn(&program_id, &account_key, &mint_key, &owner_key, &[], 42).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
)
.unwrap();
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
do_process_instruction(
close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(),
vec![
&mut account_account,
&mut account3_account,
&mut owner2_account,
],
)
);
do_process_instruction(
close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(),
vec![
&mut account_account,
&mut account3_account,
&mut owner_account,
],
)
.unwrap();
assert_eq!(account_account.lamports, 0);
assert_eq!(account3_account.lamports, 2 * account_minimum_balance());
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, 0);
let account_key = Pubkey::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner2_key = Pubkey::new_unique();
let mut owner2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
account_account.lamports = 2;
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&owner2_key),
AuthorityType::CloseAccount,
&owner_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner_account],
)
.unwrap();
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
do_process_instruction(
close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(),
vec![
&mut account_account,
&mut account3_account,
&mut owner_account,
],
)
);
do_process_instruction(
close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(),
vec![
&mut account_account,
&mut account3_account,
&mut owner2_account,
],
)
.unwrap();
assert_eq!(account_account.lamports, 0);
assert_eq!(account3_account.lamports, 2 * account_minimum_balance() + 2);
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, 0);
do_process_instruction(
close_account(&program_id, &account2_key, &account3_key, &owner_key, &[]).unwrap(),
vec![
&mut account2_account,
&mut account3_account,
&mut owner_account,
],
)
.unwrap();
assert_eq!(account2_account.data, [0u8; Account::LEN]);
assert_eq!(
account3_account.lamports,
3 * account_minimum_balance() + 2 + 42
);
}
#[test]
fn test_native_token() {
let program_id = crate::id();
let mut mint_account = native_mint();
let account_key = Pubkey::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance() + 40,
Account::get_packed_len(),
&program_id,
);
let account2_key = Pubkey::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account3_key = Pubkey::new_unique();
let mut account3_account = SolanaAccount::new(account_minimum_balance(), 0, &program_id);
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Pubkey::new_unique();
let mut owner2_account = SolanaAccount::default();
let owner3_key = Pubkey::new_unique();
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
initialize_account(
&program_id,
&account_key,
&crate::native_mint::id(),
&owner_key,
)
.unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert!(account.is_native());
assert_eq!(account.amount, 40);
do_process_instruction(
initialize_account(
&program_id,
&account2_key,
&crate::native_mint::id(),
&owner_key,
)
.unwrap(),
vec![
&mut account2_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
let account = Account::unpack_unchecked(&account2_account.data).unwrap();
assert!(account.is_native());
assert_eq!(account.amount, 0);
assert_eq!(
Err(TokenError::NativeNotSupported.into()),
do_process_instruction(
mint_to(
&program_id,
&crate::native_mint::id(),
&account_key,
&owner_key,
&[],
42
)
.unwrap(),
vec![&mut mint_account, &mut account_account, &mut owner_account],
)
);
let bogus_mint_key = Pubkey::new_unique();
let mut bogus_mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
do_process_instruction(
initialize_mint(&program_id, &bogus_mint_key, &owner_key, None, 2).unwrap(),
vec![&mut bogus_mint_account, &mut rent_sysvar],
)
.unwrap();
assert_eq!(
Err(TokenError::NativeNotSupported.into()),
do_process_instruction(
burn(
&program_id,
&account_key,
&bogus_mint_key,
&owner_key,
&[],
42
)
.unwrap(),
vec![
&mut account_account,
&mut bogus_mint_account,
&mut owner_account
],
)
);
assert_eq!(
Err(TokenError::InsufficientFunds.into()),
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account_key,
&account2_key,
&owner_key,
&[],
50,
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut owner_account,
],
)
);
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account_key,
&account2_key,
&owner_key,
&[],
40,
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut owner_account,
],
)
.unwrap();
assert_eq!(account_account.lamports, account_minimum_balance());
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert!(account.is_native());
assert_eq!(account.amount, 0);
assert_eq!(account2_account.lamports, account_minimum_balance() + 40);
let account = Account::unpack_unchecked(&account2_account.data).unwrap();
assert!(account.is_native());
assert_eq!(account.amount, 40);
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&owner3_key),
AuthorityType::CloseAccount,
&owner_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner_account],
)
.unwrap();
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.close_authority, COption::Some(owner3_key));
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&owner2_key),
AuthorityType::AccountOwner,
&owner_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner_account],
)
.unwrap();
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.close_authority, COption::None);
do_process_instruction(
close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(),
vec![
&mut account_account,
&mut account3_account,
&mut owner2_account,
],
)
.unwrap();
assert_eq!(account_account.lamports, 0);
assert_eq!(account3_account.lamports, 2 * account_minimum_balance());
assert_eq!(account_account.data, [0u8; Account::LEN]);
}
#[test]
fn test_overflow() {
let program_id = crate::id();
let account_key = Pubkey::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Pubkey::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Pubkey::new_unique();
let mut owner2_account = SolanaAccount::default();
let mint_owner_key = Pubkey::new_unique();
let mut mint_owner_account = SolanaAccount::default();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &mint_owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account2_key, &mint_key, &owner2_key).unwrap(),
vec![
&mut account2_account,
&mut mint_account,
&mut owner2_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
mint_to(
&program_id,
&mint_key,
&account_key,
&mint_owner_key,
&[],
u64::MAX,
)
.unwrap(),
vec![
&mut mint_account,
&mut account_account,
&mut mint_owner_account,
],
)
.unwrap();
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, u64::MAX);
assert_eq!(
Err(TokenError::Overflow.into()),
do_process_instruction(
mint_to(
&program_id,
&mint_key,
&account_key,
&mint_owner_key,
&[],
1,
)
.unwrap(),
vec![
&mut mint_account,
&mut account_account,
&mut mint_owner_account,
],
)
);
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, u64::MAX);
assert_eq!(
Err(TokenError::Overflow.into()),
do_process_instruction(
mint_to(
&program_id,
&mint_key,
&account2_key,
&mint_owner_key,
&[],
1,
)
.unwrap(),
vec![
&mut mint_account,
&mut account2_account,
&mut mint_owner_account,
],
)
);
do_process_instruction(
burn(&program_id, &account_key, &mint_key, &owner_key, &[], 100).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
)
.unwrap();
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, u64::MAX - 100);
do_process_instruction(
mint_to(
&program_id,
&mint_key,
&account_key,
&mint_owner_key,
&[],
100,
)
.unwrap(),
vec![
&mut mint_account,
&mut account_account,
&mut mint_owner_account,
],
)
.unwrap();
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, u64::MAX);
let mut account = Account::unpack_unchecked(&account2_account.data).unwrap();
account.amount = 1;
Account::pack(account, &mut account2_account.data).unwrap();
assert_eq!(
Err(TokenError::Overflow.into()),
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account2_key,
&account_key,
&owner2_key,
&[],
1,
)
.unwrap(),
vec![
&mut account2_account,
&mut account_account,
&mut owner2_account,
],
)
);
}
#[test]
fn test_frozen() {
let program_id = crate::id();
let account_key = Pubkey::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Pubkey::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account2_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(),
vec![&mut mint_account, &mut account_account, &mut owner_account],
)
.unwrap();
let mut account = Account::unpack_unchecked(&account2_account.data).unwrap();
account.state = AccountState::Frozen;
Account::pack(account, &mut account2_account.data).unwrap();
assert_eq!(
Err(TokenError::AccountFrozen.into()),
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account_key,
&account2_key,
&owner_key,
&[],
500,
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut owner_account,
],
)
);
let mut account = Account::unpack_unchecked(&account_account.data).unwrap();
account.state = AccountState::Initialized;
Account::pack(account, &mut account_account.data).unwrap();
let mut account = Account::unpack_unchecked(&account2_account.data).unwrap();
account.state = AccountState::Frozen;
Account::pack(account, &mut account2_account.data).unwrap();
assert_eq!(
Err(TokenError::AccountFrozen.into()),
do_process_instruction(
#[allow(deprecated)]
transfer(
&program_id,
&account_key,
&account2_key,
&owner_key,
&[],
500,
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut owner_account,
],
)
);
let mut account = Account::unpack_unchecked(&account_account.data).unwrap();
account.state = AccountState::Frozen;
Account::pack(account, &mut account_account.data).unwrap();
let delegate_key = Pubkey::new_unique();
let mut delegate_account = SolanaAccount::default();
assert_eq!(
Err(TokenError::AccountFrozen.into()),
do_process_instruction(
approve(
&program_id,
&account_key,
&delegate_key,
&owner_key,
&[],
100
)
.unwrap(),
vec![
&mut account_account,
&mut delegate_account,
&mut owner_account,
],
)
);
let mut account = Account::unpack_unchecked(&account_account.data).unwrap();
account.delegate = COption::Some(delegate_key);
account.delegated_amount = 100;
Account::pack(account, &mut account_account.data).unwrap();
assert_eq!(
Err(TokenError::AccountFrozen.into()),
do_process_instruction(
revoke(&program_id, &account_key, &owner_key, &[]).unwrap(),
vec![&mut account_account, &mut owner_account],
)
);
let new_owner_key = Pubkey::new_unique();
assert_eq!(
Err(TokenError::AccountFrozen.into()),
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&new_owner_key),
AuthorityType::AccountOwner,
&owner_key,
&[]
)
.unwrap(),
vec![&mut account_account, &mut owner_account,],
)
);
assert_eq!(
Err(TokenError::AccountFrozen.into()),
do_process_instruction(
mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 100).unwrap(),
vec![&mut mint_account, &mut account_account, &mut owner_account,],
)
);
assert_eq!(
Err(TokenError::AccountFrozen.into()),
do_process_instruction(
burn(&program_id, &account_key, &mint_key, &owner_key, &[], 100).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
)
);
}
#[test]
fn test_freeze_thaw_dups() {
let program_id = crate::id();
let account1_key = Pubkey::new_unique();
let mut account1_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into();
let owner_key = Pubkey::new_unique();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into();
let rent_key = rent::id();
let mut rent_sysvar = rent_sysvar();
let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into();
do_process_instruction_dups(
initialize_mint(&program_id, &mint_key, &owner_key, Some(&account1_key), 2).unwrap(),
vec![mint_info.clone(), rent_info.clone()],
)
.unwrap();
do_process_instruction_dups(
initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(),
vec![
account1_info.clone(),
mint_info.clone(),
account1_info.clone(),
rent_info.clone(),
],
)
.unwrap();
do_process_instruction_dups(
freeze_account(&program_id, &account1_key, &mint_key, &account1_key, &[]).unwrap(),
vec![
account1_info.clone(),
mint_info.clone(),
account1_info.clone(),
],
)
.unwrap();
let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap();
account.state = AccountState::Frozen;
Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap();
do_process_instruction_dups(
thaw_account(&program_id, &account1_key, &mint_key, &account1_key, &[]).unwrap(),
vec![
account1_info.clone(),
mint_info.clone(),
account1_info.clone(),
],
)
.unwrap();
}
#[test]
fn test_freeze_account() {
let program_id = crate::id();
let account_key = Pubkey::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account_owner_key = Pubkey::new_unique();
let mut account_owner_account = SolanaAccount::default();
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Pubkey::new_unique();
let mut owner2_account = SolanaAccount::default();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &account_owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut account_owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(),
vec![&mut mint_account, &mut account_account, &mut owner_account],
)
.unwrap();
assert_eq!(
Err(TokenError::MintCannotFreeze.into()),
do_process_instruction(
freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
)
);
let mut mint = Mint::unpack_unchecked(&mint_account.data).unwrap();
mint.freeze_authority = COption::Some(owner_key);
Mint::pack(mint, &mut mint_account.data).unwrap();
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
do_process_instruction(
freeze_account(&program_id, &account_key, &mint_key, &owner2_key, &[]).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner2_account],
)
);
assert_eq!(
Err(TokenError::InvalidState.into()),
do_process_instruction(
thaw_account(&program_id, &account_key, &mint_key, &owner2_key, &[]).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner2_account],
)
);
do_process_instruction(
freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
)
.unwrap();
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.state, AccountState::Frozen);
assert_eq!(
Err(TokenError::InvalidState.into()),
do_process_instruction(
freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
)
);
assert_eq!(
Err(TokenError::OwnerMismatch.into()),
do_process_instruction(
thaw_account(&program_id, &account_key, &mint_key, &owner2_key, &[]).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner2_account],
)
);
do_process_instruction(
thaw_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
)
.unwrap();
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.state, AccountState::Initialized);
}
#[test]
fn test_initialize_account2_and_3() {
let program_id = crate::id();
let account_key = Pubkey::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let mut account3_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
do_process_instruction(
initialize_account2(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![&mut account2_account, &mut mint_account, &mut rent_sysvar],
)
.unwrap();
assert_eq!(account_account, account2_account);
do_process_instruction(
initialize_account3(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![&mut account3_account, &mut mint_account],
)
.unwrap();
assert_eq!(account_account, account3_account);
}
#[test]
fn initialize_account_on_non_transferable_mint() {
let program_id = crate::id();
let account = Pubkey::new_unique();
let account_len = ExtensionType::try_calculate_account_len::<Mint>(&[
ExtensionType::NonTransferableAccount,
])
.unwrap();
let mut account_without_enough_length = SolanaAccount::new(
Rent::default().minimum_balance(account_len),
account_len,
&program_id,
);
let account2 = Pubkey::new_unique();
let account2_len = ExtensionType::try_calculate_account_len::<Mint>(&[
ExtensionType::NonTransferableAccount,
ExtensionType::ImmutableOwner,
])
.unwrap();
let mut account_with_enough_length = SolanaAccount::new(
Rent::default().minimum_balance(account2_len),
account2_len,
&program_id,
);
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let mint_key = Pubkey::new_unique();
let mint_len =
ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::NonTransferable])
.unwrap();
let mut mint_account = SolanaAccount::new(
Rent::default().minimum_balance(mint_len),
mint_len,
&program_id,
);
let mut rent_sysvar = rent_sysvar();
assert_eq!(
Ok(()),
do_process_instruction(
initialize_non_transferable_mint(&program_id, &mint_key).unwrap(),
vec![&mut mint_account],
)
);
assert_eq!(
Ok(()),
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar]
)
);
assert_eq!(
Err(ProgramError::InvalidAccountData),
do_process_instruction(
initialize_account(&program_id, &account, &mint_key, &owner_key).unwrap(),
vec![
&mut account_without_enough_length,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
]
)
);
assert_eq!(
Ok(()),
do_process_instruction(
initialize_account(&program_id, &account2, &mint_key, &owner_key).unwrap(),
vec![
&mut account_with_enough_length,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
]
)
);
}
#[test]
fn test_sync_native() {
let program_id = crate::id();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let native_account_key = Pubkey::new_unique();
let lamports = 40;
let mut native_account = SolanaAccount::new(
account_minimum_balance() + lamports,
Account::get_packed_len(),
&program_id,
);
let non_native_account_key = Pubkey::new_unique();
let mut non_native_account = SolanaAccount::new(
account_minimum_balance() + 50,
Account::get_packed_len(),
&program_id,
);
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
do_process_instruction(
initialize_account(&program_id, &non_native_account_key, &mint_key, &owner_key)
.unwrap(),
vec![
&mut non_native_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
let account = Account::unpack_unchecked(&non_native_account.data).unwrap();
assert!(!account.is_native());
assert_eq!(account.amount, 0);
assert_eq!(
Err(TokenError::NonNativeNotSupported.into()),
do_process_instruction(
sync_native(&program_id, &non_native_account_key,).unwrap(),
vec![&mut non_native_account],
)
);
assert_eq!(
Err(ProgramError::UninitializedAccount),
do_process_instruction(
sync_native(&program_id, &native_account_key,).unwrap(),
vec![&mut native_account],
)
);
do_process_instruction(
initialize_account(
&program_id,
&native_account_key,
&crate::native_mint::id(),
&owner_key,
)
.unwrap(),
vec![
&mut native_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
let not_program_id = Pubkey::new_unique();
native_account.owner = not_program_id;
assert_eq!(
Err(ProgramError::IncorrectProgramId),
do_process_instruction(
sync_native(&program_id, &native_account_key,).unwrap(),
vec![&mut native_account],
)
);
native_account.owner = program_id;
let account = Account::unpack_unchecked(&native_account.data).unwrap();
assert!(account.is_native());
assert_eq!(account.amount, lamports);
do_process_instruction(
sync_native(&program_id, &native_account_key).unwrap(),
vec![&mut native_account],
)
.unwrap();
let account = Account::unpack_unchecked(&native_account.data).unwrap();
assert_eq!(account.amount, lamports);
let new_lamports = lamports + 50;
native_account.lamports = account_minimum_balance() + new_lamports;
do_process_instruction(
sync_native(&program_id, &native_account_key).unwrap(),
vec![&mut native_account],
)
.unwrap();
let account = Account::unpack_unchecked(&native_account.data).unwrap();
assert_eq!(account.amount, new_lamports);
native_account.lamports -= 1;
assert_eq!(
Err(TokenError::InvalidState.into()),
do_process_instruction(
sync_native(&program_id, &native_account_key,).unwrap(),
vec![&mut native_account],
)
);
}
#[test]
#[serial]
fn test_get_account_data_size() {
let program_id = crate::id();
let owner_key = Pubkey::new_unique();
let mut owner_account = SolanaAccount::default();
let mut rent_sysvar = rent_sysvar();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint_key = Pubkey::new_unique();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
set_expected_data(
ExtensionType::try_calculate_account_len::<Account>(&[])
.unwrap()
.to_le_bytes()
.to_vec(),
);
do_process_instruction(
get_account_data_size(&program_id, &mint_key, &[]).unwrap(),
vec![&mut mint_account],
)
.unwrap();
set_expected_data(
ExtensionType::try_calculate_account_len::<Account>(&[
ExtensionType::TransferFeeAmount,
])
.unwrap()
.to_le_bytes()
.to_vec(),
);
do_process_instruction(
get_account_data_size(
&program_id,
&mint_key,
&[
ExtensionType::TransferFeeAmount,
ExtensionType::TransferFeeAmount, ],
)
.unwrap(),
vec![&mut mint_account],
)
.unwrap();
let mut mint_account = native_mint();
set_expected_data(
ExtensionType::try_calculate_account_len::<Account>(&[])
.unwrap()
.to_le_bytes()
.to_vec(),
);
do_process_instruction(
get_account_data_size(&program_id, &mint_key, &[]).unwrap(),
vec![&mut mint_account],
)
.unwrap();
let mint_len =
ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::TransferFeeConfig])
.unwrap();
let mut extended_mint_account = SolanaAccount::new(
Rent::default().minimum_balance(mint_len),
mint_len,
&program_id,
);
let extended_mint_key = Pubkey::new_unique();
do_process_instruction(
initialize_transfer_fee_config(&program_id, &extended_mint_key, None, None, 10, 4242)
.unwrap(),
vec![&mut extended_mint_account],
)
.unwrap();
do_process_instruction(
initialize_mint(&program_id, &extended_mint_key, &owner_key, None, 2).unwrap(),
vec![&mut extended_mint_account, &mut rent_sysvar],
)
.unwrap();
set_expected_data(
ExtensionType::try_calculate_account_len::<Account>(&[
ExtensionType::TransferFeeAmount,
])
.unwrap()
.to_le_bytes()
.to_vec(),
);
do_process_instruction(
get_account_data_size(&program_id, &mint_key, &[]).unwrap(),
vec![&mut extended_mint_account],
)
.unwrap();
do_process_instruction(
get_account_data_size(
&program_id,
&mint_key,
&[ExtensionType::TransferFeeAmount],
)
.unwrap(),
vec![&mut extended_mint_account],
)
.unwrap();
let mut invalid_mint_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let invalid_mint_key = Pubkey::new_unique();
do_process_instruction(
initialize_account(&program_id, &invalid_mint_key, &mint_key, &owner_key).unwrap(),
vec![
&mut invalid_mint_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
)
.unwrap();
assert_eq!(
do_process_instruction(
get_account_data_size(&program_id, &invalid_mint_key, &[]).unwrap(),
vec![&mut invalid_mint_account],
),
Err(TokenError::InvalidMint.into())
);
let invalid_program_id = Pubkey::new_unique();
let mut invalid_mint_account = SolanaAccount::new(
mint_minimum_balance(),
Mint::get_packed_len(),
&invalid_program_id,
);
let invalid_mint_key = Pubkey::new_unique();
let mut instruction =
initialize_mint(&program_id, &invalid_mint_key, &owner_key, None, 2).unwrap();
instruction.program_id = invalid_program_id;
do_process_instruction(
instruction,
vec![&mut invalid_mint_account, &mut rent_sysvar],
)
.unwrap();
assert_eq!(
do_process_instruction(
get_account_data_size(&program_id, &invalid_mint_key, &[]).unwrap(),
vec![&mut invalid_mint_account],
),
Err(ProgramError::IncorrectProgramId)
);
assert_eq!(
do_process_instruction(
get_account_data_size(&program_id, &mint_key, &[ExtensionType::Uninitialized])
.unwrap(),
vec![&mut mint_account],
),
Err(TokenError::ExtensionTypeMismatch.into())
);
assert_eq!(
do_process_instruction(
get_account_data_size(
&program_id,
&mint_key,
&[
ExtensionType::MemoTransfer,
ExtensionType::MintCloseAuthority
]
)
.unwrap(),
vec![&mut mint_account],
),
Err(TokenError::ExtensionTypeMismatch.into())
);
}
#[test]
#[serial]
fn test_amount_to_ui_amount() {
let program_id = crate::id();
let owner_key = Pubkey::new_unique();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mut rent_sysvar = rent_sysvar();
assert_eq!(
Err(TokenError::InvalidMint.into()),
do_process_instruction(
amount_to_ui_amount(&program_id, &mint_key, 110).unwrap(),
vec![&mut mint_account],
)
);
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
set_expected_data("0.23".as_bytes().to_vec());
do_process_instruction(
amount_to_ui_amount(&program_id, &mint_key, 23).unwrap(),
vec![&mut mint_account],
)
.unwrap();
set_expected_data("1.1".as_bytes().to_vec());
do_process_instruction(
amount_to_ui_amount(&program_id, &mint_key, 110).unwrap(),
vec![&mut mint_account],
)
.unwrap();
set_expected_data("42".as_bytes().to_vec());
do_process_instruction(
amount_to_ui_amount(&program_id, &mint_key, 4200).unwrap(),
vec![&mut mint_account],
)
.unwrap();
set_expected_data("0".as_bytes().to_vec());
do_process_instruction(
amount_to_ui_amount(&program_id, &mint_key, 0).unwrap(),
vec![&mut mint_account],
)
.unwrap();
}
#[test]
#[serial]
fn test_ui_amount_to_amount() {
let program_id = crate::id();
let owner_key = Pubkey::new_unique();
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mut rent_sysvar = rent_sysvar();
assert_eq!(
Err(TokenError::InvalidMint.into()),
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "1.1").unwrap(),
vec![&mut mint_account],
)
);
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
set_expected_data(23u64.to_le_bytes().to_vec());
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "0.23").unwrap(),
vec![&mut mint_account],
)
.unwrap();
set_expected_data(20u64.to_le_bytes().to_vec());
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "0.20").unwrap(),
vec![&mut mint_account],
)
.unwrap();
set_expected_data(20u64.to_le_bytes().to_vec());
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "0.2000").unwrap(),
vec![&mut mint_account],
)
.unwrap();
set_expected_data(20u64.to_le_bytes().to_vec());
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, ".20").unwrap(),
vec![&mut mint_account],
)
.unwrap();
set_expected_data(110u64.to_le_bytes().to_vec());
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "1.1").unwrap(),
vec![&mut mint_account],
)
.unwrap();
set_expected_data(110u64.to_le_bytes().to_vec());
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "1.10").unwrap(),
vec![&mut mint_account],
)
.unwrap();
set_expected_data(4200u64.to_le_bytes().to_vec());
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "42").unwrap(),
vec![&mut mint_account],
)
.unwrap();
set_expected_data(4200u64.to_le_bytes().to_vec());
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "42.").unwrap(),
vec![&mut mint_account],
)
.unwrap();
set_expected_data(0u64.to_le_bytes().to_vec());
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "0").unwrap(),
vec![&mut mint_account],
)
.unwrap();
assert_eq!(
Err(ProgramError::InvalidArgument),
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "").unwrap(),
vec![&mut mint_account],
)
);
assert_eq!(
Err(ProgramError::InvalidArgument),
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, ".").unwrap(),
vec![&mut mint_account],
)
);
assert_eq!(
Err(ProgramError::InvalidArgument),
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "0.111").unwrap(),
vec![&mut mint_account],
)
);
assert_eq!(
Err(ProgramError::InvalidArgument),
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "0.t").unwrap(),
vec![&mut mint_account],
)
);
}
#[test]
#[serial]
fn test_withdraw_excess_lamports_from_multisig() {
{
use std::sync::Once;
static ONCE: Once = Once::new();
ONCE.call_once(|| {
solana_sdk::program_stubs::set_syscall_stubs(Box::new(SyscallStubs {}));
});
}
let program_id = crate::id();
let mut lamports = 0;
let mut destination_data = vec![];
let system_program_id = system_program::id();
let destination_key = Pubkey::new_unique();
let destination_info = AccountInfo::new(
&destination_key,
true,
false,
&mut lamports,
&mut destination_data,
&system_program_id,
false,
Epoch::default(),
);
let multisig_key = Pubkey::new_unique();
let mut multisig_account = SolanaAccount::new(0, Multisig::get_packed_len(), &program_id);
let excess_lamports = 4_000_000_000_000;
multisig_account.lamports = excess_lamports + multisig_minimum_balance();
let mut signer_keys = [Pubkey::default(); MAX_SIGNERS];
for signer_key in signer_keys.iter_mut().take(MAX_SIGNERS) {
*signer_key = Pubkey::new_unique();
}
let signer_refs: Vec<&Pubkey> = signer_keys.iter().collect();
let mut signer_lamports = 0;
let mut signer_data = vec![];
let mut signers: Vec<AccountInfo<'_>> = vec![
AccountInfo::new(
&destination_key,
true,
false,
&mut signer_lamports,
&mut signer_data,
&program_id,
false,
Epoch::default(),
);
MAX_SIGNERS + 1
];
for (signer, key) in signers.iter_mut().zip(&signer_keys) {
signer.key = key;
}
let mut multisig =
Multisig::unpack_unchecked(&vec![0; Multisig::get_packed_len()]).unwrap();
multisig.m = MAX_SIGNERS as u8;
multisig.n = MAX_SIGNERS as u8;
multisig.signers = signer_keys;
multisig.is_initialized = true;
Multisig::pack(multisig, &mut multisig_account.data).unwrap();
let multisig_info: AccountInfo = (&multisig_key, true, &mut multisig_account).into();
let mut signers_infos = vec![
multisig_info.clone(),
destination_info.clone(),
multisig_info.clone(),
];
signers_infos.extend(signers);
do_process_instruction_dups(
withdraw_excess_lamports(
&program_id,
&multisig_key,
&destination_key,
&multisig_key,
&signer_refs,
)
.unwrap(),
signers_infos,
)
.unwrap();
assert_eq!(destination_info.lamports(), excess_lamports);
}
#[test]
#[serial]
fn test_withdraw_excess_lamports_from_account() {
let excess_lamports = 4_000_000_000_000;
let program_id = crate::id();
let account_key = Pubkey::new_unique();
let mut account_account = SolanaAccount::new(
excess_lamports + account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let system_program_id = system_program::id();
let owner_key = Pubkey::new_unique();
let mut destination_lamports = 0;
let mut destination_data = vec![];
let destination_key = Pubkey::new_unique();
let destination_info = AccountInfo::new(
&destination_key,
true,
false,
&mut destination_lamports,
&mut destination_data,
&system_program_id,
false,
Epoch::default(),
);
let mint_key = Pubkey::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
let mint_info = AccountInfo::new(
&mint_key,
true,
false,
&mut mint_account.lamports,
&mut mint_account.data,
&program_id,
false,
Epoch::default(),
);
let account_info: AccountInfo = (&account_key, true, &mut account_account).into();
do_process_instruction_dups(
initialize_account3(&program_id, &account_key, &mint_key, &account_key).unwrap(),
vec![account_info.clone(), mint_info.clone()],
)
.unwrap();
do_process_instruction_dups(
withdraw_excess_lamports(
&program_id,
&account_key,
&destination_key,
&account_key,
&[],
)
.unwrap(),
vec![
account_info.clone(),
destination_info.clone(),
account_info.clone(),
],
)
.unwrap();
assert_eq!(destination_info.lamports(), excess_lamports);
}
#[test]
#[serial]
fn test_withdraw_excess_lamports_from_mint() {
let excess_lamports = 4_000_000_000_000;
let program_id = crate::id();
let system_program_id = system_program::id();
let mut destination_lamports = 0;
let mut destination_data = vec![];
let destination_key = Pubkey::new_unique();
let destination_info = AccountInfo::new(
&destination_key,
true,
false,
&mut destination_lamports,
&mut destination_data,
&system_program_id,
false,
Epoch::default(),
);
let mint_key = Pubkey::new_unique();
let mut mint_account = SolanaAccount::new(
excess_lamports + mint_minimum_balance(),
Mint::get_packed_len(),
&program_id,
);
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &mint_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
)
.unwrap();
let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into();
do_process_instruction_dups(
withdraw_excess_lamports(&program_id, &mint_key, &destination_key, &mint_key, &[])
.unwrap(),
vec![
mint_info.clone(),
destination_info.clone(),
mint_info.clone(),
],
)
.unwrap();
assert_eq!(destination_info.lamports(), excess_lamports);
}
}