use {
crate::{
extension::{
confidential_mint_burn, confidential_transfer, confidential_transfer_fee,
cpi_guard::{self, in_cpi},
default_account_state, group_member_pointer, group_pointer, interest_bearing_mint,
memo_transfer::{self, check_previous_sibling_instruction_is_memo},
metadata_pointer, pausable, permissioned_burn, reallocate, scaled_ui_amount,
token_group, token_metadata, transfer_fee, transfer_hook,
},
pod_instruction::{
decode_instruction_data_with_coption_pubkey, decode_instruction_data_with_coption_u64,
AmountCheckedData, AmountData, InitializeMintData, InitializeMultisigData,
PodTokenInstruction, SetAuthorityData,
},
},
pinocchio::{
account::next_account_view,
cpi::{Seed, Signer},
sysvars::{clock::Clock, rent::Rent, Sysvar},
AccountView,
},
pinocchio_system::instructions::{CreateAccountAllowPrefund, Funding},
pinocchio_transfer_hook_interface::instructions::Execute,
solana_address::Address,
solana_cpi::set_return_data,
solana_msg::msg,
solana_program_error::{ProgramError, ProgramResult},
solana_program_pack::Pack,
solana_zero_copy::unaligned::U64,
spl_token_2022_interface::{
check_program_account,
error::TokenError,
extension::{
account_len::{
try_calculate_account_len_from_mint_data,
try_for_each_required_init_account_extension,
},
confidential_mint_burn::ConfidentialMintBurn,
confidential_transfer::{ConfidentialTransferAccount, ConfidentialTransferMint},
confidential_transfer_fee::{
ConfidentialTransferFeeAmount, ConfidentialTransferFeeConfig,
},
cpi_guard::CpiGuard,
default_account_state::DefaultAccountState,
group_member_pointer::GroupMemberPointer,
group_pointer::GroupPointer,
immutable_owner::ImmutableOwner,
interest_bearing_mint::InterestBearingConfig,
memo_transfer::memo_required,
metadata_pointer::MetadataPointer,
mint_close_authority::MintCloseAuthority,
non_transferable::{NonTransferable, NonTransferableAccount},
pausable::{PausableAccount, PausableConfig},
permanent_delegate::{get_permanent_delegate, PermanentDelegate},
permissioned_burn::PermissionedBurnConfig,
scaled_ui_amount::ScaledUiAmountConfig,
transfer_fee::{TransferFeeAmount, TransferFeeConfig},
transfer_hook::{TransferHook, TransferHookAccount},
BaseStateWithExtensions, BaseStateWithExtensionsMut, ExtensionType,
PodStateWithExtensions, PodStateWithExtensionsMut,
},
inline_spl_token,
instruction::{
decode_instruction_data, decode_instruction_type, is_valid_signer_index, AuthorityType,
MAX_SIGNERS,
},
native_mint::{self, PROGRAM_ADDRESS_SEEDS},
pod::{PodAccount, PodCOption, PodMint, PodMultisig},
state::{AccountState, Mint, PackedSizeOf},
},
spl_token_group_interface::instruction::TokenGroupInstruction,
spl_token_metadata_interface::instruction::TokenMetadataInstruction,
std::convert::{TryFrom, TryInto},
};
pub(crate) enum TransferInstruction {
Unchecked,
Checked { decimals: u8 },
CheckedWithFee { decimals: u8, fee: u64 },
}
pub(crate) enum InstructionVariant {
Unchecked,
Checked { decimals: u8 },
}
pub(crate) enum BurnInstructionVariant {
Standard,
Permissioned,
}
pub struct Processor {}
impl Processor {
fn _process_initialize_mint(
accounts: &mut [AccountView],
decimals: u8,
mint_authority: &Address,
freeze_authority: PodCOption<Address>,
rent_sysvar_account: bool,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let mint_info = next_account_view(account_info_iter)?;
let mint_data_len = mint_info.data_len();
let mint_owner = *mint_info.owner();
let mint_lamports = mint_info.lamports();
let mut mint_data = mint_info.try_borrow_mut()?;
check_program_account(&mint_owner)?;
let rent = if rent_sysvar_account {
Rent::from_account_view(next_account_view(account_info_iter)?)?
} else {
Rent::get()?
};
if !rent.is_exempt(mint_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 = true.into();
mint.base.freeze_authority = freeze_authority;
mint.init_account_type()?;
Ok(())
}
pub fn process_initialize_mint(
accounts: &mut [AccountView],
decimals: u8,
mint_authority: &Address,
freeze_authority: PodCOption<Address>,
) -> ProgramResult {
Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority, true)
}
pub fn process_initialize_mint2(
accounts: &mut [AccountView],
decimals: u8,
mint_authority: &Address,
freeze_authority: PodCOption<Address>,
) -> ProgramResult {
Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority, false)
}
fn _process_initialize_account(
accounts: &mut [AccountView],
owner: Option<&Address>,
rent_sysvar_account: bool,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let new_account_info = next_account_view(account_info_iter)?;
let mint_info = next_account_view(account_info_iter)?;
check_program_account(new_account_info.owner())?;
check_program_account(mint_info.owner())?;
let owner = if let Some(owner) = owner {
owner
} else {
next_account_view(account_info_iter)?.address()
};
let new_account_info_data_len = new_account_info.data_len();
let rent = if rent_sysvar_account {
Rent::from_account_view(next_account_view(account_info_iter)?)?
} else {
Rent::get()?
};
let new_account_lamports = new_account_info.lamports();
let mut account_data = new_account_info.try_borrow_mut()?;
let mut account =
PodStateWithExtensionsMut::<PodAccount>::unpack_uninitialized(&mut account_data)?;
if !rent.is_exempt(new_account_lamports, new_account_info_data_len) {
return Err(TokenError::NotRentExempt.into());
}
let mint_data = mint_info.try_borrow()?;
let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data)
.map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
if mint
.get_extension::<PermanentDelegate>()
.map(|e| Option::<Address>::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");
}
check_program_account(mint_info.owner())?;
let required_account_len = try_calculate_account_len_from_mint_data(&mint_data, &[])?;
if required_account_len > new_account_info_data_len {
return Err(ProgramError::InvalidAccountData);
}
try_for_each_required_init_account_extension(mint.get_tlv_data(), |extension_type| {
account.init_account_extension_from_type(extension_type)
})?;
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.address();
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.address() == &native_mint::id() {
let rent_exempt_reserve = rent.try_minimum_balance(new_account_info_data_len)?;
account.base.is_native = PodCOption::some(rent_exempt_reserve.into());
account.base.amount = new_account_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: &mut [AccountView]) -> ProgramResult {
Self::_process_initialize_account(accounts, None, true)
}
pub fn process_initialize_account2(
accounts: &mut [AccountView],
owner: &Address,
) -> ProgramResult {
Self::_process_initialize_account(accounts, Some(owner), true)
}
pub fn process_initialize_account3(
accounts: &mut [AccountView],
owner: &Address,
) -> ProgramResult {
Self::_process_initialize_account(accounts, Some(owner), false)
}
fn _process_initialize_multisig(
accounts: &mut [AccountView],
m: u8,
rent_sysvar_account: bool,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let multisig_info = next_account_view(account_info_iter)?;
check_program_account(multisig_info.owner())?;
let multisig_info_data_len = multisig_info.data_len();
let rent = if rent_sysvar_account {
Rent::from_account_view(next_account_view(account_info_iter)?)?
} else {
Rent::get()?
};
let multisig_lamports = multisig_info.lamports();
let mut multisig_data = multisig_info.try_borrow_mut()?;
let multisig = bytemuck::try_from_bytes_mut::<PodMultisig>(&mut multisig_data)
.map_err(|_| ProgramError::InvalidArgument)?;
if bool::from(multisig.is_initialized) {
return Err(TokenError::AlreadyInUse.into());
}
if !rent.is_exempt(multisig_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.address();
}
multisig.is_initialized = true.into();
Ok(())
}
pub fn process_initialize_multisig(accounts: &mut [AccountView], m: u8) -> ProgramResult {
Self::_process_initialize_multisig(accounts, m, true)
}
pub fn process_initialize_multisig2(accounts: &mut [AccountView], m: u8) -> ProgramResult {
Self::_process_initialize_multisig(accounts, m, false)
}
pub(crate) fn process_transfer(
program_id: &Address,
accounts: &mut [AccountView],
amount: u64,
transfer_instruction: TransferInstruction,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let source_account_info = next_account_view(account_info_iter)?;
let expected_mint_info = match transfer_instruction {
TransferInstruction::Unchecked => None,
TransferInstruction::Checked { decimals }
| TransferInstruction::CheckedWithFee { decimals, .. } => {
Some((next_account_view(account_info_iter)?, decimals))
}
};
let destination_account_info = next_account_view(account_info_iter)?;
let authority_info = next_account_view(account_info_iter)?;
let authority_info_data_len = authority_info.data_len();
check_program_account(source_account_info.owner())?;
check_program_account(destination_account_info.owner())?;
let mut cloned_source_account_info = *source_account_info;
let mut source_account_data = cloned_source_account_info.try_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 (calculated_fee, maybe_permanent_delegate, maybe_transfer_hook_program_id) =
if let Some((ref mint_info, expected_decimals)) = expected_mint_info {
check_program_account(mint_info.owner())?;
if &source_account.base.mint != mint_info.address() {
return Err(TokenError::MintMismatch.into());
}
let mint_data = mint_info.try_borrow()?;
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
};
if let Ok(extension) = mint.get_extension::<PausableConfig>() {
if extension.paused.into() {
return Err(TokenError::MintPaused.into());
}
}
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());
}
if source_account.get_extension::<PausableAccount>().is_ok() {
return Err(TokenError::MintRequiredForTransfer.into());
}
(0, None, None)
};
if let TransferInstruction::CheckedWithFee { fee, .. } = transfer_instruction {
if calculated_fee != fee {
msg!("Calculated fee {calculated_fee}, received {fee}");
return Err(TokenError::FeeMismatch.into());
}
}
let self_transfer = source_account_info.address() == destination_account_info.address();
if let Ok(cpi_guard) = source_account.get_extension::<CpiGuard>() {
if *authority_info.address() == 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.address() == delegate => {
Self::validate_owner(
program_id,
delegate,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?
}
(
PodCOption {
option: PodCOption::<Address>::SOME,
value: delegate,
},
_,
) if authority_info.address() == &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(),
)?;
}
}
if self_transfer {
if memo_required(&source_account) {
check_previous_sibling_instruction_is_memo()?;
}
return Ok(());
}
let mut cloned_destination_account_info = *destination_account_info;
let mut destination_account_data = cloned_destination_account_info.try_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(calculated_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 calculated_fee > 0 {
if let Ok(extension) = destination_account.get_extension_mut::<TransferFeeAmount>() {
let new_withheld_amount = u64::from(extension.withheld_amount)
.checked_add(calculated_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.set_lamports(
source_starting_lamports
.checked_sub(amount)
.ok_or(TokenError::Overflow)?,
);
let destination_starting_lamports = destination_account_info.lamports();
destination_account_info.set_lamports(
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);
Execute {
program_id: &program_id,
source: source_account_info,
mint: mint_info,
destination: destination_account_info,
authority: authority_info,
additional_accounts: account_info_iter.as_slice(),
amount,
}
.invoke()?;
crate::extension::unset_transferring(source_account_info)?;
crate::extension::unset_transferring(destination_account_info)?;
} else {
return Err(TokenError::MintRequiredForTransfer.into());
}
}
Ok(())
}
pub(crate) fn process_approve(
program_id: &Address,
accounts: &mut [AccountView],
amount: u64,
instruction_variant: InstructionVariant,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let source_account_info = next_account_view(account_info_iter)?;
check_program_account(source_account_info.owner())?;
let expected_mint_info =
if let InstructionVariant::Checked { decimals } = instruction_variant {
Some((next_account_view(account_info_iter)?, decimals))
} else {
None
};
let delegate_info = next_account_view(account_info_iter)?;
let owner_info = next_account_view(account_info_iter)?;
let owner_info_data_len = owner_info.data_len();
let mut source_account_data = source_account_info.try_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 {
check_program_account(mint_info.owner())?;
if &source_account.base.mint != mint_info.address() {
return Err(TokenError::MintMismatch.into());
}
let mint_data = mint_info.try_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.address());
source_account.base.delegated_amount = amount.into();
Ok(())
}
pub fn process_revoke(program_id: &Address, accounts: &mut [AccountView]) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let source_account_info = next_account_view(account_info_iter)?;
let authority_info = next_account_view(account_info_iter)?;
let authority_info_data_len = authority_info.data_len();
check_program_account(source_account_info.owner())?;
let mut source_account_data = source_account_info.try_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::<Address>::SOME,
value: delegate,
} if authority_info.address() == 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: &Address,
accounts: &mut [AccountView],
authority_type: AuthorityType,
new_authority: PodCOption<Address>,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let account_info = next_account_view(account_info_iter)?;
let authority_info = next_account_view(account_info_iter)?;
let authority_info_data_len = authority_info.data_len();
check_program_account(account_info.owner())?;
let mut account_data = account_info.try_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::<Address>::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<Address> = 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<Address> =
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<Address> =
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<Address> = 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<Address> = 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<Address> =
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<Address> = 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<Address> = 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<Address> = 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<Address> = 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<Address> = 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::ScaledUiAmount => {
let extension = mint.get_extension_mut::<ScaledUiAmountConfig>()?;
let maybe_authority: Option<Address> = 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::Pause => {
let extension = mint.get_extension_mut::<PausableConfig>()?;
let maybe_authority: Option<Address> = 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::PermissionedBurn => {
let extension = mint.get_extension_mut::<PermissionedBurnConfig>()?;
let maybe_authority: Option<Address> = 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(crate) fn process_mint_to(
program_id: &Address,
accounts: &mut [AccountView],
amount: u64,
instruction_variant: InstructionVariant,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let mint_info = next_account_view(account_info_iter)?;
let destination_account_info = next_account_view(account_info_iter)?;
let owner_info = next_account_view(account_info_iter)?;
let owner_info_data_len = owner_info.data_len();
check_program_account(mint_info.owner())?;
check_program_account(destination_account_info.owner())?;
let mut destination_account_data = destination_account_info.try_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.address() != &destination_account.base.mint {
return Err(TokenError::MintMismatch.into());
}
let mut mint_data = mint_info.try_borrow_mut()?;
let mint = PodStateWithExtensionsMut::<PodMint>::unpack(&mut mint_data)?;
if mint.get_extension::<NonTransferable>().is_ok()
&& (destination_account
.get_extension::<ImmutableOwner>()
.is_err()
|| destination_account
.get_extension::<NonTransferableAccount>()
.is_err())
{
return Err(TokenError::NonTransferableNeedsImmutableOwnership.into());
}
if let Ok(extension) = mint.get_extension::<PausableConfig>() {
if extension.paused.into() {
return Err(TokenError::MintPaused.into());
}
}
if mint.get_extension::<ConfidentialMintBurn>().is_ok() {
return Err(TokenError::IllegalMintBurnConversion.into());
}
if let InstructionVariant::Checked { decimals } = instruction_variant {
if decimals != mint.base.decimals {
return Err(TokenError::MintDecimalsMismatch.into());
}
}
match &mint.base.mint_authority {
PodCOption {
option: PodCOption::<Address>::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()),
}
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(crate) fn process_burn(
program_id: &Address,
accounts: &mut [AccountView],
amount: u64,
burn_variant: BurnInstructionVariant,
instruction_variant: InstructionVariant,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let source_account_info = next_account_view(account_info_iter)?;
let mint_info = next_account_view(account_info_iter)?;
check_program_account(source_account_info.owner())?;
check_program_account(mint_info.owner())?;
let (permissioned_burn_authority_info, authority_info) = match burn_variant {
BurnInstructionVariant::Permissioned => {
let permissioned_burn_authority_info = next_account_view(account_info_iter)?;
let authority_info = next_account_view(account_info_iter)?;
(Some(permissioned_burn_authority_info), authority_info)
}
BurnInstructionVariant::Standard => (None, next_account_view(account_info_iter)?),
};
let authority_info_data_len = authority_info.data_len();
let mint_address = *mint_info.address();
let mut mint_data = mint_info.try_borrow_mut()?;
let mint = PodStateWithExtensionsMut::<PodMint>::unpack(&mut mint_data)?;
let permissioned_ext = mint.get_extension::<PermissionedBurnConfig>();
let maybe_permissioned_burn_authority = permissioned_ext
.as_ref()
.ok()
.and_then(|ext| Option::<Address>::from(ext.authority));
match burn_variant {
BurnInstructionVariant::Standard => {
if maybe_permissioned_burn_authority.is_some() {
return Err(TokenError::InvalidInstruction.into());
}
}
BurnInstructionVariant::Permissioned => {
permissioned_ext.map_err(|_| TokenError::InvalidInstruction)?;
let expected_burn_authority =
maybe_permissioned_burn_authority.ok_or_else(|| {
msg!("Permissioned burn authority is None; use the standard burn");
TokenError::InvalidInstruction
})?;
let approver_ai =
permissioned_burn_authority_info.ok_or(ProgramError::NotEnoughAccountKeys)?;
if !approver_ai.is_signer() {
return Err(ProgramError::MissingRequiredSignature);
}
if *approver_ai.address() != expected_burn_authority {
return Err(ProgramError::InvalidAccountData);
}
}
}
let mut source_account_data = source_account_info.try_borrow_mut()?;
let source_account =
PodStateWithExtensionsMut::<PodAccount>::unpack(&mut source_account_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_address != source_account.base.mint {
return Err(TokenError::MintMismatch.into());
}
if let InstructionVariant::Checked { decimals } = instruction_variant {
if decimals != mint.base.decimals {
return Err(TokenError::MintDecimalsMismatch.into());
}
}
if let Ok(extension) = mint.get_extension::<PausableConfig>() {
if extension.paused.into() {
return Err(TokenError::MintPaused.into());
}
}
if mint.get_extension::<ConfidentialMintBurn>().is_ok() {
return Err(TokenError::IllegalMintBurnConversion.into());
}
let maybe_permanent_delegate = get_permanent_delegate(&mint);
if let Ok(cpi_guard) = source_account.get_extension::<CpiGuard>() {
if *authority_info.address() == 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.address() == delegate => {
Self::validate_owner(
program_id,
delegate,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?
}
(
PodCOption {
option: PodCOption::<Address>::SOME,
value: delegate,
},
_,
) if authority_info.address() == 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(),
)?;
}
}
}
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: &Address,
accounts: &mut [AccountView],
) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let source_account_info = next_account_view(account_info_iter)?;
let destination_account_info = next_account_view(account_info_iter)?;
let authority_info = next_account_view(account_info_iter)?;
let authority_info_data_len = authority_info.data_len();
check_program_account(source_account_info.owner())?;
if source_account_info.address() == destination_account_info.address() {
return Err(ProgramError::InvalidAccountData);
}
let source_account_data = source_account_info.try_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.address() != &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_sdk_ids::incinerator::check_id(destination_account_info.address()) {
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<Address> = 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());
}
if let Ok(confidential_mint_burn) = mint.get_extension::<ConfidentialMintBurn>() {
confidential_mint_burn.closable()?;
}
} else {
return Err(ProgramError::UninitializedAccount);
}
let destination_starting_lamports = destination_account_info.lamports();
destination_account_info.set_lamports(
destination_starting_lamports
.checked_add(source_account_info.lamports())
.ok_or(TokenError::Overflow)?,
);
drop(source_account_data);
unsafe { source_account_info.close_unchecked() };
Ok(())
}
pub fn process_toggle_freeze_account(
program_id: &Address,
accounts: &mut [AccountView],
freeze: bool,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let source_account_info = next_account_view(account_info_iter)?;
let mint_info = next_account_view(account_info_iter)?;
let authority_info = next_account_view(account_info_iter)?;
let authority_info_data_len = authority_info.data_len();
check_program_account(source_account_info.owner())?;
check_program_account(mint_info.owner())?;
let mut source_account_data = source_account_info.try_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.address() != &source_account.base.mint {
return Err(TokenError::MintMismatch.into());
}
let mint_data = mint_info.try_borrow()?;
let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data)?;
match &mint.base.freeze_authority {
PodCOption {
option: PodCOption::<Address>::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: &mut [AccountView]) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let native_account_info = next_account_view(account_info_iter)?;
check_program_account(native_account_info.owner())?;
let rent_exempt_reserve = if let Ok(rent_sysvar_info) = next_account_view(account_info_iter)
{
let rent = Rent::from_account_view(rent_sysvar_info)?;
rent.try_minimum_balance(native_account_info.data_len())?
} else {
Rent::get()?.try_minimum_balance(native_account_info.data_len())?
};
let native_account_lamports = native_account_info.lamports();
let mut native_account_data = native_account_info.try_borrow_mut()?;
let native_account =
PodStateWithExtensionsMut::<PodAccount>::unpack(&mut native_account_data)?;
match native_account.base.is_native {
PodCOption {
option: PodCOption::<U64>::SOME,
..
} => {
let new_amount = native_account_lamports
.checked_sub(rent_exempt_reserve)
.ok_or(TokenError::Overflow)?;
native_account.base.is_native = PodCOption::some(rent_exempt_reserve.into());
native_account.base.amount = new_amount.into();
}
_ => return Err(TokenError::NonNativeNotSupported.into()),
}
Ok(())
}
pub fn process_initialize_mint_close_authority(
accounts: &mut [AccountView],
close_authority: PodCOption<Address>,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let mint_account_info = next_account_view(account_info_iter)?;
check_program_account(mint_account_info.owner())?;
let mut mint_data = mint_account_info.try_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: &[AccountView],
new_extension_types: &[ExtensionType],
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let mint_account_info = next_account_view(account_info_iter)?;
check_program_account(mint_account_info.owner())?;
let mint_data = mint_account_info.try_borrow()?;
let account_len =
try_calculate_account_len_from_mint_data(&mint_data, new_extension_types)?;
set_return_data(&account_len.to_le_bytes());
Ok(())
}
pub fn process_initialize_immutable_owner(accounts: &mut [AccountView]) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let token_account_info = next_account_view(account_info_iter)?;
check_program_account(token_account_info.owner())?;
let token_account_data = &mut token_account_info.try_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: &[AccountView], amount: u64) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let mint_info = next_account_view(account_info_iter)?;
check_program_account(mint_info.owner())?;
let mint_data = mint_info.try_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 if let Ok(extension) = mint.get_extension::<ScaledUiAmountConfig>() {
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: &[AccountView], ui_amount: &str) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let mint_info = next_account_view(account_info_iter)?;
check_program_account(mint_info.owner())?;
let mint_data = mint_info.try_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 if let Ok(extension) = mint.get_extension::<ScaledUiAmountConfig>() {
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: &mut [AccountView]) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let payer_info = next_account_view(account_info_iter)?;
let native_mint_info = next_account_view(account_info_iter)?;
let _system_program_info = next_account_view(account_info_iter)?;
if *native_mint_info.address() != native_mint::id() {
return Err(TokenError::InvalidMint.into());
}
let rent = Rent::get()?;
let new_minimum_balance = rent.try_minimum_balance(Mint::get_packed_len())?;
let lamports_diff = new_minimum_balance.saturating_sub(native_mint_info.lamports());
let signer_seeds = &[
Seed::from(PROGRAM_ADDRESS_SEEDS[0]),
Seed::from(PROGRAM_ADDRESS_SEEDS[1]),
];
CreateAccountAllowPrefund {
funding: Some(Funding {
from: payer_info,
lamports: lamports_diff,
}),
to: native_mint_info,
space: Mint::get_packed_len() as u64,
owner: &crate::ID,
}
.invoke_signed(&[Signer::from(signer_seeds)])?;
Mint::pack(
Mint {
decimals: native_mint::DECIMALS,
is_initialized: true,
..Mint::default()
},
&mut native_mint_info.try_borrow_mut()?,
)
}
pub fn process_initialize_non_transferable_mint(accounts: &mut [AccountView]) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let mint_account_info = next_account_view(account_info_iter)?;
check_program_account(mint_account_info.owner())?;
let mut mint_data = mint_account_info.try_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: &mut [AccountView],
delegate: &Address,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let mint_account_info = next_account_view(account_info_iter)?;
check_program_account(mint_account_info.owner())?;
let mut mint_data = mint_account_info.try_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()
.map_err(|_| ProgramError::InvalidArgument)?;
Ok(())
}
pub fn process_withdraw_excess_lamports(
program_id: &Address,
accounts: &mut [AccountView],
) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let source_info = next_account_view(account_info_iter)?;
let destination_info = next_account_view(account_info_iter)?;
let authority_info = next_account_view(account_info_iter)?;
check_program_account(source_info.owner())?;
let source_data = source_info.try_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(),
)?;
if let Ok(cpi_guard) = account.get_extension::<CpiGuard>() {
if cpi_guard.lock_cpi.into() && in_cpi() {
return Err(TokenError::CpiGuardTransferBlocked.into());
}
}
} else if let Ok(mint) = PodStateWithExtensions::<PodMint>::unpack(&source_data) {
match &mint.base.mint_authority {
PodCOption {
option: PodCOption::<Address>::SOME,
value: mint_authority,
} => {
Self::validate_owner(
program_id,
mint_authority,
authority_info,
authority_info.data_len(),
account_info_iter.as_slice(),
)?;
}
PodCOption {
option: PodCOption::<Address>::NONE,
value: _,
} if source_info.address() == authority_info.address() => {
if !authority_info.is_signer() {
return Err(ProgramError::MissingRequiredSignature);
}
}
_ => return Err(TokenError::AuthorityTypeNotSupported.into()),
}
} else if source_data.len() == PodMultisig::SIZE_OF {
Self::validate_owner(
program_id,
source_info.address(),
authority_info,
authority_info.data_len(),
account_info_iter.as_slice(),
)?;
} else {
return Err(TokenError::InvalidState.into());
}
drop(source_data);
let source_rent_exempt_reserve =
Rent::get()?.try_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.set_lamports(
source_starting_lamports
.checked_sub(transfer_amount)
.ok_or(TokenError::Overflow)?,
);
let destination_starting_lamports = destination_info.lamports();
destination_info.set_lamports(
destination_starting_lamports
.checked_add(transfer_amount)
.ok_or(TokenError::Overflow)?,
);
Ok(())
}
pub fn process_unwrap_lamports(
program_id: &Address,
accounts: &mut [AccountView],
amount: PodCOption<u64>,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter_mut();
let source_account_info = next_account_view(account_info_iter)?;
let destination_account_info = next_account_view(account_info_iter)?;
let authority_info = next_account_view(account_info_iter)?;
let authority_info_data_len = authority_info.data_len();
check_program_account(source_account_info.owner())?;
let mut source_account_data = source_account_info.try_borrow_mut()?;
let source_account =
PodStateWithExtensionsMut::<PodAccount>::unpack(&mut source_account_data)?;
let (amount, remaining_amount) = match amount {
PodCOption {
option: PodCOption::<u64>::SOME,
value: amount,
} => (
amount,
Into::<u64>::into(source_account.base.amount)
.checked_sub(amount)
.ok_or(TokenError::InsufficientFunds)?,
),
PodCOption {
option: PodCOption::<u64>::NONE,
value: _,
} => (source_account.base.amount.into(), 0),
_ => return Err(ProgramError::InvalidInstructionData),
};
if !source_account.base.is_native() {
return Err(TokenError::NonNativeNotSupported.into());
}
match source_account.base.delegate {
PodCOption {
option: PodCOption::<Address>::SOME,
value: delegate,
} if authority_info.address() == &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);
source_account.base.delegated_amount = delegated_amount
.checked_sub(amount)
.ok_or(TokenError::InsufficientFunds)?
.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(),
)?;
}
}
if let Ok(cpi_guard) = source_account.get_extension::<CpiGuard>() {
if cpi_guard.lock_cpi.into() && in_cpi() {
return Err(TokenError::CpiGuardTransferBlocked.into());
}
}
if amount != 0 {
source_account.base.amount = remaining_amount.into();
drop(source_account_data);
if source_account_info.address() != destination_account_info.address() {
let source_starting_lamports = source_account_info.lamports();
source_account_info.set_lamports(
source_starting_lamports
.checked_sub(amount)
.ok_or(TokenError::Overflow)?,
);
let destination_starting_lamports = destination_account_info.lamports();
destination_account_info.set_lamports(
destination_starting_lamports
.checked_add(amount)
.ok_or(TokenError::Overflow)?,
);
}
}
Ok(())
}
const IX_HEADER_SIZE: usize = 2;
pub fn process_batch(
program_id: &Address,
mut accounts: &mut [AccountView],
mut data: &[u8],
) -> ProgramResult {
loop {
let header = data
.get(..Self::IX_HEADER_SIZE)
.ok_or(TokenError::InvalidInstruction)?;
let expected_accounts = header[0] as usize;
let data_offset = Self::IX_HEADER_SIZE + header[1] as usize;
let ix_accounts = accounts
.get_mut(..expected_accounts)
.ok_or(ProgramError::NotEnoughAccountKeys)?;
let ix_data = data
.get(Self::IX_HEADER_SIZE..data_offset)
.ok_or(TokenError::InvalidInstruction)?;
Self::_process_inner(program_id, ix_accounts, ix_data)?;
if data_offset == data.len() {
break;
}
accounts = &mut accounts[expected_accounts..];
data = &data[data_offset..];
}
Ok(())
}
pub fn process(
program_id: &Address,
accounts: &mut [AccountView],
input: &[u8],
) -> ProgramResult {
if let Ok(PodTokenInstruction::Batch) = decode_instruction_type(input) {
msg!("Instruction: Batch");
Self::process_batch(program_id, accounts, &input[1..])
} else {
Self::_process_inner(program_id, accounts, input)
}
}
fn _process_inner(
program_id: &Address,
accounts: &mut [AccountView],
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::<Address>(input)?;
Self::process_initialize_account2(accounts, owner)
}
PodTokenInstruction::InitializeAccount3 => {
msg!("Instruction: InitializeAccount3");
let owner = decode_instruction_data::<Address>(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(),
TransferInstruction::Unchecked,
)
}
PodTokenInstruction::Approve => {
msg!("Instruction: Approve");
let data = decode_instruction_data::<AmountData>(input)?;
Self::process_approve(
program_id,
accounts,
data.amount.into(),
InstructionVariant::Unchecked,
)
}
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(),
InstructionVariant::Unchecked,
)
}
PodTokenInstruction::Burn => {
msg!("Instruction: Burn");
let data = decode_instruction_data::<AmountData>(input)?;
Self::process_burn(
program_id,
accounts,
data.amount.into(),
BurnInstructionVariant::Standard,
InstructionVariant::Unchecked,
)
}
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(),
TransferInstruction::Checked {
decimals: data.decimals,
},
)
}
PodTokenInstruction::ApproveChecked => {
msg!("Instruction: ApproveChecked");
let data = decode_instruction_data::<AmountCheckedData>(input)?;
Self::process_approve(
program_id,
accounts,
data.amount.into(),
InstructionVariant::Checked {
decimals: data.decimals,
},
)
}
PodTokenInstruction::MintToChecked => {
msg!("Instruction: MintToChecked");
let data = decode_instruction_data::<AmountCheckedData>(input)?;
Self::process_mint_to(
program_id,
accounts,
data.amount.into(),
InstructionVariant::Checked {
decimals: data.decimals,
},
)
}
PodTokenInstruction::BurnChecked => {
msg!("Instruction: BurnChecked");
let data = decode_instruction_data::<AmountCheckedData>(input)?;
Self::process_burn(
program_id,
accounts,
data.amount.into(),
BurnInstructionVariant::Standard,
InstructionVariant::Checked {
decimals: 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::<Address>(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..],
)
}
PodTokenInstruction::ConfidentialMintBurnExtension => {
msg!("Instruction: ConfidentialMintBurnExtension");
confidential_mint_burn::processor::process_instruction(
program_id,
accounts,
&input[1..],
)
}
PodTokenInstruction::ScaledUiAmountExtension => {
msg!("Instruction: ScaledUiAmountExtension");
scaled_ui_amount::processor::process_instruction(
program_id,
accounts,
&input[1..],
)
}
PodTokenInstruction::PausableExtension => {
msg!("Instruction: PausableExtension");
pausable::processor::process_instruction(program_id, accounts, &input[1..])
}
PodTokenInstruction::PermissionedBurnExtension => {
msg!("Instruction: PermissionedBurnExtension");
permissioned_burn::processor::process_instruction(
program_id,
accounts,
&input[1..],
)
}
PodTokenInstruction::UnwrapLamports => {
msg!("Instruction: UnwrapLamports");
let (_, amount) = decode_instruction_data_with_coption_u64::<()>(input)?;
Self::process_unwrap_lamports(program_id, accounts, amount)
}
PodTokenInstruction::Batch => Err(TokenError::InvalidInstruction.into()),
}
} 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: &Address,
expected_owner: &Address,
owner_account_info: &AccountView,
owner_account_data_len: usize,
signers: &[AccountView],
) -> ProgramResult {
if expected_owner != owner_account_info.address() {
return Err(TokenError::OwnerMismatch.into());
}
let owned_by_token_program = program_id == owner_account_info.owner()
|| owner_account_info.owner() == &inline_spl_token::id()
|| owner_account_info.owner() == &spl_token_2022_interface::id();
if owned_by_token_program && owner_account_data_len == PodMultisig::SIZE_OF {
let multisig_data = &owner_account_info.try_borrow()?;
let multisig = bytemuck::try_from_bytes::<PodMultisig>(multisig_data)
.map_err(|_| ProgramError::InvalidArgument)?;
if !bool::from(multisig.is_initialized) {
return Err(ProgramError::UninitializedAccount);
}
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.address() && !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(())
}
}
#[cfg(test)]
mod tests {
use {
super::*,
mollusk_svm::{result::Check, Mollusk},
solana_account::{
state_traits::StateMutWincode, Account as SolanaAccount, ReadableAccount,
},
solana_account_info::AccountInfo,
solana_account_info::IntoAccountInfo,
solana_instruction::{AccountMeta, Instruction},
solana_program_option::COption,
solana_rent::Rent,
solana_sdk_ids::system_program,
solana_sdk_ids::sysvar::{self, rent},
spl_token_2022_interface::{
extension::{
permissioned_burn, transfer_fee::instruction::initialize_transfer_fee_config,
ExtensionType,
},
instruction::*,
state::{Account, Multisig},
},
std::collections::HashMap,
};
fn do_process_instruction(
instruction: Instruction,
mut accounts: Vec<&mut SolanaAccount>,
checks: &[Check],
) {
let instruction_accounts: Vec<(Address, SolanaAccount)> = instruction
.accounts
.iter()
.zip(&accounts)
.map(|(account_meta, account)| (account_meta.pubkey, (*account).clone()))
.collect();
let mut mollusk = Mollusk::new(&crate::id(), "spl_token_2022");
mollusk.sysvars.rent = test_rent();
mollusk.config.panic = true;
let result =
mollusk.process_and_validate_instruction(&instruction, &instruction_accounts, checks);
for (original, (_, updated)) in accounts.iter_mut().zip(result.resulting_accounts) {
original.data = updated.data().to_vec();
original.lamports = updated.lamports();
original.owner = *updated.owner();
}
}
fn do_process_instruction_dups(
instruction: Instruction,
account_infos: Vec<AccountInfo>,
checks: &[Check],
) {
let mut cached_accounts = HashMap::new();
let mut dedup_accounts = Vec::new();
account_infos.iter().for_each(|account_info| {
if !cached_accounts.contains_key(account_info.key) {
let account = SolanaAccount {
lamports: account_info.lamports(),
data: account_info.try_borrow_data().unwrap().to_vec(),
owner: *account_info.owner,
executable: account_info.executable,
rent_epoch: u64::MAX,
};
dedup_accounts.push((*account_info.key, account));
cached_accounts.insert(account_info.key, account_info);
}
});
let mut mollusk = Mollusk::new(&crate::id(), "spl_token_2022");
mollusk.sysvars.rent = test_rent();
mollusk.config.panic = true;
let result =
mollusk.process_and_validate_instruction(&instruction, &dedup_accounts, checks);
result
.resulting_accounts
.into_iter()
.for_each(|(pubkey, account)| {
let account_info = cached_accounts.get_mut(&pubkey).unwrap();
if account.data().is_empty() {
*account_info.try_borrow_mut_data().unwrap() = &mut [];
} else {
account_info
.try_borrow_mut_data()
.unwrap()
.copy_from_slice(account.data());
}
**account_info.try_borrow_mut_lamports().unwrap() = account.lamports();
account_info.assign(account.owner());
});
}
fn return_token_error_as_program_error() -> ProgramError {
TokenError::MintMismatch.into()
}
fn test_rent() -> solana_rent::Rent {
solana_rent::Rent::default()
}
fn rent_sysvar() -> SolanaAccount {
SolanaAccount::new_data_with_space(1, &test_rent(), solana_rent::SIZE, &sysvar::id())
.unwrap()
}
fn mint_minimum_balance() -> u64 {
solana_rent::Rent::default().minimum_balance(Mint::get_packed_len())
}
fn account_minimum_balance() -> u64 {
solana_rent::Rent::default().minimum_balance(Account::get_packed_len())
}
fn multisig_minimum_balance() -> u64 {
solana_rent::Rent::default().minimum_balance(Multisig::get_packed_len())
}
fn batch_instruction(instructions: &[&Instruction]) -> Result<Instruction, ProgramError> {
let mut accounts: Vec<AccountMeta> = vec![];
let mut data: Vec<u8> = vec![0xff];
for instruction in instructions {
if instruction.program_id.ne(&crate::ID) {
return Err(ProgramError::IncorrectProgramId);
}
data.push(instruction.accounts.len() as u8);
data.push(instruction.data.len() as u8);
data.extend_from_slice(&instruction.data);
accounts.extend_from_slice(&instruction.accounts);
}
Ok(Instruction {
program_id: crate::ID,
data,
accounts,
})
}
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(),
&Address::default(),
None,
crate::native_mint::DECIMALS,
)
.unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
&[Check::success()],
);
mint_account
}
fn native_mint_to(account: &AccountInfo, amount: u64) -> ProgramResult {
let mut buffer = account.try_borrow_mut_data()?;
let mut account_account = Account::unpack(&buffer)?;
if !account_account.is_native() {
return Err(TokenError::NonNativeNotSupported.into());
}
let mut lamports = account.try_borrow_mut_lamports()?;
**lamports += amount;
account_account.amount += amount;
Account::pack(account_account, &mut buffer)?;
Ok(())
}
#[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 = Address::new_unique();
let mint_key = Address::new_unique();
let mut mint_account = SolanaAccount::new(42, Mint::get_packed_len(), &program_id);
let mint2_key = Address::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],
&[Check::err(TokenError::NotRentExempt.into())],
);
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],
&[Check::success()],
);
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
&[Check::err(TokenError::AlreadyInUse.into())],
);
do_process_instruction(
initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(),
vec![&mut mint2_account, &mut rent_sysvar],
&[Check::success()],
);
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 = Address::new_unique();
let mint_key = Address::new_unique();
let mut mint_account = SolanaAccount::new(42, Mint::get_packed_len(), &program_id);
let mint2_key = Address::new_unique();
let mut mint2_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
do_process_instruction(
initialize_mint2(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account],
&[Check::err(TokenError::NotRentExempt.into())],
);
mint_account.lamports = mint_minimum_balance();
do_process_instruction(
initialize_mint2(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account],
&[Check::success()],
);
do_process_instruction(
initialize_mint2(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account],
&[Check::err(TokenError::AlreadyInUse.into())],
);
do_process_instruction(
initialize_mint2(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(),
vec![&mut mint2_account],
&[Check::success()],
);
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 = Address::new_unique();
let mut account_account = SolanaAccount::new(42, Account::get_packed_len(), &program_id);
let owner_key = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let mint_key = Address::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_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut owner_account,
&mut rent_sysvar,
],
&[Check::err(TokenError::NotRentExempt.into())],
);
account_account.lamports = account_minimum_balance();
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,
],
&[Check::err(TokenError::InvalidMint.into())],
);
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
&[Check::success()],
);
let not_program_id = Address::new_unique();
mint_account.owner = not_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,
],
&[Check::err(ProgramError::IncorrectProgramId)],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::err(TokenError::AlreadyInUse.into())],
);
}
#[test]
fn test_transfer_dups() {
let program_id = crate::id();
let account1_key = Address::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 = Address::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 = Address::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 = Address::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 = Address::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 = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into();
let mint_key = Address::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()],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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()],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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()],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
do_process_instruction_dups(
initialize_multisig(&program_id, &multisig_key, &[&account4_key], 1).unwrap(),
vec![
multisig_info.clone(),
rent_info.clone(),
account4_info.clone(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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()],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
}
#[test]
fn test_transfer() {
let program_id = crate::id();
let account_key = Address::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Address::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account3_key = Address::new_unique();
let mut account3_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let delegate_key = Address::new_unique();
let mut delegate_account = SolanaAccount::default();
let mismatch_key = Address::new_unique();
let mut mismatch_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Address::new_unique();
let mut owner2_account = SolanaAccount::default();
let mint_key = Address::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint2_key = Address::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],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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],
&[Check::success()],
);
#[allow(deprecated)]
let mut instruction = transfer(
&program_id,
&account_key,
&account2_key,
&owner_key,
&[],
1000,
)
.unwrap();
instruction.accounts[2].is_signer = false;
do_process_instruction(
instruction,
vec![
&mut account_account,
&mut account2_account,
&mut owner_account,
],
&[Check::err(ProgramError::MissingRequiredSignature)],
);
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,
],
&[Check::err(TokenError::MintMismatch.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,
],
&[Check::err(TokenError::OwnerMismatch.into())],
);
let not_program_id = Address::new_unique();
account_account.owner = not_program_id;
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,
],
&[Check::err(ProgramError::IncorrectProgramId)],
);
account_account.owner = program_id;
let not_program_id = Address::new_unique();
account2_account.owner = not_program_id;
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,
],
&[Check::err(ProgramError::IncorrectProgramId)],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::err(TokenError::InsufficientFunds.into())],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::err(TokenError::MintDecimalsMismatch.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,
],
&[Check::err(TokenError::MintMismatch.into())],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::err(TokenError::InsufficientFunds.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,
],
&[Check::success()],
);
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,
],
&[Check::err(TokenError::OwnerMismatch.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,
],
&[Check::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,
],
&[Check::success()],
);
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,
],
&[Check::err(TokenError::OwnerMismatch.into())],
);
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,
],
&[Check::success()],
);
do_process_instruction(
approve(
&program_id,
&account_key,
&delegate_key,
&owner_key,
&[],
100,
)
.unwrap(),
vec![
&mut account_account,
&mut delegate_account,
&mut owner_account,
],
&[Check::success()],
);
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,
],
&[Check::err(TokenError::InsufficientFunds.into())],
);
}
#[test]
fn test_self_transfer() {
let program_id = crate::id();
let account_key = Address::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Address::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account3_key = Address::new_unique();
let mut account3_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let delegate_key = Address::new_unique();
let mut delegate_account = SolanaAccount::default();
let owner_key = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Address::new_unique();
let mut owner2_account = SolanaAccount::default();
let mint_key = Address::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],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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],
&[Check::success()],
);
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();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&[Check::success()],
);
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();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
mint_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&[Check::success()],
);
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;
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
account_info.clone(),
owner_no_sign_info.clone(),
],
&[Check::err(ProgramError::MissingRequiredSignature)],
);
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;
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
mint_info.clone(),
account_info.clone(),
owner_no_sign_info,
],
&[Check::err(ProgramError::MissingRequiredSignature)],
);
#[allow(deprecated)]
let instruction = transfer(
&program_id,
account_info.key,
account_info.key,
owner2_info.key,
&[],
1000,
)
.unwrap();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
account_info.clone(),
owner2_info.clone(),
],
&[Check::err(TokenError::OwnerMismatch.into())],
);
let instruction = transfer_checked(
&program_id,
account_info.key,
mint_info.key,
account_info.key,
owner2_info.key,
&[],
1000,
2,
)
.unwrap();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
mint_info.clone(),
account_info.clone(),
owner2_info.clone(),
],
&[Check::err(TokenError::OwnerMismatch.into())],
);
#[allow(deprecated)]
let instruction = transfer(
&program_id,
account_info.key,
account_info.key,
owner_info.key,
&[],
1001,
)
.unwrap();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&[Check::err(TokenError::InsufficientFunds.into())],
);
let instruction = transfer_checked(
&program_id,
account_info.key,
mint_info.key,
account_info.key,
owner_info.key,
&[],
1001,
2,
)
.unwrap();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
mint_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&[Check::err(TokenError::InsufficientFunds.into())],
);
let instruction = transfer_checked(
&program_id,
account_info.key,
mint_info.key,
account_info.key,
owner_info.key,
&[],
1,
10, )
.unwrap();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
mint_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&[Check::err(TokenError::MintDecimalsMismatch.into())],
);
let instruction = transfer_checked(
&program_id,
account_info.key,
account3_info.key, account_info.key,
owner_info.key,
&[],
1,
2,
)
.unwrap();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
account3_info.clone(), account_info.clone(),
owner_info.clone(),
],
&[Check::err(TokenError::MintMismatch.into())],
);
let instruction = approve(
&program_id,
account_info.key,
delegate_info.key,
owner_info.key,
&[],
100,
)
.unwrap();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
delegate_info.clone(),
owner_info.clone(),
],
&[Check::success()],
);
#[allow(deprecated)]
let instruction = transfer(
&program_id,
account_info.key,
account_info.key,
delegate_info.key,
&[],
100,
)
.unwrap();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
account_info.clone(),
delegate_info.clone(),
],
&[Check::success()],
);
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();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
mint_info.clone(),
account_info.clone(),
delegate_info.clone(),
],
&[Check::success()],
);
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();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
account_info.clone(),
delegate_info.clone(),
],
&[Check::err(TokenError::InsufficientFunds.into())],
);
let instruction = transfer_checked(
&program_id,
account_info.key,
mint_info.key,
account_info.key,
delegate_info.key,
&[],
101,
2,
)
.unwrap();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
mint_info.clone(),
account_info.clone(),
delegate_info.clone(),
],
&[Check::err(TokenError::InsufficientFunds.into())],
);
#[allow(deprecated)]
let instruction = transfer(
&program_id,
account_info.key,
account_info.key,
owner_info.key,
&[],
1000,
)
.unwrap();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&[Check::success()],
);
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();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
mint_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&[Check::success()],
);
let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap();
assert_eq!(account.amount, 1000);
}
#[test]
fn test_unwrap_lamports_dups() {
let program_id = crate::id();
let account1_key = Address::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 = Address::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 = Address::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 = Address::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 = Address::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 = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into();
let mint_key = native_mint::id();
let mut mint_account = native_mint();
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_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(),
vec![
account1_info.clone(),
mint_info.clone(),
account1_info.clone(),
rent_info.clone(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
native_mint_to(&account1_info, 1000).unwrap();
do_process_instruction_dups(
unwrap_lamports(
&program_id,
&account1_key,
&account2_key,
&account1_key,
&[],
Some(500),
)
.unwrap(),
vec![
account1_info.clone(),
account2_info.clone(),
account1_info.clone(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
native_mint_to(&account3_info, 1000).unwrap();
account1_info.is_signer = false;
account2_info.is_signer = true;
do_process_instruction_dups(
unwrap_lamports(
&program_id,
&account3_key,
&account2_key,
&account2_key,
&[],
Some(500),
)
.unwrap(),
vec![
account3_info.clone(),
account2_info.clone(),
account2_info.clone(),
],
&[Check::success()],
);
do_process_instruction_dups(
initialize_multisig(&program_id, &multisig_key, &[&account4_key], 1).unwrap(),
vec![
multisig_info.clone(),
rent_info.clone(),
account4_info.clone(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
native_mint_to(&account4_info, 1000).unwrap();
do_process_instruction_dups(
unwrap_lamports(
&program_id,
&account4_key,
&account2_key,
&multisig_key,
&[&account4_key],
Some(500),
)
.unwrap(),
vec![
account4_info.clone(),
account2_info.clone(),
multisig_info.clone(),
account4_info.clone(),
],
&[Check::success()],
);
}
#[test]
fn test_unwrap_lamports() {
let program_id = crate::id();
let zero_space_rent_exempt_balance = Rent::default().minimum_balance(0);
let account_key = Address::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Address::new_unique();
let mut account2_account = SolanaAccount::new(
zero_space_rent_exempt_balance,
Account::get_packed_len(),
&program_id,
);
let mismatch_account_key = Address::new_unique();
let mut mismatch_account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Address::new_unique();
let mut owner2_account = SolanaAccount::default();
let mint_key = native_mint::id();
let mut mint_account = native_mint();
let mismatch_mint_key = Address::new_unique();
let mut mismatch_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, &mismatch_mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mismatch_mint_account, &mut rent_sysvar],
&[Check::success()],
);
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,
],
&[Check::success()],
);
do_process_instruction(
initialize_account(
&program_id,
&mismatch_account_key,
&mismatch_mint_key,
&owner_key,
)
.unwrap(),
vec![
&mut mismatch_account_account,
&mut mismatch_mint_account,
&mut owner_account,
&mut rent_sysvar,
],
&[Check::success()],
);
let account_info = (&account_key, false, &mut account_account).into();
native_mint_to(&account_info, 1000).unwrap();
let mut instruction = unwrap_lamports(
&program_id,
&account_key,
&account2_key,
&owner_key,
&[],
Some(1000),
)
.unwrap();
instruction.accounts[2].is_signer = false;
do_process_instruction(
instruction,
vec![
&mut account_account,
&mut account2_account,
&mut owner_account,
],
&[Check::err(ProgramError::MissingRequiredSignature)],
);
do_process_instruction(
unwrap_lamports(
&program_id,
&mismatch_account_key,
&account_key,
&owner_key,
&[],
None,
)
.unwrap(),
vec![
&mut mismatch_account_account,
&mut account_account,
&mut owner_account,
],
&[Check::err(TokenError::NonNativeNotSupported.into())],
);
do_process_instruction(
unwrap_lamports(
&program_id,
&account_key,
&account2_key,
&owner2_key,
&[],
Some(1000),
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut owner2_account,
],
&[Check::err(TokenError::OwnerMismatch.into())],
);
let not_program_id = Address::new_unique();
account_account.owner = not_program_id;
do_process_instruction(
unwrap_lamports(
&program_id,
&account_key,
&account2_key,
&owner_key,
&[],
Some(0),
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut owner2_account,
],
&[Check::err(ProgramError::IncorrectProgramId)],
);
account_account.owner = program_id;
do_process_instruction(
unwrap_lamports(
&program_id,
&account_key,
&account2_key,
&owner_key,
&[],
Some(500),
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut owner_account,
],
&[Check::success()],
);
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, 500);
assert_eq!(account_account.lamports(), 500 + account_minimum_balance());
assert_eq!(
account2_account.lamports(),
zero_space_rent_exempt_balance + 500
);
do_process_instruction(
unwrap_lamports(
&program_id,
&account_key,
&account2_key,
&owner_key,
&[],
Some(501),
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut owner_account,
],
&[Check::err(TokenError::InsufficientFunds.into())],
);
do_process_instruction(
unwrap_lamports(
&program_id,
&account_key,
&account2_key,
&owner_key,
&[],
None,
)
.unwrap(),
vec![
&mut account_account,
&mut account2_account,
&mut owner_account,
],
&[Check::success()],
);
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, 0);
assert_eq!(account_account.lamports(), account_minimum_balance());
assert_eq!(
account2_account.lamports(),
zero_space_rent_exempt_balance + 500 + 500
);
}
#[test]
fn test_self_unwrap_lamports() {
let program_id = crate::id();
let account_key = Address::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let mismatch_account_key = Address::new_unique();
let mut mismatch_account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Address::new_unique();
let mut owner2_account = SolanaAccount::default();
let mint_key = native_mint::id();
let mut mint_account = native_mint();
let mismatch_mint_key = Address::new_unique();
let mut mismatch_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, &mismatch_mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mismatch_mint_account, &mut rent_sysvar],
&[Check::success()],
);
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,
],
&[Check::success()],
);
do_process_instruction(
initialize_account(
&program_id,
&mismatch_account_key,
&mismatch_mint_key,
&owner_key,
)
.unwrap(),
vec![
&mut mismatch_account_account,
&mut mismatch_mint_account,
&mut owner_account,
&mut rent_sysvar,
],
&[Check::success()],
);
let account_info = (&account_key, false, &mut account_account).into_account_info();
let mismatch_account_info =
(&mismatch_account_key, false, &mut mismatch_account_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();
native_mint_to(&account_info, 1000).unwrap();
let instruction = unwrap_lamports(
&program_id,
account_info.key,
account_info.key,
owner_info.key,
&[],
Some(500),
)
.unwrap();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&[Check::success()],
);
let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap();
assert_eq!(account.amount, 500);
assert_eq!(account_info.lamports(), 1000 + account_minimum_balance());
let instruction = unwrap_lamports(
&program_id,
account_info.key,
account_info.key,
owner_info.key,
&[],
Some(501),
)
.unwrap();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&[Check::err(TokenError::InsufficientFunds.into())],
);
let mut owner_no_sign_info = owner_info.clone();
let mut instruction = unwrap_lamports(
&program_id,
account_info.key,
account_info.key,
owner_no_sign_info.key,
&[],
Some(500),
)
.unwrap();
instruction.accounts[2].is_signer = false;
owner_no_sign_info.is_signer = false;
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
account_info.clone(),
owner_no_sign_info.clone(),
],
&[Check::err(ProgramError::MissingRequiredSignature)],
);
let instruction = unwrap_lamports(
&program_id,
mismatch_account_info.key,
mismatch_account_info.key,
owner_info.key,
&[],
None,
)
.unwrap();
do_process_instruction_dups(
instruction,
vec![
mismatch_account_info.clone(),
mismatch_account_info.clone(),
owner_info.clone(),
],
&[Check::err(TokenError::NonNativeNotSupported.into())],
);
let instruction = unwrap_lamports(
&program_id,
account_info.key,
account_info.key,
owner2_info.key,
&[],
Some(500),
)
.unwrap();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
account_info.clone(),
owner2_info.clone(),
],
&[Check::err(TokenError::OwnerMismatch.into())],
);
let instruction = unwrap_lamports(
&program_id,
account_info.key,
account_info.key,
owner_info.key,
&[],
None,
)
.unwrap();
do_process_instruction_dups(
instruction,
vec![
account_info.clone(),
account_info.clone(),
owner_info.clone(),
],
&[Check::success()],
);
let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap();
assert_eq!(account.amount, 0);
assert_eq!(account_info.lamports(), 1000 + account_minimum_balance());
}
#[test]
fn test_mintable_token_with_zero_supply() {
let program_id = crate::id();
let account_key = Address::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let mint_key = Address::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],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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],
&[Check::success()],
);
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 + 1,
)
.unwrap(),
vec![&mut mint_account, &mut account_account, &mut owner_account],
&[Check::err(TokenError::MintDecimalsMismatch.into())],
);
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],
&[Check::success()],
);
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 = Address::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 = Address::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 = Address::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 = Address::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 = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into();
let mint_key = Address::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()],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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()],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
do_process_instruction_dups(
revoke(&program_id, &account1_key, &account1_key, &[]).unwrap(),
vec![account1_info.clone(), account1_info.clone()],
&[Check::success()],
);
do_process_instruction_dups(
initialize_multisig(&program_id, &multisig_key, &[&account3_key], 1).unwrap(),
vec![
multisig_info.clone(),
rent_info.clone(),
account3_info.clone(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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()],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
do_process_instruction_dups(
revoke(&program_id, &account3_key, &multisig_key, &[&account3_key]).unwrap(),
vec![
account3_info.clone(),
multisig_info.clone(),
account3_info.clone(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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()],
&[Check::success()],
);
}
#[test]
fn test_approve() {
let program_id = crate::id();
let account_key = Address::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Address::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let delegate_key = Address::new_unique();
let mut delegate_account = SolanaAccount::default();
let owner_key = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Address::new_unique();
let mut owner2_account = SolanaAccount::default();
let mint_key = Address::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],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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],
&[Check::success()],
);
let mut instruction = approve(
&program_id,
&account_key,
&delegate_key,
&owner_key,
&[],
100,
)
.unwrap();
instruction.accounts[2].is_signer = false;
do_process_instruction(
instruction,
vec![
&mut account_account,
&mut delegate_account,
&mut owner_account,
],
&[Check::err(ProgramError::MissingRequiredSignature)],
);
do_process_instruction(
approve(
&program_id,
&account_key,
&delegate_key,
&owner2_key,
&[],
100,
)
.unwrap(),
vec![
&mut account_account,
&mut delegate_account,
&mut owner2_account,
],
&[Check::err(TokenError::OwnerMismatch.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,
],
&[Check::success()],
);
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,
],
&[Check::err(TokenError::MintDecimalsMismatch.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,
],
&[Check::err(TokenError::MintMismatch.into())],
);
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,
],
&[Check::success()],
);
do_process_instruction(
revoke(&program_id, &account_key, &owner_key, &[]).unwrap(),
vec![&mut account_account, &mut owner_account],
&[Check::success()],
);
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,
],
&[Check::success()],
);
do_process_instruction(
revoke(&program_id, &account_key, &delegate_key, &[]).unwrap(),
vec![&mut account_account, &mut delegate_account],
&[Check::success()],
);
do_process_instruction(
revoke(&program_id, &account_key, &delegate_key, &[]).unwrap(),
vec![&mut account_account, &mut delegate_account],
&[Check::err(TokenError::OwnerMismatch.into())],
);
}
#[test]
fn test_set_authority_dups() {
let program_id = crate::id();
let account1_key = Address::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 = Address::new_unique();
let mint_key = Address::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()],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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()],
&[Check::success()],
);
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()],
&[Check::success()],
);
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()],
&[Check::success()],
);
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()],
&[Check::success()],
);
}
#[test]
fn test_set_authority() {
let program_id = crate::id();
let account_key = Address::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Address::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Address::new_unique();
let mut owner2_account = SolanaAccount::default();
let owner3_key = Address::new_unique();
let mut owner3_account = SolanaAccount::default();
let mint_key = Address::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint2_key = Address::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],
&[Check::success()],
);
do_process_instruction(
initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(),
vec![&mut mint2_account, &mut rent_sysvar],
&[Check::success()],
);
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&owner2_key),
AuthorityType::AccountOwner,
&owner_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner_account],
&[Check::err(ProgramError::InvalidAccountData)],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&owner_key),
AuthorityType::AccountOwner,
&owner2_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner2_account],
&[Check::err(TokenError::OwnerMismatch.into())],
);
let mut instruction = set_authority(
&program_id,
&account_key,
Some(&owner2_key),
AuthorityType::AccountOwner,
&owner_key,
&[],
)
.unwrap();
instruction.accounts[1].is_signer = false;
do_process_instruction(
instruction,
vec![&mut account_account, &mut owner_account],
&[Check::err(ProgramError::MissingRequiredSignature)],
);
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&owner2_key),
AuthorityType::FreezeAccount,
&owner_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner_account],
&[Check::err(TokenError::AuthorityTypeNotSupported.into())],
);
do_process_instruction(
set_authority(
&program_id,
&account_key,
None,
AuthorityType::AccountOwner,
&owner_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner_account],
&[Check::err(TokenError::InvalidInstruction.into())],
);
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,
],
&[Check::success()],
);
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],
&[Check::success()],
);
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],
&[Check::success()],
);
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&owner2_key),
AuthorityType::CloseAccount,
&owner2_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner2_account],
&[Check::success()],
);
do_process_instruction(
set_authority(
&program_id,
&account_key,
None,
AuthorityType::CloseAccount,
&owner2_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner2_account],
&[Check::success()],
);
do_process_instruction(
set_authority(
&program_id,
&mint_key,
Some(&owner3_key),
AuthorityType::MintTokens,
&owner2_key,
&[],
)
.unwrap(),
vec![&mut mint_account, &mut owner2_account],
&[Check::err(TokenError::OwnerMismatch.into())],
);
let mut instruction = set_authority(
&program_id,
&mint_key,
Some(&owner2_key),
AuthorityType::MintTokens,
&owner_key,
&[],
)
.unwrap();
instruction.accounts[1].is_signer = false;
do_process_instruction(
instruction,
vec![&mut mint_account, &mut owner_account],
&[Check::err(ProgramError::MissingRequiredSignature)],
);
do_process_instruction(
set_authority(
&program_id,
&mint_key,
Some(&owner2_key),
AuthorityType::FreezeAccount,
&owner_key,
&[],
)
.unwrap(),
vec![&mut mint_account, &mut owner_account],
&[Check::err(TokenError::MintCannotFreeze.into())],
);
do_process_instruction(
set_authority(
&program_id,
&mint_key,
Some(&owner2_key),
AuthorityType::MintTokens,
&owner_key,
&[],
)
.unwrap(),
vec![&mut mint_account, &mut owner_account],
&[Check::success()],
);
do_process_instruction(
set_authority(
&program_id,
&mint_key,
None,
AuthorityType::MintTokens,
&owner2_key,
&[],
)
.unwrap(),
vec![&mut mint_account, &mut owner2_account],
&[Check::success()],
);
do_process_instruction(
set_authority(
&program_id,
&mint2_key,
Some(&owner2_key),
AuthorityType::MintTokens,
&owner_key,
&[],
)
.unwrap(),
vec![&mut mint_account, &mut owner_account],
&[Check::err(TokenError::FixedSupply.into())],
);
do_process_instruction(
set_authority(
&program_id,
&mint2_key,
Some(&owner2_key),
AuthorityType::FreezeAccount,
&owner_key,
&[],
)
.unwrap(),
vec![&mut mint2_account, &mut owner_account],
&[Check::success()],
);
do_process_instruction(
set_authority(
&program_id,
&mint2_key,
None,
AuthorityType::FreezeAccount,
&owner2_key,
&[],
)
.unwrap(),
vec![&mut mint2_account, &mut owner2_account],
&[Check::success()],
);
do_process_instruction(
set_authority(
&program_id,
&mint2_key,
Some(&owner2_key),
AuthorityType::FreezeAccount,
&owner_key,
&[],
)
.unwrap(),
vec![&mut mint2_account, &mut owner2_account],
&[Check::err(TokenError::MintCannotFreeze.into())],
);
}
#[test]
fn test_set_authority_with_immutable_owner_extension() {
let program_id = crate::id();
let account_key = Address::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 = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Address::new_unique();
let mint_key = Address::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],
&[Check::success()],
);
do_process_instruction(
initialize_immutable_owner(&program_id, &account_key).unwrap(),
vec![&mut account_account],
&[Check::success()],
);
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,
],
&[Check::success()],
);
do_process_instruction(
set_authority(
&program_id,
&account_key,
Some(&owner2_key),
AuthorityType::AccountOwner,
&owner_key,
&[],
)
.unwrap(),
vec![&mut account_account, &mut owner_account],
&[Check::err(TokenError::ImmutableOwner.into())],
);
}
#[test]
fn test_mint_to_dups() {
let program_id = crate::id();
let account1_key = Address::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 = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into();
let mint_key = Address::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()],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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()],
&[Check::success()],
);
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()],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
}
#[test]
fn test_mint_to() {
let program_id = crate::id();
let account_key = Address::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Address::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account3_key = Address::new_unique();
let mut account3_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let mismatch_key = Address::new_unique();
let mut mismatch_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Address::new_unique();
let mut owner2_account = SolanaAccount::default();
let mint_key = Address::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint2_key = Address::new_unique();
let uninitialized_key = Address::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],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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],
&[Check::success()],
);
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],
&[Check::success()],
);
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;
do_process_instruction(
instruction,
vec![&mut mint_account, &mut account2_account, &mut owner_account],
&[Check::err(ProgramError::MissingRequiredSignature)],
);
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],
&[Check::err(TokenError::MintMismatch.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,
],
&[Check::err(TokenError::OwnerMismatch.into())],
);
let not_program_id = Address::new_unique();
mint_account.owner = not_program_id;
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],
&[Check::err(ProgramError::IncorrectProgramId)],
);
mint_account.owner = program_id;
let not_program_id = Address::new_unique();
account_account.owner = not_program_id;
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],
&[Check::err(ProgramError::IncorrectProgramId)],
);
account_account.owner = program_id;
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,
],
&[Check::err(ProgramError::UninitializedAccount)],
);
do_process_instruction(
set_authority(
&program_id,
&mint_key,
None,
AuthorityType::MintTokens,
&owner_key,
&[],
)
.unwrap(),
vec![&mut mint_account, &mut owner_account],
&[Check::success()],
);
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],
&[Check::err(TokenError::FixedSupply.into())],
);
}
#[test]
fn test_batch_with_invalid_count() {
let program_id = crate::id();
let account_key = Address::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let mint_account_key = Address::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint_owner = Address::new_unique();
let account_owner = Address::new_unique();
let mut account_owner_account = SolanaAccount::default();
let rent_key = rent::id();
let mut rent_sysvar = rent_sysvar();
let account_info: AccountInfo = (&account_key, true, &mut account_account).into();
let mint_account_info: AccountInfo = (&mint_account_key, true, &mut mint_account).into();
let account_owner_info: AccountInfo =
(&account_owner, true, &mut account_owner_account).into();
let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into();
let initialize_mint_instruction =
initialize_mint(&program_id, &mint_account_key, &mint_owner, None, 9).unwrap();
let initialize_account_instruction =
initialize_account3(&program_id, &account_key, &mint_account_key, &account_owner)
.unwrap();
let close_account_instruction = close_account(
&program_id,
&account_key,
&account_owner,
&account_owner,
&[],
)
.unwrap();
let mut batch_instr = batch_instruction(&[
&initialize_mint_instruction,
&initialize_account_instruction,
&close_account_instruction,
])
.unwrap();
batch_instr.data.pop();
let temp_mint_data = mint_account_info.data.borrow().to_vec();
let temp_account_data = account_info.data.borrow().to_vec();
do_process_instruction_dups(
batch_instr,
vec![
mint_account_info.clone(),
rent_info.clone(),
account_info.clone(),
mint_account_info.clone(),
account_info.clone(),
account_owner_info.clone(),
account_owner_info.clone(),
],
&[Check::err(TokenError::InvalidInstruction.into())],
);
mint_account_info
.data
.borrow_mut()
.copy_from_slice(&temp_mint_data);
account_info
.data
.borrow_mut()
.copy_from_slice(&temp_account_data);
let mut batch_instr = batch_instruction(&[
&initialize_mint_instruction,
&initialize_account_instruction,
&close_account_instruction,
])
.unwrap();
batch_instr.accounts.truncate(3);
do_process_instruction_dups(
batch_instr,
vec![
mint_account_info.clone(),
rent_info.clone(),
account_info.clone(),
], &[Check::err(ProgramError::NotEnoughAccountKeys)],
);
mint_account_info
.data
.borrow_mut()
.copy_from_slice(&temp_mint_data);
account_info
.data
.borrow_mut()
.copy_from_slice(&temp_account_data);
let batch_instruction = batch_instruction(&[
&initialize_mint_instruction,
&initialize_account_instruction,
&close_account_instruction,
])
.unwrap();
do_process_instruction_dups(
batch_instruction,
vec![
mint_account_info.clone(),
rent_info.clone(),
account_info.clone(),
mint_account_info.clone(),
account_info.clone(),
account_owner_info.clone(),
account_owner_info.clone(),
],
&[Check::success()],
);
}
#[test]
fn test_batch_in_batch() {
let program_id = crate::id();
let mint_account_key = Address::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint_owner = Address::new_unique();
let mut mint_owner_account = SolanaAccount::default();
let rent_key = rent::id();
let mut rent_sysvar = rent_sysvar();
let mint_account_info: AccountInfo = (&mint_account_key, true, &mut mint_account).into();
let mint_owner_info: AccountInfo = (&mint_owner, true, &mut mint_owner_account).into();
let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into();
let initialize_mint_instruction = initialize_mint(
&program_id,
&mint_account_key,
&mint_owner,
Some(&mint_owner),
9,
)
.unwrap();
let set_authority_instruction = set_authority(
&program_id,
&mint_account_key,
None,
AuthorityType::FreezeAccount,
&mint_owner,
&[],
)
.unwrap();
let batch_instr = batch_instruction(&[&set_authority_instruction]).unwrap();
let batch_in_batch_instruction =
batch_instruction(&[&batch_instr, &initialize_mint_instruction]).unwrap();
let temp_mint_account_data = mint_account_info.data.borrow().to_vec();
do_process_instruction_dups(
batch_in_batch_instruction,
vec![
mint_account_info.clone(),
rent_info.clone(),
mint_account_info.clone(),
mint_owner_info.clone(),
],
&[Check::err(TokenError::InvalidInstruction.into())],
);
mint_account_info
.data
.borrow_mut()
.copy_from_slice(&temp_mint_account_data);
do_process_instruction_dups(
initialize_mint_instruction,
vec![mint_account_info.clone(), rent_info.clone()],
&[Check::success()],
);
do_process_instruction_dups(
batch_instr,
vec![mint_account_info.clone(), mint_owner_info.clone()],
&[Check::success()],
);
}
#[test]
#[allow(deprecated)]
fn test_batch_initialize_mint_swap() {
let program_id = crate::id();
let account1_key = Address::new_unique();
let mut account1_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Address::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account3_key = Address::new_unique();
let mut account3_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account4_key = Address::new_unique();
let mut account4_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let mint_account1_key = Address::new_unique();
let mut mint1_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint_account2_key = Address::new_unique();
let mut mint2_account = SolanaAccount::new(
mint_minimum_balance(),
Mint::get_packed_len(),
&Address::new_unique(),
);
let mint_owner = Address::new_unique();
let mut mint_owner_account = SolanaAccount::default();
let account_owner = Address::new_unique();
let mut account_owner_account = SolanaAccount::default();
let rent_key = rent::id();
let mut rent_sysvar = rent_sysvar();
let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into();
let account2_info: AccountInfo = (&account2_key, true, &mut account2_account).into();
let account3_info: AccountInfo = (&account3_key, true, &mut account3_account).into();
let account4_info: AccountInfo = (&account4_key, true, &mut account4_account).into();
let mint_account1_info: AccountInfo = (&mint_account1_key, true, &mut mint1_account).into();
let mint_account2_info: AccountInfo = (&mint_account2_key, true, &mut mint2_account).into();
let mint_owner_info: AccountInfo = (&mint_owner, true, &mut mint_owner_account).into();
let account_owner_info: AccountInfo =
(&account_owner, true, &mut account_owner_account).into();
let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into();
let initialize_mint_1_instruction =
initialize_mint(&program_id, &mint_account1_key, &mint_owner, None, 9).unwrap();
let initialize_mint_2_instruction =
initialize_mint(&program_id, &mint_account2_key, &mint_owner, None, 9).unwrap();
let initialize_account_1_instruction = initialize_account3(
&program_id,
&account1_key,
&mint_account1_key,
&account_owner,
)
.unwrap();
let initialize_account_2_instruction = initialize_account3(
&program_id,
&account2_key,
&mint_account2_key,
&account_owner,
)
.unwrap();
let initialize_account_3_instruction = initialize_account3(
&program_id,
&account3_key,
&mint_account1_key,
&account_owner,
)
.unwrap();
let initialize_account_4_instruction = initialize_account3(
&program_id,
&account4_key,
&mint_account2_key,
&account_owner,
)
.unwrap();
let mint_to_1_instruction = mint_to(
&program_id,
&mint_account1_key,
&account1_key,
&mint_owner,
&[],
1000,
)
.unwrap();
let mint_to_2_instruction = mint_to_checked(
&program_id,
&mint_account2_key,
&account2_key,
&mint_owner,
&[],
1000,
9,
)
.unwrap();
let mint_to_2_with_invalid_decimals = mint_to_checked(
&program_id,
&mint_account2_key,
&account2_key,
&mint_owner,
&[],
1000,
8,
)
.unwrap();
let transfer_to_3_instruction = transfer(
&program_id,
&account1_key,
&account3_key,
&account_owner,
&[],
500,
)
.unwrap();
let transfer_to_3_with_invalid_amount = transfer(
&program_id,
&account1_key,
&account3_key,
&account_owner,
&[],
501,
)
.unwrap();
let transfer_4_instruction = transfer_checked(
&program_id,
&account2_key,
&mint_account2_key,
&account4_key,
&account_owner,
&[],
500,
9,
)
.unwrap();
let batch_1_instruction = batch_instruction(&[&initialize_mint_1_instruction]).unwrap();
do_process_instruction_dups(
batch_1_instruction,
vec![mint_account1_info.clone(), rent_info.clone()],
&[Check::success()],
);
let temp_mint1_data = mint_account1_info.data.borrow().to_vec();
let temp_account1_data = account1_info.data.borrow().to_vec();
let temp_account2_data = account2_info.data.borrow().to_vec();
let batch_2_fail_instruction = batch_instruction(&[
&initialize_mint_2_instruction,
&initialize_account_1_instruction,
&initialize_account_2_instruction,
&initialize_account_3_instruction,
&initialize_account_4_instruction,
])
.unwrap();
do_process_instruction_dups(
batch_2_fail_instruction,
vec![
mint_account2_info.clone(),
rent_info.clone(),
account1_info.clone(),
mint_account1_info.clone(),
account2_info.clone(),
mint_account2_info.clone(),
account3_info.clone(),
mint_account1_info.clone(),
account4_info.clone(),
mint_account2_info.clone(),
],
&[Check::err(ProgramError::IncorrectProgramId)],
);
mint_account1_info
.data
.borrow_mut()
.copy_from_slice(&temp_mint1_data);
account1_info
.data
.borrow_mut()
.copy_from_slice(&temp_account1_data);
account2_info
.data
.borrow_mut()
.copy_from_slice(&temp_account2_data);
let mut mint2_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint_account2_info: AccountInfo = (&mint_account2_key, true, &mut mint2_account).into();
let batch_2_success_instruction = batch_instruction(&[
&initialize_mint_2_instruction,
&initialize_account_1_instruction,
&initialize_account_2_instruction,
&initialize_account_3_instruction,
&initialize_account_4_instruction,
])
.unwrap();
do_process_instruction_dups(
batch_2_success_instruction,
vec![
mint_account2_info.clone(),
rent_info.clone(),
account1_info.clone(),
mint_account1_info.clone(),
account2_info.clone(),
mint_account2_info.clone(),
account3_info.clone(),
mint_account1_info.clone(),
account4_info.clone(),
mint_account2_info.clone(),
],
&[Check::success()],
);
let temp_mint1_data = mint_account1_info.data.borrow().to_vec();
let temp_account1_data = account1_info.data.borrow().to_vec();
let temp_mint2_data = mint_account2_info.data.borrow().to_vec();
let temp_account2_data = account2_info.data.borrow().to_vec();
let batch_3_fail_instruction = batch_instruction(&[
&mint_to_1_instruction,
&mint_to_2_instruction,
&mint_to_2_with_invalid_decimals,
])
.unwrap();
do_process_instruction_dups(
batch_3_fail_instruction,
vec![
mint_account1_info.clone(),
account1_info.clone(),
mint_owner_info.clone(),
mint_account2_info.clone(),
account2_info.clone(),
mint_owner_info.clone(),
mint_account2_info.clone(),
account2_info.clone(),
mint_owner_info.clone(),
],
&[Check::err(TokenError::MintDecimalsMismatch.into())],
);
mint_account1_info
.data
.borrow_mut()
.copy_from_slice(&temp_mint1_data);
account1_info
.data
.borrow_mut()
.copy_from_slice(&temp_account1_data);
mint_account2_info
.data
.borrow_mut()
.copy_from_slice(&temp_mint2_data);
account2_info
.data
.borrow_mut()
.copy_from_slice(&temp_account2_data);
let batch_3_success_instruction =
batch_instruction(&[&mint_to_1_instruction, &mint_to_2_instruction]).unwrap();
do_process_instruction_dups(
batch_3_success_instruction,
vec![
mint_account1_info.clone(),
account1_info.clone(),
mint_owner_info.clone(),
mint_account2_info.clone(),
account2_info.clone(),
mint_owner_info.clone(),
],
&[Check::success()],
);
let temp_account1_data = account1_info.data.borrow().to_vec();
let temp_account3_data = account3_info.data.borrow().to_vec();
let temp_account2_data = account2_info.data.borrow().to_vec();
let temp_account4_data = account4_info.data.borrow().to_vec();
let batch_4_fail_instruction = batch_instruction(&[
&transfer_to_3_instruction,
&transfer_to_3_with_invalid_amount,
&transfer_4_instruction,
])
.unwrap();
do_process_instruction_dups(
batch_4_fail_instruction,
vec![
account1_info.clone(),
account3_info.clone(),
account_owner_info.clone(),
account1_info.clone(),
account3_info.clone(),
account_owner_info.clone(),
account2_info.clone(),
mint_account2_info.clone(),
account4_info.clone(),
account_owner_info.clone(),
],
&[Check::err(TokenError::InsufficientFunds.into())],
);
account1_info
.data
.borrow_mut()
.copy_from_slice(&temp_account1_data);
account3_info
.data
.borrow_mut()
.copy_from_slice(&temp_account3_data);
account2_info
.data
.borrow_mut()
.copy_from_slice(&temp_account2_data);
account4_info
.data
.borrow_mut()
.copy_from_slice(&temp_account4_data);
let batch_4_success_instruction =
batch_instruction(&[&transfer_to_3_instruction, &transfer_4_instruction]).unwrap();
do_process_instruction_dups(
batch_4_success_instruction,
vec![
account1_info.clone(),
account3_info.clone(),
account_owner_info.clone(),
account2_info.clone(),
mint_account2_info.clone(),
account4_info.clone(),
account_owner_info.clone(),
],
&[Check::success()],
);
}
#[test]
fn test_burn_dups() {
let program_id = crate::id();
let account1_key = Address::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 = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into();
let mint_key = Address::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()],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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()],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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()],
&[Check::success()],
);
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()],
&[Check::success()],
);
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()],
&[Check::success()],
);
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()],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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()],
&[Check::success()],
);
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()],
&[Check::success()],
);
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()],
&[Check::success()],
);
}
#[test]
fn test_burn() {
let program_id = crate::id();
let account_key = Address::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Address::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account3_key = Address::new_unique();
let mut account3_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let delegate_key = Address::new_unique();
let mut delegate_account = SolanaAccount::default();
let mismatch_key = Address::new_unique();
let mut mismatch_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Address::new_unique();
let mut owner2_account = SolanaAccount::default();
let mint_key = Address::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let mint2_key = Address::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],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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],
&[Check::success()],
);
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],
&[Check::success()],
);
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;
do_process_instruction(
instruction,
vec![
&mut account_account,
&mut mint_account,
&mut delegate_account,
],
&[Check::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],
&[Check::err(TokenError::OwnerMismatch.into())],
);
let not_program_id = Address::new_unique();
account_account.owner = not_program_id;
do_process_instruction(
burn(&program_id, &account_key, &mint_key, &owner_key, &[], 0).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
&[Check::err(ProgramError::IncorrectProgramId)],
);
account_account.owner = program_id;
let not_program_id = Address::new_unique();
mint_account.owner = not_program_id;
do_process_instruction(
burn(&program_id, &account_key, &mint_key, &owner_key, &[], 0).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
&[Check::err(ProgramError::IncorrectProgramId)],
);
mint_account.owner = program_id;
do_process_instruction(
burn(&program_id, &mismatch_key, &mint_key, &owner_key, &[], 42).unwrap(),
vec![&mut mismatch_account, &mut mint_account, &mut owner_account],
&[Check::err(TokenError::MintMismatch.into())],
);
do_process_instruction(
burn(&program_id, &account_key, &mint_key, &owner_key, &[], 21).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
&[Check::success()],
);
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],
&[Check::err(TokenError::MintDecimalsMismatch.into())],
);
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],
&[Check::success()],
);
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);
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],
&[Check::err(TokenError::InsufficientFunds.into())],
);
do_process_instruction(
approve(
&program_id,
&account_key,
&delegate_key,
&owner_key,
&[],
84,
)
.unwrap(),
vec![
&mut account_account,
&mut delegate_account,
&mut owner_account,
],
&[Check::success()],
);
do_process_instruction(
burn(
&program_id,
&account_key,
&mint_key,
&owner2_key, &[],
1,
)
.unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner2_account],
&[Check::err(TokenError::OwnerMismatch.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,
],
&[Check::err(TokenError::InsufficientFunds.into())],
);
do_process_instruction(
burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 84).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut delegate_account,
],
&[Check::success()],
);
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);
do_process_instruction(
burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 1).unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut delegate_account,
],
&[Check::err(TokenError::OwnerMismatch.into())],
);
}
#[test]
fn test_multisig() {
let program_id = crate::id();
let mint_key = Address::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let account_key = Address::new_unique();
let mut account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Address::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let multisig_key = Address::new_unique();
let mut multisig_account = SolanaAccount::new(42, Multisig::get_packed_len(), &program_id);
let mut empty_multisig_account =
SolanaAccount::new(42, Multisig::get_packed_len(), &program_id);
let multisig_delegate_key = Address::new_unique();
let mut multisig_delegate_account = SolanaAccount::new(
multisig_minimum_balance(),
Multisig::get_packed_len(),
&program_id,
);
let signer_keys = vec![Address::new_unique(); MAX_SIGNERS];
let signer_key_refs: Vec<&Address> = 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();
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(),
],
&[Check::err(TokenError::NotRentExempt.into())],
);
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(),
],
&[Check::success()],
);
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()],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
do_process_instruction(
initialize_mint(&program_id, &mint_key, &multisig_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
let account_info_iter = &mut signer_accounts.iter_mut();
do_process_instruction(
approve(
&program_id,
&account_key,
&multisig_delegate_key,
&multisig_key,
&[],
100,
)
.unwrap(),
vec![
&mut account,
&mut multisig_delegate_account,
&mut empty_multisig_account,
account_info_iter.next().unwrap(),
],
&[Check::err(ProgramError::UninitializedAccount)],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
let account3_key = Address::new_unique();
let mut account3_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let mint2_key = Address::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],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
}
#[test]
fn test_permissioned_burn_none_authority_errors() {
let program_id = crate::id();
let mint_key = Address::new_unique();
let owner_key = Address::new_unique();
let burn_authority_key = Address::new_unique();
let account_key = Address::new_unique();
let mint_size =
ExtensionType::try_calculate_account_len::<PodMint>(&[ExtensionType::PermissionedBurn])
.unwrap();
let mut mint_account = SolanaAccount::new(
Rent::default().minimum_balance(mint_size),
mint_size,
&program_id,
);
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let mut owner_account = SolanaAccount::default();
let mut burn_authority_account = SolanaAccount::default();
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
permissioned_burn::instruction::initialize(&program_id, &mint_key, &burn_authority_key)
.unwrap(),
vec![&mut mint_account],
&[Check::success()],
);
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
&[Check::success()],
);
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,
],
&[Check::success()],
);
do_process_instruction(
mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 10).unwrap(),
vec![&mut mint_account, &mut account_account, &mut owner_account],
&[Check::success()],
);
do_process_instruction(
set_authority(
&program_id,
&mint_key,
None,
AuthorityType::PermissionedBurn,
&burn_authority_key,
&[],
)
.unwrap(),
vec![&mut mint_account, &mut burn_authority_account],
&[Check::success()],
);
do_process_instruction(
permissioned_burn::instruction::burn(
&program_id,
&account_key,
&mint_key,
&burn_authority_key,
&owner_key,
&[],
1,
)
.unwrap(),
vec![
&mut account_account,
&mut mint_account,
&mut burn_authority_account,
&mut owner_account,
],
&[Check::err(TokenError::InvalidInstruction.into())],
);
do_process_instruction(
burn(&program_id, &account_key, &mint_key, &owner_key, &[], 1).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
&[Check::success()],
);
}
#[test]
fn test_validate_owner() {
use pinocchio::{account::RuntimeAccount, entrypoint::NON_DUP_MARKER};
unsafe fn make_account_view(
address: &Address,
owner: &Address,
is_signer: bool,
data: &[u8],
) -> (Vec<u64>, AccountView) {
let mut storage =
vec![0u64; (size_of::<RuntimeAccount>() + data.len()).div_ceil(size_of::<u64>())];
let account = storage.as_mut_ptr() as *mut RuntimeAccount;
account.write(RuntimeAccount {
borrow_state: NON_DUP_MARKER,
address: *address,
owner: *owner,
is_signer: u8::from(is_signer),
data_len: data.len() as u64,
..RuntimeAccount::default()
});
core::ptr::copy_nonoverlapping(
data.as_ptr(),
(account as *mut u8).add(size_of::<RuntimeAccount>()),
data.len(),
);
(storage, AccountView::new_unchecked(account))
}
let program_id = crate::id();
let owner_key = Address::new_unique();
let account_to_validate = Address::new_unique();
let mut signer_keys = [Address::default(); MAX_SIGNERS];
for signer_key in signer_keys.iter_mut().take(MAX_SIGNERS) {
*signer_key = Address::new_unique();
}
let signer_accounts: Vec<_> = signer_keys
.iter()
.chain(std::iter::once(&owner_key))
.map(|key| unsafe { make_account_view(key, &program_id, true, &[]) })
.collect();
let mut signers: Vec<_> = signer_accounts.iter().map(|(_, view)| *view).collect();
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_backing, mut owner_account_info) =
unsafe { make_account_view(&owner_key, &program_id, false, &data) };
let owner_account_data_len = owner_account_info.data_len();
{
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_backing, account_info) =
unsafe { make_account_view(&account_to_validate, &program_id, true, &data) };
let account_info_data_len = account_info.data_len();
let mut account_alias = account_info;
let mut borrowed_data = account_alias.try_borrow_mut().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_data_len,
&signers,
)
.unwrap();
{
let tokenkeg_id = inline_spl_token::id();
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 (_tokenkeg_backing, tokenkeg_multisig_info) =
unsafe { make_account_view(&owner_key, &tokenkeg_id, false, &data) };
let tokenkeg_multisig_data_len = tokenkeg_multisig_info.data_len();
Processor::validate_owner(
&program_id,
&owner_key,
&tokenkeg_multisig_info,
tokenkeg_multisig_data_len,
&signers,
)
.unwrap();
}
{
let mut multisig =
Multisig::unpack_unchecked(&owner_account_info.try_borrow().unwrap()).unwrap();
multisig.m = 1;
Multisig::pack(multisig, &mut owner_account_info.try_borrow_mut().unwrap()).unwrap();
}
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_data_len,
&signers,
)
.unwrap();
{
let mut multisig =
Multisig::unpack_unchecked(&owner_account_info.try_borrow().unwrap()).unwrap();
multisig.m = 2;
multisig.n = 1;
Multisig::pack(multisig, &mut owner_account_info.try_borrow_mut().unwrap()).unwrap();
}
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_data_len,
&signers
)
);
{
let mut multisig =
Multisig::unpack_unchecked(&owner_account_info.try_borrow().unwrap()).unwrap();
multisig.m = 0;
multisig.n = 11;
Multisig::pack(multisig, &mut owner_account_info.try_borrow_mut().unwrap()).unwrap();
}
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_data_len,
&signers,
)
.unwrap();
{
let mut multisig =
Multisig::unpack_unchecked(&owner_account_info.try_borrow().unwrap()).unwrap();
multisig.m = 2;
multisig.n = 11;
Multisig::pack(multisig, &mut owner_account_info.try_borrow_mut().unwrap()).unwrap();
}
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_data_len,
&[]
)
);
{
let mut multisig =
Multisig::unpack_unchecked(&owner_account_info.try_borrow().unwrap()).unwrap();
multisig.m = 2;
multisig.n = 11;
Multisig::pack(multisig, &mut owner_account_info.try_borrow_mut().unwrap()).unwrap();
}
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_data_len,
&signers[0..1]
)
);
{
let mut multisig =
Multisig::unpack_unchecked(&owner_account_info.try_borrow().unwrap()).unwrap();
multisig.m = 2;
multisig.n = 11;
Multisig::pack(multisig, &mut owner_account_info.try_borrow_mut().unwrap()).unwrap();
}
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_data_len,
&signers[5..7],
)
.unwrap();
{
let mut multisig =
Multisig::unpack_unchecked(&owner_account_info.try_borrow().unwrap()).unwrap();
multisig.m = 11;
multisig.n = 11;
Multisig::pack(multisig, &mut owner_account_info.try_borrow_mut().unwrap()).unwrap();
}
let (_non_signer_backing, non_signer) =
unsafe { make_account_view(&signer_keys[5], &program_id, false, &[]) };
signers[5] = non_signer;
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_data_len,
&signers
)
);
signers[5] = signer_accounts[5].1;
{
let signers = vec![signers[5]; MAX_SIGNERS + 1];
let mut multisig =
Multisig::unpack_unchecked(&owner_account_info.try_borrow().unwrap()).unwrap();
multisig.m = 11;
multisig.n = 11;
Multisig::pack(multisig, &mut owner_account_info.try_borrow_mut().unwrap()).unwrap();
assert_eq!(
Err(ProgramError::MissingRequiredSignature),
Processor::validate_owner(
&program_id,
&owner_key,
&owner_account_info,
owner_account_data_len,
&signers
)
);
}
}
#[test]
fn test_owner_close_account_dups() {
let program_id = crate::id();
let owner_key = Address::new_unique();
let mint_key = Address::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()],
&[Check::success()],
);
let to_close_key = Address::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 = Address::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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
assert!((*to_close_account_info.data.borrow()).is_empty());
}
#[test]
fn test_close_authority_close_account_dups() {
let program_id = crate::id();
let owner_key = Address::new_unique();
let mint_key = Address::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()],
&[Check::success()],
);
let to_close_key = Address::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 = Address::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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
assert!(to_close_account_info.data.borrow().is_empty());
}
#[test]
fn test_close_account() {
let program_id = crate::id();
let mint_key = Address::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let account_key = Address::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Address::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance() + 42,
Account::get_packed_len(),
&program_id,
);
let account3_key = Address::new_unique();
let mut account3_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Address::new_unique();
let mut owner2_account = SolanaAccount::default();
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(),
vec![
&mut account_account,
&mut account3_account,
&mut owner2_account,
],
&[Check::err(ProgramError::UninitializedAccount)],
);
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
let account = Account::unpack_unchecked(&account2_account.data).unwrap();
assert!(account.is_native());
assert_eq!(account.amount, 42);
do_process_instruction(
close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(),
vec![
&mut account_account,
&mut account3_account,
&mut owner_account,
],
&[Check::err(TokenError::NonNativeHasBalance.into())],
);
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],
&[Check::success()],
);
do_process_instruction(
close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(),
vec![
&mut account_account,
&mut account3_account,
&mut owner2_account,
],
&[Check::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,
],
&[Check::success()],
);
assert_eq!(account_account.lamports, 0);
assert_eq!(account3_account.lamports, 2 * account_minimum_balance());
assert!(account_account.data.is_empty());
assert_eq!(account_account.owner, system_program::id());
let account_key = Address::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner2_key = Address::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,
],
&[Check::success()],
);
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],
&[Check::success()],
);
do_process_instruction(
close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(),
vec![
&mut account_account,
&mut account3_account,
&mut owner_account,
],
&[Check::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,
],
&[Check::success()],
);
assert_eq!(account_account.lamports, 0);
assert_eq!(account3_account.lamports, 2 * account_minimum_balance() + 2);
assert!(account_account.data.is_empty());
assert_eq!(account_account.owner, system_program::id());
do_process_instruction(
close_account(&program_id, &account2_key, &account3_key, &owner_key, &[]).unwrap(),
vec![
&mut account2_account,
&mut account3_account,
&mut owner_account,
],
&[Check::success()],
);
assert!(account2_account.data.is_empty());
assert_eq!(account2_account.owner, system_program::id());
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 = Address::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance() + 40,
Account::get_packed_len(),
&program_id,
);
let account2_key = Address::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account3_key = Address::new_unique();
let mut account3_account = SolanaAccount::new(account_minimum_balance(), 0, &program_id);
let owner_key = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Address::new_unique();
let mut owner2_account = SolanaAccount::default();
let owner3_key = Address::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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
let account = Account::unpack_unchecked(&account2_account.data).unwrap();
assert!(account.is_native());
assert_eq!(account.amount, 0);
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],
&[Check::err(TokenError::NativeNotSupported.into())],
);
let bogus_mint_key = Address::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],
&[Check::success()],
);
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,
],
&[Check::err(TokenError::NativeNotSupported.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,
],
&[Check::err(TokenError::InsufficientFunds.into())],
);
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,
],
&[Check::success()],
);
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],
&[Check::success()],
);
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],
&[Check::success()],
);
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,
],
&[Check::success()],
);
assert_eq!(account_account.lamports, 0);
assert_eq!(account3_account.lamports, 2 * account_minimum_balance());
assert!(account_account.data.is_empty());
assert_eq!(account_account.owner, system_program::id());
}
#[test]
fn test_overflow() {
let program_id = crate::id();
let account_key = Address::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Address::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Address::new_unique();
let mut owner2_account = SolanaAccount::default();
let mint_owner_key = Address::new_unique();
let mut mint_owner_account = SolanaAccount::default();
let mint_key = Address::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],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, u64::MAX);
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,
],
&[Check::err(TokenError::Overflow.into())],
);
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.amount, u64::MAX);
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,
],
&[Check::err(TokenError::Overflow.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],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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();
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,
],
&[Check::err(TokenError::Overflow.into())],
);
}
#[test]
fn test_frozen() {
let program_id = crate::id();
let account_key = Address::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account2_key = Address::new_unique();
let mut account2_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let owner_key = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let mint_key = Address::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],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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],
&[Check::success()],
);
let mut account = Account::unpack_unchecked(&account2_account.data).unwrap();
account.state = AccountState::Frozen;
Account::pack(account, &mut account2_account.data).unwrap();
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,
],
&[Check::err(TokenError::AccountFrozen.into())],
);
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();
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,
],
&[Check::err(TokenError::AccountFrozen.into())],
);
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 = Address::new_unique();
let mut delegate_account = SolanaAccount::default();
do_process_instruction(
approve(
&program_id,
&account_key,
&delegate_key,
&owner_key,
&[],
100,
)
.unwrap(),
vec![
&mut account_account,
&mut delegate_account,
&mut owner_account,
],
&[Check::err(TokenError::AccountFrozen.into())],
);
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();
do_process_instruction(
revoke(&program_id, &account_key, &owner_key, &[]).unwrap(),
vec![&mut account_account, &mut owner_account],
&[Check::err(TokenError::AccountFrozen.into())],
);
let new_owner_key = Address::new_unique();
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],
&[Check::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],
&[Check::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],
&[Check::err(TokenError::AccountFrozen.into())],
);
}
#[test]
fn test_freeze_thaw_dups() {
let program_id = crate::id();
let account1_key = Address::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 = Address::new_unique();
let mint_key = Address::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()],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
}
#[test]
fn test_freeze_account() {
let program_id = crate::id();
let account_key = Address::new_unique();
let mut account_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let account_owner_key = Address::new_unique();
let mut account_owner_account = SolanaAccount::default();
let owner_key = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let owner2_key = Address::new_unique();
let mut owner2_account = SolanaAccount::default();
let mint_key = Address::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],
&[Check::success()],
);
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,
],
&[Check::success()],
);
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],
&[Check::success()],
);
do_process_instruction(
freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
&[Check::err(TokenError::MintCannotFreeze.into())],
);
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();
do_process_instruction(
freeze_account(&program_id, &account_key, &mint_key, &owner2_key, &[]).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner2_account],
&[Check::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],
&[Check::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],
&[Check::success()],
);
let account = Account::unpack_unchecked(&account_account.data).unwrap();
assert_eq!(account.state, AccountState::Frozen);
do_process_instruction(
freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
&[Check::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],
&[Check::err(TokenError::OwnerMismatch.into())],
);
do_process_instruction(
thaw_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(),
vec![&mut account_account, &mut mint_account, &mut owner_account],
&[Check::success()],
);
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 = Address::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 = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let mint_key = Address::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],
&[Check::success()],
);
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,
],
&[Check::success()],
);
do_process_instruction(
initialize_account2(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
vec![&mut account2_account, &mut mint_account, &mut rent_sysvar],
&[Check::success()],
);
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],
&[Check::success()],
);
assert_eq!(account_account, account3_account);
}
#[test]
fn initialize_account_on_non_transferable_mint() {
let program_id = crate::id();
let account = Address::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 = Address::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 = Address::new_unique();
let mut owner_account = SolanaAccount::default();
let mint_key = Address::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();
do_process_instruction(
initialize_non_transferable_mint(&program_id, &mint_key).unwrap(),
vec![&mut mint_account],
&[Check::success()],
);
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
&[Check::success()],
);
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,
],
&[Check::err(ProgramError::InvalidAccountData)],
);
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,
],
&[Check::success()],
);
}
#[test]
fn test_sync_native() {
let program_id = crate::id();
let mint_key = Address::new_unique();
let mut mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let native_account_key = Address::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 = Address::new_unique();
let mut non_native_account = SolanaAccount::new(
account_minimum_balance() + 50,
Account::get_packed_len(),
&program_id,
);
let owner_key = Address::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],
&[Check::success()],
);
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,
],
&[Check::success()],
);
let account = Account::unpack_unchecked(&non_native_account.data).unwrap();
assert!(!account.is_native());
assert_eq!(account.amount, 0);
do_process_instruction(
sync_native(&program_id, &non_native_account_key).unwrap(),
vec![&mut non_native_account],
&[Check::err(TokenError::NonNativeNotSupported.into())],
);
do_process_instruction(
sync_native(&program_id, &native_account_key).unwrap(),
vec![&mut native_account],
&[Check::err(ProgramError::UninitializedAccount)],
);
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,
],
&[Check::success()],
);
let not_program_id = Address::new_unique();
native_account.owner = not_program_id;
do_process_instruction(
sync_native(&program_id, &native_account_key).unwrap(),
vec![&mut native_account],
&[Check::err(ProgramError::IncorrectProgramId)],
);
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],
&[Check::success()],
);
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],
&[Check::success()],
);
let account = Account::unpack_unchecked(&native_account.data).unwrap();
assert_eq!(account.amount, new_lamports);
native_account.lamports -= 1;
do_process_instruction(
sync_native(&program_id, &native_account_key).unwrap(),
vec![&mut native_account],
&[Check::success()],
);
let account = Account::unpack_unchecked(&native_account.data).unwrap();
assert_eq!(account.amount, new_lamports - 1);
}
#[test]
fn test_get_account_data_size() {
let program_id = crate::id();
let owner_key = Address::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 = Address::new_unique();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
&[Check::success()],
);
do_process_instruction(
get_account_data_size(&program_id, &mint_key, &[]).unwrap(),
vec![&mut mint_account],
&[
Check::success(),
Check::return_data(
&ExtensionType::try_calculate_account_len::<Account>(&[])
.unwrap()
.to_le_bytes(),
),
],
);
do_process_instruction(
get_account_data_size(
&program_id,
&mint_key,
&[
ExtensionType::TransferFeeAmount,
ExtensionType::TransferFeeAmount, ],
)
.unwrap(),
vec![&mut mint_account],
&[
Check::success(),
Check::return_data(
&ExtensionType::try_calculate_account_len::<Account>(&[
ExtensionType::TransferFeeAmount,
])
.unwrap()
.to_le_bytes(),
),
],
);
let mut mint_account = native_mint();
do_process_instruction(
get_account_data_size(&program_id, &mint_key, &[]).unwrap(),
vec![&mut mint_account],
&[
Check::success(),
Check::return_data(
&ExtensionType::try_calculate_account_len::<Account>(&[])
.unwrap()
.to_le_bytes(),
),
],
);
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 = Address::new_unique();
do_process_instruction(
initialize_transfer_fee_config(&program_id, &extended_mint_key, None, None, 10, 4242)
.unwrap(),
vec![&mut extended_mint_account],
&[Check::success()],
);
do_process_instruction(
initialize_mint(&program_id, &extended_mint_key, &owner_key, None, 2).unwrap(),
vec![&mut extended_mint_account, &mut rent_sysvar],
&[Check::success()],
);
do_process_instruction(
get_account_data_size(&program_id, &mint_key, &[]).unwrap(),
vec![&mut extended_mint_account],
&[
Check::success(),
Check::return_data(
&ExtensionType::try_calculate_account_len::<Account>(&[
ExtensionType::TransferFeeAmount,
])
.unwrap()
.to_le_bytes(),
),
],
);
do_process_instruction(
get_account_data_size(
&program_id,
&mint_key,
&[ExtensionType::TransferFeeAmount],
)
.unwrap(),
vec![&mut extended_mint_account],
&[
Check::success(),
Check::return_data(
&ExtensionType::try_calculate_account_len::<Account>(&[
ExtensionType::TransferFeeAmount,
])
.unwrap()
.to_le_bytes(),
),
],
);
let mut invalid_mint_account = SolanaAccount::new(
account_minimum_balance(),
Account::get_packed_len(),
&program_id,
);
let invalid_mint_key = Address::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,
],
&[Check::success()],
);
do_process_instruction(
get_account_data_size(&program_id, &invalid_mint_key, &[]).unwrap(),
vec![&mut invalid_mint_account],
&[Check::err(TokenError::InvalidMint.into())],
);
let invalid_program_id = Address::new_unique();
let mut invalid_mint_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let invalid_mint_key = Address::new_unique();
let instruction =
initialize_mint(&program_id, &invalid_mint_key, &owner_key, None, 2).unwrap();
do_process_instruction(
instruction,
vec![&mut invalid_mint_account, &mut rent_sysvar],
&[Check::success()],
);
invalid_mint_account.owner = invalid_program_id;
do_process_instruction(
get_account_data_size(&program_id, &invalid_mint_key, &[]).unwrap(),
vec![&mut invalid_mint_account],
&[Check::err(ProgramError::IncorrectProgramId)],
);
do_process_instruction(
get_account_data_size(&program_id, &mint_key, &[ExtensionType::Uninitialized]).unwrap(),
vec![&mut mint_account],
&[Check::err(TokenError::ExtensionTypeMismatch.into())],
);
do_process_instruction(
get_account_data_size(
&program_id,
&mint_key,
&[
ExtensionType::MemoTransfer,
ExtensionType::MintCloseAuthority,
],
)
.unwrap(),
vec![&mut mint_account],
&[Check::err(TokenError::ExtensionTypeMismatch.into())],
);
}
#[test]
fn test_amount_to_ui_amount() {
let program_id = crate::id();
let owner_key = Address::new_unique();
let mint_key = Address::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(
amount_to_ui_amount(&program_id, &mint_key, 110).unwrap(),
vec![&mut mint_account],
&[Check::err(TokenError::InvalidMint.into())],
);
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
&[Check::success()],
);
do_process_instruction(
amount_to_ui_amount(&program_id, &mint_key, 23).unwrap(),
vec![&mut mint_account],
&[Check::success(), Check::return_data("0.23".as_bytes())],
);
do_process_instruction(
amount_to_ui_amount(&program_id, &mint_key, 110).unwrap(),
vec![&mut mint_account],
&[Check::success(), Check::return_data("1.1".as_bytes())],
);
do_process_instruction(
amount_to_ui_amount(&program_id, &mint_key, 4200).unwrap(),
vec![&mut mint_account],
&[Check::success(), Check::return_data("42".as_bytes())],
);
do_process_instruction(
amount_to_ui_amount(&program_id, &mint_key, 0).unwrap(),
vec![&mut mint_account],
&[Check::success(), Check::return_data("0".as_bytes())],
);
}
#[test]
fn test_ui_amount_to_amount() {
let program_id = crate::id();
let owner_key = Address::new_unique();
let mint_key = Address::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(
ui_amount_to_amount(&program_id, &mint_key, "1.1").unwrap(),
vec![&mut mint_account],
&[Check::err(TokenError::InvalidMint.into())],
);
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
&[Check::success()],
);
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "0.23").unwrap(),
vec![&mut mint_account],
&[Check::success(), Check::return_data(&23u64.to_le_bytes())],
);
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "0.20").unwrap(),
vec![&mut mint_account],
&[Check::success(), Check::return_data(&20u64.to_le_bytes())],
);
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "0.2000").unwrap(),
vec![&mut mint_account],
&[Check::success(), Check::return_data(&20u64.to_le_bytes())],
);
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, ".20").unwrap(),
vec![&mut mint_account],
&[Check::success(), Check::return_data(&20u64.to_le_bytes())],
);
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "1.1").unwrap(),
vec![&mut mint_account],
&[Check::success(), Check::return_data(&110u64.to_le_bytes())],
);
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "1.10").unwrap(),
vec![&mut mint_account],
&[Check::success(), Check::return_data(&110u64.to_le_bytes())],
);
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "42").unwrap(),
vec![&mut mint_account],
&[Check::success(), Check::return_data(&4200u64.to_le_bytes())],
);
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "42.").unwrap(),
vec![&mut mint_account],
&[Check::success(), Check::return_data(&4200u64.to_le_bytes())],
);
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "0").unwrap(),
vec![&mut mint_account],
&[Check::success(), Check::return_data(&0u64.to_le_bytes())],
);
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "").unwrap(),
vec![&mut mint_account],
&[Check::err(ProgramError::InvalidArgument)],
);
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, ".").unwrap(),
vec![&mut mint_account],
&[Check::err(ProgramError::InvalidArgument)],
);
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "0.111").unwrap(),
vec![&mut mint_account],
&[Check::err(ProgramError::InvalidArgument)],
);
do_process_instruction(
ui_amount_to_amount(&program_id, &mint_key, "0.t").unwrap(),
vec![&mut mint_account],
&[Check::err(ProgramError::InvalidArgument)],
);
}
#[test]
fn test_withdraw_excess_lamports_from_multisig() {
let program_id = crate::id();
let mut lamports = 0;
let mut destination_data = vec![];
let system_program_id = system_program::id();
let destination_key = Address::new_unique();
let destination_info = AccountInfo::new(
&destination_key,
true,
false,
&mut lamports,
&mut destination_data,
&system_program_id,
false,
);
let multisig_key = Address::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 = [Address::default(); MAX_SIGNERS];
for signer_key in signer_keys.iter_mut().take(MAX_SIGNERS) {
*signer_key = Address::new_unique();
}
let signer_refs: Vec<&Address> = 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,
);
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,
&[Check::success()],
);
assert_eq!(destination_info.lamports(), excess_lamports);
}
#[test]
fn test_withdraw_excess_lamports_from_account() {
let excess_lamports = 4_000_000_000_000;
let program_id = crate::id();
let account_key = Address::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 = Address::new_unique();
let mut destination_lamports = 0;
let mut destination_data = vec![];
let destination_key = Address::new_unique();
let destination_info = AccountInfo::new(
&destination_key,
true,
false,
&mut destination_lamports,
&mut destination_data,
&system_program_id,
false,
);
let mint_key = Address::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],
&[Check::success()],
);
let mint_info = AccountInfo::new(
&mint_key,
true,
false,
&mut mint_account.lamports,
&mut mint_account.data,
&program_id,
false,
);
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()],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
assert_eq!(destination_info.lamports(), excess_lamports);
}
#[test]
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 = Address::new_unique();
let destination_info = AccountInfo::new(
&destination_key,
true,
false,
&mut destination_lamports,
&mut destination_data,
&system_program_id,
false,
);
let mint_key = Address::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],
&[Check::success()],
);
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(),
],
&[Check::success()],
);
assert_eq!(destination_info.lamports(), excess_lamports);
}
#[test]
fn test_withdraw_excess_lamports_from_mint_with_no_mint_authority() {
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 = Address::new_unique();
let destination_info = AccountInfo::new(
&destination_key,
true,
false,
&mut destination_lamports,
&mut destination_data,
&system_program_id,
false,
);
let mint_key = Address::new_unique();
let mut mint_account = SolanaAccount::new(
excess_lamports + mint_minimum_balance(),
Mint::get_packed_len(),
&program_id,
);
let mut mint_authority_lamports = 0;
let mut mint_authority_data = vec![];
let mint_authority_key = Address::new_unique();
let mint_authority_info = AccountInfo::new(
&mint_authority_key,
true,
false,
&mut mint_authority_lamports,
&mut mint_authority_data,
&system_program_id,
false,
);
let mut rent_sysvar = rent_sysvar();
do_process_instruction(
initialize_mint(&program_id, &mint_key, &mint_authority_key, None, 2).unwrap(),
vec![&mut mint_account, &mut rent_sysvar],
&[Check::success()],
);
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(),
],
&[Check::err(TokenError::OwnerMismatch.into())],
);
do_process_instruction_dups(
set_authority(
&program_id,
&mint_key,
None,
AuthorityType::MintTokens,
&mint_authority_key,
&[],
)
.unwrap(),
vec![mint_info.clone(), mint_authority_info.clone()],
&[Check::success()],
);
do_process_instruction_dups(
withdraw_excess_lamports(
&program_id,
&mint_key,
&destination_key,
&mint_authority_key,
&[],
)
.unwrap(),
vec![
mint_info.clone(),
destination_info.clone(),
mint_authority_info.clone(),
],
&[Check::err(TokenError::AuthorityTypeNotSupported.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(),
],
&[Check::success()],
);
assert_eq!(destination_info.lamports(), excess_lamports);
}
}