From 7c8aace65234cc1150af8cc40017c1ee279f21ff Mon Sep 17 00:00:00 2001 From: febo Date: Sat, 11 Jan 2025 22:12:49 +0000 Subject: [PATCH] Move processor tests --- program/src/processor.rs | 5695 +---------------------------------- program/tests/processor.rs | 5705 ++++++++++++++++++++++++++++++++++++ 2 files changed, 5712 insertions(+), 5688 deletions(-) create mode 100644 program/tests/processor.rs diff --git a/program/src/processor.rs b/program/src/processor.rs index 4702693..56e9e4a 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -1042,18 +1042,7 @@ fn delete_account(account_info: &AccountInfo) -> Result<(), ProgramError> { mod tests { use { super::*, - crate::instruction::*, - serial_test::serial, - solana_program::{ - account_info::IntoAccountInfo, - clock::Epoch, - instruction::Instruction, - program_error::{self, PrintProgramError}, - sysvar::rent, - }, - solana_sdk::account::{ - create_account_for_test, create_is_signer_account_infos, Account as SolanaAccount, - }, + solana_program::{clock::Epoch, program_error::PrintProgramError}, std::sync::{Arc, RwLock}, }; @@ -1061,99 +1050,10 @@ mod tests { static ref EXPECTED_DATA: Arc>> = Arc::new(RwLock::new(Vec::new())); } - fn set_expected_data(expected_data: Vec) { - *EXPECTED_DATA.write().unwrap() = expected_data; - } - - struct SyscallStubs {} - impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { - fn sol_log(&self, _message: &str) {} - - fn sol_invoke_signed( - &self, - _instruction: &Instruction, - _account_infos: &[AccountInfo], - _signers_seeds: &[&[&[u8]]], - ) -> ProgramResult { - Err(ProgramError::Custom(42)) // Not supported - } - - fn sol_get_clock_sysvar(&self, _var_addr: *mut u8) -> u64 { - program_error::UNSUPPORTED_SYSVAR - } - - fn sol_get_epoch_schedule_sysvar(&self, _var_addr: *mut u8) -> u64 { - program_error::UNSUPPORTED_SYSVAR - } - - #[allow(deprecated)] - fn sol_get_fees_sysvar(&self, _var_addr: *mut u8) -> u64 { - program_error::UNSUPPORTED_SYSVAR - } - - fn sol_get_rent_sysvar(&self, var_addr: *mut u8) -> u64 { - unsafe { - *(var_addr as *mut _ as *mut Rent) = Rent::default(); - } - solana_program::entrypoint::SUCCESS - } - - fn sol_set_return_data(&self, data: &[u8]) { - assert_eq!(&*EXPECTED_DATA.write().unwrap(), data) - } - } - - fn do_process_instruction( - instruction: Instruction, - accounts: Vec<&mut SolanaAccount>, - ) -> ProgramResult { - { - use std::sync::Once; - static ONCE: Once = Once::new(); - - ONCE.call_once(|| { - solana_sdk::program_stubs::set_syscall_stubs(Box::new(SyscallStubs {})); - }); - } - - let mut meta = instruction - .accounts - .iter() - .zip(accounts) - .map(|(account_meta, account)| (&account_meta.pubkey, account_meta.is_signer, account)) - .collect::>(); - - let account_infos = create_is_signer_account_infos(&mut meta); - Processor::process(&instruction.program_id, &account_infos, &instruction.data) - } - - fn do_process_instruction_dups( - instruction: Instruction, - account_infos: Vec, - ) -> ProgramResult { - Processor::process(&instruction.program_id, &account_infos, &instruction.data) - } - fn return_token_error_as_program_error() -> ProgramError { TokenError::MintMismatch.into() } - fn rent_sysvar() -> SolanaAccount { - create_account_for_test(&Rent::default()) - } - - fn mint_minimum_balance() -> u64 { - Rent::default().minimum_balance(Mint::get_packed_len()) - } - - fn account_minimum_balance() -> u64 { - Rent::default().minimum_balance(Account::get_packed_len()) - } - - fn multisig_minimum_balance() -> u64 { - Rent::default().minimum_balance(Multisig::get_packed_len()) - } - #[test] fn test_print_error() { let error = return_token_error_as_program_error(); @@ -1275,4021 +1175,12 @@ mod tests { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, - ]; - assert_eq!(packed, expect); - let unpacked = Multisig::unpack(&packed).unwrap(); - assert_eq!(unpacked, check); - } - - #[test] - fn test_initialize_mint() { - let program_id = crate::id(); - let owner_key = Pubkey::new_unique(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = SolanaAccount::new(42, Mint::get_packed_len(), &program_id); - let mint2_key = Pubkey::new_unique(); - let mut mint2_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mut rent_sysvar = rent_sysvar(); - - // mint is not rent exempt - assert_eq!( - Err(TokenError::NotRentExempt.into()), - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar] - ) - ); - - mint_account.lamports = mint_minimum_balance(); - - // create new mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - // create twice - assert_eq!( - Err(TokenError::AlreadyInUse.into()), - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2,).unwrap(), - vec![&mut mint_account, &mut rent_sysvar] - ) - ); - - // create another mint that can freeze - do_process_instruction( - initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(), - vec![&mut mint2_account, &mut rent_sysvar], - ) - .unwrap(); - let mint = Mint::unpack_unchecked(&mint2_account.data).unwrap(); - assert_eq!(mint.freeze_authority, COption::Some(owner_key)); - } - - #[test] - fn test_initialize_mint2() { - let program_id = crate::id(); - let owner_key = Pubkey::new_unique(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = SolanaAccount::new(42, Mint::get_packed_len(), &program_id); - let mint2_key = Pubkey::new_unique(); - let mut mint2_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - - // mint is not rent exempt - assert_eq!( - Err(TokenError::NotRentExempt.into()), - do_process_instruction( - initialize_mint2(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account] - ) - ); - - mint_account.lamports = mint_minimum_balance(); - - // create new mint - do_process_instruction( - initialize_mint2(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account], - ) - .unwrap(); - - // create twice - assert_eq!( - Err(TokenError::AlreadyInUse.into()), - do_process_instruction( - initialize_mint2(&program_id, &mint_key, &owner_key, None, 2,).unwrap(), - vec![&mut mint_account] - ) - ); - - // create another mint that can freeze - do_process_instruction( - initialize_mint2(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(), - vec![&mut mint2_account], - ) - .unwrap(); - let mint = Mint::unpack_unchecked(&mint2_account.data).unwrap(); - assert_eq!(mint.freeze_authority, COption::Some(owner_key)); - } - - #[test] - fn test_initialize_mint_account() { - let program_id = crate::id(); - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new(42, Account::get_packed_len(), &program_id); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mut rent_sysvar = rent_sysvar(); - - // account is not rent exempt - assert_eq!( - Err(TokenError::NotRentExempt.into()), - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar - ], - ) - ); - - account_account.lamports = account_minimum_balance(); - - // mint is not valid (not initialized) - assert_eq!( - Err(TokenError::InvalidMint.into()), - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar - ], - ) - ); - - // create mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - // mint not owned by program - let not_program_id = Pubkey::new_unique(); - mint_account.owner = not_program_id; - assert_eq!( - Err(ProgramError::IncorrectProgramId), - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar - ], - ) - ); - mint_account.owner = program_id; - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create twice - assert_eq!( - Err(TokenError::AlreadyInUse.into()), - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar - ], - ) - ); - } - - #[test] - fn test_transfer_dups() { - let program_id = crate::id(); - let account1_key = Pubkey::new_unique(); - let mut account1_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let mut account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); - let account2_key = Pubkey::new_unique(); - let mut account2_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let mut account2_info: AccountInfo = (&account2_key, false, &mut account2_account).into(); - let account3_key = Pubkey::new_unique(); - let mut account3_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account3_info: AccountInfo = (&account3_key, false, &mut account3_account).into(); - let account4_key = Pubkey::new_unique(); - let mut account4_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account4_info: AccountInfo = (&account4_key, true, &mut account4_account).into(); - let multisig_key = Pubkey::new_unique(); - let mut multisig_account = SolanaAccount::new( - multisig_minimum_balance(), - Multisig::get_packed_len(), - &program_id, - ); - let multisig_info: AccountInfo = (&multisig_key, true, &mut multisig_account).into(); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into(); - let rent_key = rent::id(); - let mut rent_sysvar = rent_sysvar(); - let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); - - // create mint - do_process_instruction_dups( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![mint_info.clone(), rent_info.clone()], - ) - .unwrap(); - - // create account - do_process_instruction_dups( - initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), - vec![ - account1_info.clone(), - mint_info.clone(), - account1_info.clone(), - rent_info.clone(), - ], - ) - .unwrap(); - - // create another account - do_process_instruction_dups( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![ - account2_info.clone(), - mint_info.clone(), - owner_info.clone(), - rent_info.clone(), - ], - ) - .unwrap(); - - // mint to account - do_process_instruction_dups( - mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), - vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], - ) - .unwrap(); - - // source-owner transfer - do_process_instruction_dups( - transfer( - &program_id, - &account1_key, - &account2_key, - &account1_key, - &[], - 500, - ) - .unwrap(), - vec![ - account1_info.clone(), - account2_info.clone(), - account1_info.clone(), - ], - ) - .unwrap(); - - // source-owner TransferChecked - do_process_instruction_dups( - transfer_checked( - &program_id, - &account1_key, - &mint_key, - &account2_key, - &account1_key, - &[], - 500, - 2, - ) - .unwrap(), - vec![ - account1_info.clone(), - mint_info.clone(), - account2_info.clone(), - account1_info.clone(), - ], - ) - .unwrap(); - - // source-delegate transfer - 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( - transfer( - &program_id, - &account1_key, - &account2_key, - &account1_key, - &[], - 500, - ) - .unwrap(), - vec![ - account1_info.clone(), - account2_info.clone(), - account1_info.clone(), - ], - ) - .unwrap(); - - // source-delegate TransferChecked - do_process_instruction_dups( - transfer_checked( - &program_id, - &account1_key, - &mint_key, - &account2_key, - &account1_key, - &[], - 500, - 2, - ) - .unwrap(), - vec![ - account1_info.clone(), - mint_info.clone(), - account2_info.clone(), - account1_info.clone(), - ], - ) - .unwrap(); - - // test destination-owner transfer - do_process_instruction_dups( - initialize_account(&program_id, &account3_key, &mint_key, &account2_key).unwrap(), - vec![ - account3_info.clone(), - mint_info.clone(), - account2_info.clone(), - rent_info.clone(), - ], - ) - .unwrap(); - do_process_instruction_dups( - mint_to(&program_id, &mint_key, &account3_key, &owner_key, &[], 1000).unwrap(), - vec![mint_info.clone(), account3_info.clone(), owner_info.clone()], - ) - .unwrap(); - - account1_info.is_signer = false; - account2_info.is_signer = true; - do_process_instruction_dups( - transfer( - &program_id, - &account3_key, - &account2_key, - &account2_key, - &[], - 500, - ) - .unwrap(), - vec![ - account3_info.clone(), - account2_info.clone(), - account2_info.clone(), - ], - ) - .unwrap(); - - // destination-owner TransferChecked - do_process_instruction_dups( - transfer_checked( - &program_id, - &account3_key, - &mint_key, - &account2_key, - &account2_key, - &[], - 500, - 2, - ) - .unwrap(), - vec![ - account3_info.clone(), - mint_info.clone(), - account2_info.clone(), - account2_info.clone(), - ], - ) - .unwrap(); - - // test source-multisig signer - do_process_instruction_dups( - initialize_multisig(&program_id, &multisig_key, &[&account4_key], 1).unwrap(), - vec![ - multisig_info.clone(), - rent_info.clone(), - account4_info.clone(), - ], - ) - .unwrap(); - - do_process_instruction_dups( - initialize_account(&program_id, &account4_key, &mint_key, &multisig_key).unwrap(), - vec![ - account4_info.clone(), - mint_info.clone(), - multisig_info.clone(), - rent_info.clone(), - ], - ) - .unwrap(); - - do_process_instruction_dups( - mint_to(&program_id, &mint_key, &account4_key, &owner_key, &[], 1000).unwrap(), - vec![mint_info.clone(), account4_info.clone(), owner_info.clone()], - ) - .unwrap(); - - // source-multisig-signer transfer - do_process_instruction_dups( - transfer( - &program_id, - &account4_key, - &account2_key, - &multisig_key, - &[&account4_key], - 500, - ) - .unwrap(), - vec![ - account4_info.clone(), - account2_info.clone(), - multisig_info.clone(), - account4_info.clone(), - ], - ) - .unwrap(); - - // source-multisig-signer TransferChecked - do_process_instruction_dups( - transfer_checked( - &program_id, - &account4_key, - &mint_key, - &account2_key, - &multisig_key, - &[&account4_key], - 500, - 2, - ) - .unwrap(), - vec![ - account4_info.clone(), - mint_info.clone(), - account2_info.clone(), - multisig_info.clone(), - account4_info.clone(), - ], - ) - .unwrap(); - } - - #[test] - fn test_transfer() { - let program_id = crate::id(); - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account2_key = Pubkey::new_unique(); - let mut account2_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account3_key = Pubkey::new_unique(); - let mut account3_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let delegate_key = Pubkey::new_unique(); - let mut delegate_account = SolanaAccount::default(); - let mismatch_key = Pubkey::new_unique(); - let mut mismatch_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = Pubkey::new_unique(); - let mut owner2_account = SolanaAccount::default(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint2_key = Pubkey::new_unique(); - let mut rent_sysvar = rent_sysvar(); - - // create mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account2_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account3_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create mismatch account - do_process_instruction( - initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut mismatch_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap(); - account.mint = mint2_key; - Account::pack(account, &mut mismatch_account.data).unwrap(); - - // mint to account - do_process_instruction( - mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - .unwrap(); - - // missing signer - let mut instruction = transfer( - &program_id, - &account_key, - &account2_key, - &owner_key, - &[], - 1000, - ) - .unwrap(); - instruction.accounts[2].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - do_process_instruction( - instruction, - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - ); - - // mismatch mint - assert_eq!( - Err(TokenError::MintMismatch.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &mismatch_key, - &owner_key, - &[], - 1000 - ) - .unwrap(), - vec![ - &mut account_account, - &mut mismatch_account, - &mut owner_account, - ], - ) - ); - - // missing owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &owner2_key, - &[], - 1000 - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner2_account, - ], - ) - ); - - // account not owned by program - let not_program_id = Pubkey::new_unique(); - account_account.owner = not_program_id; - assert_eq!( - Err(ProgramError::IncorrectProgramId), - do_process_instruction( - transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 0,).unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner2_account, - ], - ) - ); - account_account.owner = program_id; - - // account 2 not owned by program - let not_program_id = Pubkey::new_unique(); - account2_account.owner = not_program_id; - assert_eq!( - Err(ProgramError::IncorrectProgramId), - do_process_instruction( - transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 0,).unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner2_account, - ], - ) - ); - account2_account.owner = program_id; - - // transfer - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &owner_key, - &[], - 1000, - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - .unwrap(); - - // insufficient funds - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 1).unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - ); - - // transfer half back - do_process_instruction( - transfer( - &program_id, - &account2_key, - &account_key, - &owner_key, - &[], - 500, - ) - .unwrap(), - vec![ - &mut account2_account, - &mut account_account, - &mut owner_account, - ], - ) - .unwrap(); - - // incorrect decimals - assert_eq!( - Err(TokenError::MintDecimalsMismatch.into()), - do_process_instruction( - transfer_checked( - &program_id, - &account2_key, - &mint_key, - &account_key, - &owner_key, - &[], - 1, - 10 // <-- incorrect decimals - ) - .unwrap(), - vec![ - &mut account2_account, - &mut mint_account, - &mut account_account, - &mut owner_account, - ], - ) - ); - - // incorrect mint - assert_eq!( - Err(TokenError::MintMismatch.into()), - do_process_instruction( - transfer_checked( - &program_id, - &account2_key, - &account3_key, // <-- incorrect mint - &account_key, - &owner_key, - &[], - 1, - 2 - ) - .unwrap(), - vec![ - &mut account2_account, - &mut account3_account, // <-- incorrect mint - &mut account_account, - &mut owner_account, - ], - ) - ); - // transfer rest with explicit decimals - do_process_instruction( - transfer_checked( - &program_id, - &account2_key, - &mint_key, - &account_key, - &owner_key, - &[], - 500, - 2, - ) - .unwrap(), - vec![ - &mut account2_account, - &mut mint_account, - &mut account_account, - &mut owner_account, - ], - ) - .unwrap(); - - // insufficient funds - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - transfer(&program_id, &account2_key, &account_key, &owner_key, &[], 1).unwrap(), - vec![ - &mut account2_account, - &mut account_account, - &mut owner_account, - ], - ) - ); - - // approve delegate - do_process_instruction( - approve( - &program_id, - &account_key, - &delegate_key, - &owner_key, - &[], - 100, - ) - .unwrap(), - vec![ - &mut account_account, - &mut delegate_account, - &mut owner_account, - ], - ) - .unwrap(); - - // not a delegate of source account - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &owner2_key, // <-- incorrect owner or delegate - &[], - 1, - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner2_account, - ], - ) - ); - - // insufficient funds approved via delegate - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &delegate_key, - &[], - 101 - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut delegate_account, - ], - ) - ); - - // transfer via delegate - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &delegate_key, - &[], - 100, - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut delegate_account, - ], - ) - .unwrap(); - - // insufficient funds approved via delegate - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &delegate_key, - &[], - 1 - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut delegate_account, - ], - ) - ); - - // transfer rest - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &owner_key, - &[], - 900, - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - .unwrap(); - - // approve delegate - do_process_instruction( - approve( - &program_id, - &account_key, - &delegate_key, - &owner_key, - &[], - 100, - ) - .unwrap(), - vec![ - &mut account_account, - &mut delegate_account, - &mut owner_account, - ], - ) - .unwrap(); - - // insufficient funds in source account via delegate - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &delegate_key, - &[], - 100 - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut delegate_account, - ], - ) - ); - } - - #[test] - fn test_self_transfer() { - let program_id = crate::id(); - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account2_key = Pubkey::new_unique(); - let mut account2_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account3_key = Pubkey::new_unique(); - let mut account3_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let delegate_key = Pubkey::new_unique(); - let mut delegate_account = SolanaAccount::default(); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = Pubkey::new_unique(); - let mut owner2_account = SolanaAccount::default(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mut rent_sysvar = rent_sysvar(); - - // create mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account2_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account3_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // mint to account - do_process_instruction( - mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - .unwrap(); - - let account_info = (&account_key, false, &mut account_account).into_account_info(); - let account3_info = (&account3_key, false, &mut account3_account).into_account_info(); - let delegate_info = (&delegate_key, true, &mut delegate_account).into_account_info(); - let owner_info = (&owner_key, true, &mut owner_account).into_account_info(); - let owner2_info = (&owner2_key, true, &mut owner2_account).into_account_info(); - let mint_info = (&mint_key, false, &mut mint_account).into_account_info(); - - // transfer - let instruction = transfer( - &program_id, - account_info.key, - account_info.key, - owner_info.key, - &[], - 1000, - ) - .unwrap(); - assert_eq!( - Ok(()), - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - account_info.clone(), - owner_info.clone(), - ], - &instruction.data, - ) - ); - // no balance change... - let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); - assert_eq!(account.amount, 1000); - - // transfer checked - let instruction = transfer_checked( - &program_id, - account_info.key, - mint_info.key, - account_info.key, - owner_info.key, - &[], - 1000, - 2, - ) - .unwrap(); - assert_eq!( - Ok(()), - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - mint_info.clone(), - account_info.clone(), - owner_info.clone(), - ], - &instruction.data, - ) - ); - // no balance change... - let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); - assert_eq!(account.amount, 1000); - - // missing signer - let mut owner_no_sign_info = owner_info.clone(); - let mut instruction = transfer( - &program_id, - account_info.key, - account_info.key, - owner_no_sign_info.key, - &[], - 1000, - ) - .unwrap(); - instruction.accounts[2].is_signer = false; - owner_no_sign_info.is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - account_info.clone(), - owner_no_sign_info.clone(), - ], - &instruction.data, - ) - ); - - // missing signer checked - let mut instruction = transfer_checked( - &program_id, - account_info.key, - mint_info.key, - account_info.key, - owner_no_sign_info.key, - &[], - 1000, - 2, - ) - .unwrap(); - instruction.accounts[3].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - mint_info.clone(), - account_info.clone(), - owner_no_sign_info, - ], - &instruction.data, - ) - ); - - // missing owner - let instruction = transfer( - &program_id, - account_info.key, - account_info.key, - owner2_info.key, - &[], - 1000, - ) - .unwrap(); - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - account_info.clone(), - owner2_info.clone(), - ], - &instruction.data, - ) - ); - - // missing owner checked - let instruction = transfer_checked( - &program_id, - account_info.key, - mint_info.key, - account_info.key, - owner2_info.key, - &[], - 1000, - 2, - ) - .unwrap(); - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - mint_info.clone(), - account_info.clone(), - owner2_info.clone(), - ], - &instruction.data, - ) - ); - - // insufficient funds - let instruction = transfer( - &program_id, - account_info.key, - account_info.key, - owner_info.key, - &[], - 1001, - ) - .unwrap(); - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - account_info.clone(), - owner_info.clone(), - ], - &instruction.data, - ) - ); - - // insufficient funds checked - let instruction = transfer_checked( - &program_id, - account_info.key, - mint_info.key, - account_info.key, - owner_info.key, - &[], - 1001, - 2, - ) - .unwrap(); - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - mint_info.clone(), - account_info.clone(), - owner_info.clone(), - ], - &instruction.data, - ) - ); - - // incorrect decimals - let instruction = transfer_checked( - &program_id, - account_info.key, - mint_info.key, - account_info.key, - owner_info.key, - &[], - 1, - 10, // <-- incorrect decimals - ) - .unwrap(); - assert_eq!( - Err(TokenError::MintDecimalsMismatch.into()), - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - mint_info.clone(), - account_info.clone(), - owner_info.clone(), - ], - &instruction.data, - ) - ); - - // incorrect mint - let instruction = transfer_checked( - &program_id, - account_info.key, - account3_info.key, // <-- incorrect mint - account_info.key, - owner_info.key, - &[], - 1, - 2, - ) - .unwrap(); - assert_eq!( - Err(TokenError::MintMismatch.into()), - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - account3_info.clone(), // <-- incorrect mint - account_info.clone(), - owner_info.clone(), - ], - &instruction.data, - ) - ); - - // approve delegate - let instruction = approve( - &program_id, - account_info.key, - delegate_info.key, - owner_info.key, - &[], - 100, - ) - .unwrap(); - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - delegate_info.clone(), - owner_info.clone(), - ], - &instruction.data, - ) - .unwrap(); - - // delegate transfer - let instruction = transfer( - &program_id, - account_info.key, - account_info.key, - delegate_info.key, - &[], - 100, - ) - .unwrap(); - assert_eq!( - Ok(()), - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - account_info.clone(), - delegate_info.clone(), - ], - &instruction.data, - ) - ); - // no balance change... - let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); - assert_eq!(account.amount, 1000); - assert_eq!(account.delegated_amount, 100); - - // delegate transfer checked - let instruction = transfer_checked( - &program_id, - account_info.key, - mint_info.key, - account_info.key, - delegate_info.key, - &[], - 100, - 2, - ) - .unwrap(); - assert_eq!( - Ok(()), - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - mint_info.clone(), - account_info.clone(), - delegate_info.clone(), - ], - &instruction.data, - ) - ); - // no balance change... - let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); - assert_eq!(account.amount, 1000); - assert_eq!(account.delegated_amount, 100); - - // delegate insufficient funds - let instruction = transfer( - &program_id, - account_info.key, - account_info.key, - delegate_info.key, - &[], - 101, - ) - .unwrap(); - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - account_info.clone(), - delegate_info.clone(), - ], - &instruction.data, - ) - ); - - // delegate insufficient funds checked - let instruction = transfer_checked( - &program_id, - account_info.key, - mint_info.key, - account_info.key, - delegate_info.key, - &[], - 101, - 2, - ) - .unwrap(); - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - mint_info.clone(), - account_info.clone(), - delegate_info.clone(), - ], - &instruction.data, - ) - ); - - // owner transfer with delegate assigned - let instruction = transfer( - &program_id, - account_info.key, - account_info.key, - owner_info.key, - &[], - 1000, - ) - .unwrap(); - assert_eq!( - Ok(()), - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - account_info.clone(), - owner_info.clone(), - ], - &instruction.data, - ) - ); - // no balance change... - let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); - assert_eq!(account.amount, 1000); - - // owner transfer with delegate assigned checked - let instruction = transfer_checked( - &program_id, - account_info.key, - mint_info.key, - account_info.key, - owner_info.key, - &[], - 1000, - 2, - ) - .unwrap(); - assert_eq!( - Ok(()), - Processor::process( - &instruction.program_id, - &[ - account_info.clone(), - mint_info.clone(), - account_info.clone(), - owner_info.clone(), - ], - &instruction.data, - ) - ); - // no balance change... - let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); - assert_eq!(account.amount, 1000); - } - - #[test] - fn test_mintable_token_with_zero_supply() { - let program_id = crate::id(); - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mut rent_sysvar = rent_sysvar(); - - // create mint-able token with zero supply - let decimals = 2; - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, decimals).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); - assert_eq!( - mint, - Mint { - mint_authority: COption::Some(owner_key), - supply: 0, - decimals, - is_initialized: true, - freeze_authority: COption::None, - } - ); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // mint to - do_process_instruction( - mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - .unwrap(); - let _ = Mint::unpack(&mint_account.data).unwrap(); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 42); - - // mint to 2, with incorrect decimals - assert_eq!( - Err(TokenError::MintDecimalsMismatch.into()), - do_process_instruction( - mint_to_checked( - &program_id, - &mint_key, - &account_key, - &owner_key, - &[], - 42, - decimals + 1 - ) - .unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - ); - - let _ = Mint::unpack(&mint_account.data).unwrap(); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 42); - - // mint to 2 - do_process_instruction( - mint_to_checked( - &program_id, - &mint_key, - &account_key, - &owner_key, - &[], - 42, - decimals, - ) - .unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - .unwrap(); - let _ = Mint::unpack(&mint_account.data).unwrap(); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 84); - } - - #[test] - fn test_approve_dups() { - let program_id = crate::id(); - let account1_key = Pubkey::new_unique(); - let mut account1_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); - let account2_key = Pubkey::new_unique(); - let mut account2_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account2_info: AccountInfo = (&account2_key, false, &mut account2_account).into(); - let account3_key = Pubkey::new_unique(); - let mut account3_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account3_info: AccountInfo = (&account3_key, true, &mut account3_account).into(); - let multisig_key = Pubkey::new_unique(); - let mut multisig_account = SolanaAccount::new( - multisig_minimum_balance(), - Multisig::get_packed_len(), - &program_id, - ); - let multisig_info: AccountInfo = (&multisig_key, true, &mut multisig_account).into(); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into(); - let rent_key = rent::id(); - let mut rent_sysvar = rent_sysvar(); - let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); - - // create mint - do_process_instruction_dups( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![mint_info.clone(), rent_info.clone()], - ) - .unwrap(); - - // create account - do_process_instruction_dups( - initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), - vec![ - account1_info.clone(), - mint_info.clone(), - account1_info.clone(), - rent_info.clone(), - ], - ) - .unwrap(); - - // create another account - do_process_instruction_dups( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![ - account2_info.clone(), - mint_info.clone(), - owner_info.clone(), - rent_info.clone(), - ], - ) - .unwrap(); - - // mint to account - do_process_instruction_dups( - mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), - vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], - ) - .unwrap(); - - // source-owner approve - do_process_instruction_dups( - approve( - &program_id, - &account1_key, - &account2_key, - &account1_key, - &[], - 500, - ) - .unwrap(), - vec![ - account1_info.clone(), - account2_info.clone(), - account1_info.clone(), - ], - ) - .unwrap(); - - // source-owner approve_checked - do_process_instruction_dups( - approve_checked( - &program_id, - &account1_key, - &mint_key, - &account2_key, - &account1_key, - &[], - 500, - 2, - ) - .unwrap(), - vec![ - account1_info.clone(), - mint_info.clone(), - account2_info.clone(), - account1_info.clone(), - ], - ) - .unwrap(); - - // source-owner revoke - do_process_instruction_dups( - revoke(&program_id, &account1_key, &account1_key, &[]).unwrap(), - vec![account1_info.clone(), account1_info.clone()], - ) - .unwrap(); - - // test source-multisig signer - do_process_instruction_dups( - initialize_multisig(&program_id, &multisig_key, &[&account3_key], 1).unwrap(), - vec![ - multisig_info.clone(), - rent_info.clone(), - account3_info.clone(), - ], - ) - .unwrap(); - - do_process_instruction_dups( - initialize_account(&program_id, &account3_key, &mint_key, &multisig_key).unwrap(), - vec![ - account3_info.clone(), - mint_info.clone(), - multisig_info.clone(), - rent_info.clone(), - ], - ) - .unwrap(); - - do_process_instruction_dups( - mint_to(&program_id, &mint_key, &account3_key, &owner_key, &[], 1000).unwrap(), - vec![mint_info.clone(), account3_info.clone(), owner_info.clone()], - ) - .unwrap(); - - // source-multisig-signer approve - do_process_instruction_dups( - approve( - &program_id, - &account3_key, - &account2_key, - &multisig_key, - &[&account3_key], - 500, - ) - .unwrap(), - vec![ - account3_info.clone(), - account2_info.clone(), - multisig_info.clone(), - account3_info.clone(), - ], - ) - .unwrap(); - - // source-multisig-signer approve_checked - do_process_instruction_dups( - approve_checked( - &program_id, - &account3_key, - &mint_key, - &account2_key, - &multisig_key, - &[&account3_key], - 500, - 2, - ) - .unwrap(), - vec![ - account3_info.clone(), - mint_info.clone(), - account2_info.clone(), - multisig_info.clone(), - account3_info.clone(), - ], - ) - .unwrap(); - - // source-owner multisig-signer - do_process_instruction_dups( - revoke(&program_id, &account3_key, &multisig_key, &[&account3_key]).unwrap(), - vec![ - account3_info.clone(), - multisig_info.clone(), - account3_info.clone(), - ], - ) - .unwrap(); - } - - #[test] - fn test_approve() { - let program_id = crate::id(); - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account2_key = Pubkey::new_unique(); - let mut account2_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let delegate_key = Pubkey::new_unique(); - let mut delegate_account = SolanaAccount::default(); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = Pubkey::new_unique(); - let mut owner2_account = SolanaAccount::default(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mut rent_sysvar = rent_sysvar(); - - // create mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account2_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // mint to account - do_process_instruction( - mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - .unwrap(); - - // missing signer - let mut instruction = approve( - &program_id, - &account_key, - &delegate_key, - &owner_key, - &[], - 100, - ) - .unwrap(); - instruction.accounts[2].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - do_process_instruction( - instruction, - vec![ - &mut account_account, - &mut delegate_account, - &mut owner_account, - ], - ) - ); - - // no owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - approve( - &program_id, - &account_key, - &delegate_key, - &owner2_key, - &[], - 100 - ) - .unwrap(), - vec![ - &mut account_account, - &mut delegate_account, - &mut owner2_account, - ], - ) - ); - - // approve delegate - do_process_instruction( - approve( - &program_id, - &account_key, - &delegate_key, - &owner_key, - &[], - 100, - ) - .unwrap(), - vec![ - &mut account_account, - &mut delegate_account, - &mut owner_account, - ], - ) - .unwrap(); - - // approve delegate 2, with incorrect decimals - assert_eq!( - Err(TokenError::MintDecimalsMismatch.into()), - do_process_instruction( - approve_checked( - &program_id, - &account_key, - &mint_key, - &delegate_key, - &owner_key, - &[], - 100, - 0 // <-- incorrect decimals - ) - .unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut delegate_account, - &mut owner_account, - ], - ) - ); - - // approve delegate 2, with incorrect mint - assert_eq!( - Err(TokenError::MintMismatch.into()), - do_process_instruction( - approve_checked( - &program_id, - &account_key, - &account2_key, // <-- bad mint - &delegate_key, - &owner_key, - &[], - 100, - 0 - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, // <-- bad mint - &mut delegate_account, - &mut owner_account, - ], - ) - ); - - // approve delegate 2 - do_process_instruction( - approve_checked( - &program_id, - &account_key, - &mint_key, - &delegate_key, - &owner_key, - &[], - 100, - 2, - ) - .unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut delegate_account, - &mut owner_account, - ], - ) - .unwrap(); - - // revoke delegate - do_process_instruction( - revoke(&program_id, &account_key, &owner_key, &[]).unwrap(), - vec![&mut account_account, &mut owner_account], - ) - .unwrap(); - } - - #[test] - fn test_set_authority_dups() { - let program_id = crate::id(); - let account1_key = Pubkey::new_unique(); - let mut account1_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); - let owner_key = Pubkey::new_unique(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into(); - let rent_key = rent::id(); - let mut rent_sysvar = rent_sysvar(); - let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); - - // create mint - do_process_instruction_dups( - initialize_mint(&program_id, &mint_key, &mint_key, Some(&mint_key), 2).unwrap(), - vec![mint_info.clone(), rent_info.clone()], - ) - .unwrap(); - - // create account - do_process_instruction_dups( - initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), - vec![ - account1_info.clone(), - mint_info.clone(), - account1_info.clone(), - rent_info.clone(), - ], - ) - .unwrap(); - - // set mint_authority when currently self - do_process_instruction_dups( - set_authority( - &program_id, - &mint_key, - Some(&owner_key), - AuthorityType::MintTokens, - &mint_key, - &[], - ) - .unwrap(), - vec![mint_info.clone(), mint_info.clone()], - ) - .unwrap(); - - // set freeze_authority when currently self - do_process_instruction_dups( - set_authority( - &program_id, - &mint_key, - Some(&owner_key), - AuthorityType::FreezeAccount, - &mint_key, - &[], - ) - .unwrap(), - vec![mint_info.clone(), mint_info.clone()], - ) - .unwrap(); - - // set account owner when currently self - do_process_instruction_dups( - set_authority( - &program_id, - &account1_key, - Some(&owner_key), - AuthorityType::AccountOwner, - &account1_key, - &[], - ) - .unwrap(), - vec![account1_info.clone(), account1_info.clone()], - ) - .unwrap(); - - // set close_authority when currently self - let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); - account.close_authority = COption::Some(account1_key); - Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); - - do_process_instruction_dups( - set_authority( - &program_id, - &account1_key, - Some(&owner_key), - AuthorityType::CloseAccount, - &account1_key, - &[], - ) - .unwrap(), - vec![account1_info.clone(), account1_info.clone()], - ) - .unwrap(); - } - - #[test] - fn test_set_authority() { - let program_id = crate::id(); - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account2_key = Pubkey::new_unique(); - let mut account2_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = Pubkey::new_unique(); - let mut owner2_account = SolanaAccount::default(); - let owner3_key = Pubkey::new_unique(); - let mut owner3_account = SolanaAccount::default(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint2_key = Pubkey::new_unique(); - let mut mint2_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mut rent_sysvar = rent_sysvar(); - - // create new mint with owner - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - // create mint with owner and freeze_authority - do_process_instruction( - initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(), - vec![&mut mint2_account, &mut rent_sysvar], - ) - .unwrap(); - - // invalid account - assert_eq!( - Err(ProgramError::UninitializedAccount), - do_process_instruction( - set_authority( - &program_id, - &account_key, - Some(&owner2_key), - AuthorityType::AccountOwner, - &owner_key, - &[] - ) - .unwrap(), - vec![&mut account_account, &mut owner_account], - ) - ); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint2_key, &owner_key).unwrap(), - vec![ - &mut account2_account, - &mut mint2_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // missing owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - set_authority( - &program_id, - &account_key, - Some(&owner_key), - AuthorityType::AccountOwner, - &owner2_key, - &[] - ) - .unwrap(), - vec![&mut account_account, &mut owner2_account], - ) - ); - - // owner did not sign - let mut instruction = set_authority( - &program_id, - &account_key, - Some(&owner2_key), - AuthorityType::AccountOwner, - &owner_key, - &[], - ) - .unwrap(); - instruction.accounts[1].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - do_process_instruction(instruction, vec![&mut account_account, &mut owner_account,],) - ); - - // wrong authority type - assert_eq!( - Err(TokenError::AuthorityTypeNotSupported.into()), - do_process_instruction( - set_authority( - &program_id, - &account_key, - Some(&owner2_key), - AuthorityType::FreezeAccount, - &owner_key, - &[], - ) - .unwrap(), - vec![&mut account_account, &mut owner_account], - ) - ); - - // account owner may not be set to None - assert_eq!( - Err(TokenError::InvalidInstruction.into()), - do_process_instruction( - set_authority( - &program_id, - &account_key, - None, - AuthorityType::AccountOwner, - &owner_key, - &[], - ) - .unwrap(), - vec![&mut account_account, &mut owner_account], - ) - ); - - // set delegate - do_process_instruction( - approve( - &program_id, - &account_key, - &owner2_key, - &owner_key, - &[], - u64::MAX, - ) - .unwrap(), - vec![ - &mut account_account, - &mut owner2_account, - &mut owner_account, - ], - ) - .unwrap(); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.delegate, COption::Some(owner2_key)); - assert_eq!(account.delegated_amount, u64::MAX); - - // set owner - do_process_instruction( - set_authority( - &program_id, - &account_key, - Some(&owner3_key), - AuthorityType::AccountOwner, - &owner_key, - &[], - ) - .unwrap(), - vec![&mut account_account, &mut owner_account], - ) - .unwrap(); - - // check delegate cleared - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.delegate, COption::None); - assert_eq!(account.delegated_amount, 0); - - // set owner without existing delegate - do_process_instruction( - set_authority( - &program_id, - &account_key, - Some(&owner2_key), - AuthorityType::AccountOwner, - &owner3_key, - &[], - ) - .unwrap(), - vec![&mut account_account, &mut owner3_account], - ) - .unwrap(); - - // set close_authority - do_process_instruction( - set_authority( - &program_id, - &account_key, - Some(&owner2_key), - AuthorityType::CloseAccount, - &owner2_key, - &[], - ) - .unwrap(), - vec![&mut account_account, &mut owner2_account], - ) - .unwrap(); - - // close_authority may be set to None - do_process_instruction( - set_authority( - &program_id, - &account_key, - None, - AuthorityType::CloseAccount, - &owner2_key, - &[], - ) - .unwrap(), - vec![&mut account_account, &mut owner2_account], - ) - .unwrap(); - - // wrong owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - set_authority( - &program_id, - &mint_key, - Some(&owner3_key), - AuthorityType::MintTokens, - &owner2_key, - &[] - ) - .unwrap(), - vec![&mut mint_account, &mut owner2_account], - ) - ); - - // owner did not sign - let mut instruction = set_authority( - &program_id, - &mint_key, - Some(&owner2_key), - AuthorityType::MintTokens, - &owner_key, - &[], - ) - .unwrap(); - instruction.accounts[1].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - do_process_instruction(instruction, vec![&mut mint_account, &mut owner_account],) - ); - - // cannot freeze - assert_eq!( - Err(TokenError::MintCannotFreeze.into()), - do_process_instruction( - set_authority( - &program_id, - &mint_key, - Some(&owner2_key), - AuthorityType::FreezeAccount, - &owner_key, - &[], - ) - .unwrap(), - vec![&mut mint_account, &mut owner_account], - ) - ); - - // set owner - do_process_instruction( - set_authority( - &program_id, - &mint_key, - Some(&owner2_key), - AuthorityType::MintTokens, - &owner_key, - &[], - ) - .unwrap(), - vec![&mut mint_account, &mut owner_account], - ) - .unwrap(); - - // set owner to None - do_process_instruction( - set_authority( - &program_id, - &mint_key, - None, - AuthorityType::MintTokens, - &owner2_key, - &[], - ) - .unwrap(), - vec![&mut mint_account, &mut owner2_account], - ) - .unwrap(); - - // test unsetting mint_authority is one-way operation - assert_eq!( - Err(TokenError::FixedSupply.into()), - do_process_instruction( - set_authority( - &program_id, - &mint2_key, - Some(&owner2_key), - AuthorityType::MintTokens, - &owner_key, - &[] - ) - .unwrap(), - vec![&mut mint_account, &mut owner_account], - ) - ); - - // set freeze_authority - do_process_instruction( - set_authority( - &program_id, - &mint2_key, - Some(&owner2_key), - AuthorityType::FreezeAccount, - &owner_key, - &[], - ) - .unwrap(), - vec![&mut mint2_account, &mut owner_account], - ) - .unwrap(); - - // test unsetting freeze_authority is one-way operation - do_process_instruction( - set_authority( - &program_id, - &mint2_key, - None, - AuthorityType::FreezeAccount, - &owner2_key, - &[], - ) - .unwrap(), - vec![&mut mint2_account, &mut owner2_account], - ) - .unwrap(); - - assert_eq!( - Err(TokenError::MintCannotFreeze.into()), - do_process_instruction( - set_authority( - &program_id, - &mint2_key, - Some(&owner2_key), - AuthorityType::FreezeAccount, - &owner_key, - &[], - ) - .unwrap(), - vec![&mut mint2_account, &mut owner2_account], - ) - ); - } - - #[test] - fn test_mint_to_dups() { - let program_id = crate::id(); - let account1_key = Pubkey::new_unique(); - let mut account1_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into(); - let rent_key = rent::id(); - let mut rent_sysvar = rent_sysvar(); - let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); - - // create mint - do_process_instruction_dups( - initialize_mint(&program_id, &mint_key, &mint_key, None, 2).unwrap(), - vec![mint_info.clone(), rent_info.clone()], - ) - .unwrap(); - - // create account - do_process_instruction_dups( - initialize_account(&program_id, &account1_key, &mint_key, &owner_key).unwrap(), - vec![ - account1_info.clone(), - mint_info.clone(), - owner_info.clone(), - rent_info.clone(), - ], - ) - .unwrap(); - - // mint_to when mint_authority is self - do_process_instruction_dups( - mint_to(&program_id, &mint_key, &account1_key, &mint_key, &[], 42).unwrap(), - vec![mint_info.clone(), account1_info.clone(), mint_info.clone()], - ) - .unwrap(); - - // mint_to_checked when mint_authority is self - do_process_instruction_dups( - mint_to_checked(&program_id, &mint_key, &account1_key, &mint_key, &[], 42, 2).unwrap(), - vec![mint_info.clone(), account1_info.clone(), mint_info.clone()], - ) - .unwrap(); - - // mint_to when mint_authority is account owner - let mut mint = Mint::unpack_unchecked(&mint_info.data.borrow()).unwrap(); - mint.mint_authority = COption::Some(account1_key); - Mint::pack(mint, &mut mint_info.data.borrow_mut()).unwrap(); - do_process_instruction_dups( - mint_to( - &program_id, - &mint_key, - &account1_key, - &account1_key, - &[], - 42, - ) - .unwrap(), - vec![ - mint_info.clone(), - account1_info.clone(), - account1_info.clone(), - ], - ) - .unwrap(); - - // mint_to_checked when mint_authority is account owner - do_process_instruction_dups( - mint_to( - &program_id, - &mint_key, - &account1_key, - &account1_key, - &[], - 42, - ) - .unwrap(), - vec![ - mint_info.clone(), - account1_info.clone(), - account1_info.clone(), - ], - ) - .unwrap(); - } - - #[test] - fn test_mint_to() { - let program_id = crate::id(); - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account2_key = Pubkey::new_unique(); - let mut account2_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account3_key = Pubkey::new_unique(); - let mut account3_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let mismatch_key = Pubkey::new_unique(); - let mut mismatch_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = Pubkey::new_unique(); - let mut owner2_account = SolanaAccount::default(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint2_key = Pubkey::new_unique(); - let uninitialized_key = Pubkey::new_unique(); - let mut uninitialized_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let mut rent_sysvar = rent_sysvar(); - - // create new mint with owner - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account2_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account3_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create mismatch account - do_process_instruction( - initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut mismatch_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap(); - account.mint = mint2_key; - Account::pack(account, &mut mismatch_account.data).unwrap(); - - // mint to - do_process_instruction( - mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - .unwrap(); - - let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); - assert_eq!(mint.supply, 42); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 42); - - // mint to another account to test supply accumulation - do_process_instruction( - mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(), - vec![&mut mint_account, &mut account2_account, &mut owner_account], - ) - .unwrap(); - - let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); - assert_eq!(mint.supply, 84); - let account = Account::unpack_unchecked(&account2_account.data).unwrap(); - assert_eq!(account.amount, 42); - - // missing signer - let mut instruction = - mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(); - instruction.accounts[2].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - do_process_instruction( - instruction, - vec![&mut mint_account, &mut account2_account, &mut owner_account], - ) - ); - - // mismatch account - assert_eq!( - Err(TokenError::MintMismatch.into()), - do_process_instruction( - mint_to(&program_id, &mint_key, &mismatch_key, &owner_key, &[], 42).unwrap(), - vec![&mut mint_account, &mut mismatch_account, &mut owner_account], - ) - ); - - // missing owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - mint_to(&program_id, &mint_key, &account2_key, &owner2_key, &[], 42).unwrap(), - vec![ - &mut mint_account, - &mut account2_account, - &mut owner2_account, - ], - ) - ); - - // mint not owned by program - let not_program_id = Pubkey::new_unique(); - mint_account.owner = not_program_id; - assert_eq!( - Err(ProgramError::IncorrectProgramId), - do_process_instruction( - mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 0).unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - ); - mint_account.owner = program_id; - - // account not owned by program - let not_program_id = Pubkey::new_unique(); - account_account.owner = not_program_id; - assert_eq!( - Err(ProgramError::IncorrectProgramId), - do_process_instruction( - mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 0).unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - ); - account_account.owner = program_id; - - // uninitialized destination account - assert_eq!( - Err(ProgramError::UninitializedAccount), - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &uninitialized_key, - &owner_key, - &[], - 42 - ) - .unwrap(), - vec![ - &mut mint_account, - &mut uninitialized_account, - &mut owner_account, - ], - ) - ); - - // unset mint_authority and test minting fails - do_process_instruction( - set_authority( - &program_id, - &mint_key, - None, - AuthorityType::MintTokens, - &owner_key, - &[], - ) - .unwrap(), - vec![&mut mint_account, &mut owner_account], - ) - .unwrap(); - assert_eq!( - Err(TokenError::FixedSupply.into()), - do_process_instruction( - mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(), - vec![&mut mint_account, &mut account2_account, &mut owner_account], - ) - ); - } - - #[test] - fn test_burn_dups() { - let program_id = crate::id(); - let account1_key = Pubkey::new_unique(); - let mut account1_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into(); - let rent_key = rent::id(); - let mut rent_sysvar = rent_sysvar(); - let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); - - // create mint - do_process_instruction_dups( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![mint_info.clone(), rent_info.clone()], - ) - .unwrap(); - - // create account - do_process_instruction_dups( - initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), - vec![ - account1_info.clone(), - mint_info.clone(), - account1_info.clone(), - rent_info.clone(), - ], - ) - .unwrap(); - - // mint to account - do_process_instruction_dups( - mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), - vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], - ) - .unwrap(); - - // source-owner burn - do_process_instruction_dups( - burn( - &program_id, - &mint_key, - &account1_key, - &account1_key, - &[], - 500, - ) - .unwrap(), - vec![ - account1_info.clone(), - mint_info.clone(), - account1_info.clone(), - ], - ) - .unwrap(); - - // source-owner burn_checked - do_process_instruction_dups( - burn_checked( - &program_id, - &account1_key, - &mint_key, - &account1_key, - &[], - 500, - 2, - ) - .unwrap(), - vec![ - account1_info.clone(), - mint_info.clone(), - account1_info.clone(), - ], - ) - .unwrap(); - - // mint-owner burn - do_process_instruction_dups( - mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), - vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], - ) - .unwrap(); - let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); - account.owner = mint_key; - Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); - do_process_instruction_dups( - burn(&program_id, &account1_key, &mint_key, &mint_key, &[], 500).unwrap(), - vec![account1_info.clone(), mint_info.clone(), mint_info.clone()], - ) - .unwrap(); - - // mint-owner burn_checked - do_process_instruction_dups( - burn_checked( - &program_id, - &account1_key, - &mint_key, - &mint_key, - &[], - 500, - 2, - ) - .unwrap(), - vec![account1_info.clone(), mint_info.clone(), mint_info.clone()], - ) - .unwrap(); - - // source-delegate burn - do_process_instruction_dups( - mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), - vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], - ) - .unwrap(); - let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); - account.delegated_amount = 1000; - account.delegate = COption::Some(account1_key); - account.owner = owner_key; - Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); - do_process_instruction_dups( - burn( - &program_id, - &account1_key, - &mint_key, - &account1_key, - &[], - 500, - ) - .unwrap(), - vec![ - account1_info.clone(), - mint_info.clone(), - account1_info.clone(), - ], - ) - .unwrap(); - - // source-delegate burn_checked - do_process_instruction_dups( - burn_checked( - &program_id, - &account1_key, - &mint_key, - &account1_key, - &[], - 500, - 2, - ) - .unwrap(), - vec![ - account1_info.clone(), - mint_info.clone(), - account1_info.clone(), - ], - ) - .unwrap(); - - // mint-delegate burn - do_process_instruction_dups( - mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), - vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], - ) - .unwrap(); - let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); - account.delegated_amount = 1000; - account.delegate = COption::Some(mint_key); - account.owner = owner_key; - Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); - do_process_instruction_dups( - burn(&program_id, &account1_key, &mint_key, &mint_key, &[], 500).unwrap(), - vec![account1_info.clone(), mint_info.clone(), mint_info.clone()], - ) - .unwrap(); - - // mint-delegate burn_checked - do_process_instruction_dups( - burn_checked( - &program_id, - &account1_key, - &mint_key, - &mint_key, - &[], - 500, - 2, - ) - .unwrap(), - vec![account1_info.clone(), mint_info.clone(), mint_info.clone()], - ) - .unwrap(); - } - - #[test] - fn test_burn() { - let program_id = crate::id(); - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account2_key = Pubkey::new_unique(); - let mut account2_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account3_key = Pubkey::new_unique(); - let mut account3_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let delegate_key = Pubkey::new_unique(); - let mut delegate_account = SolanaAccount::default(); - let mismatch_key = Pubkey::new_unique(); - let mut mismatch_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = Pubkey::new_unique(); - let mut owner2_account = SolanaAccount::default(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint2_key = Pubkey::new_unique(); - let mut rent_sysvar = rent_sysvar(); - - // create new mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account2_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account3_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create mismatch account - do_process_instruction( - initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut mismatch_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // mint to account - do_process_instruction( - mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - .unwrap(); - - // mint to mismatch account and change mint key - do_process_instruction( - mint_to(&program_id, &mint_key, &mismatch_key, &owner_key, &[], 1000).unwrap(), - vec![&mut mint_account, &mut mismatch_account, &mut owner_account], - ) - .unwrap(); - let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap(); - account.mint = mint2_key; - Account::pack(account, &mut mismatch_account.data).unwrap(); - - // missing signer - let mut instruction = - burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 42).unwrap(); - instruction.accounts[1].is_signer = false; - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - instruction, - vec![ - &mut account_account, - &mut mint_account, - &mut delegate_account - ], - ) - ); - - // missing owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - burn(&program_id, &account_key, &mint_key, &owner2_key, &[], 42).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner2_account], - ) - ); - - // account not owned by program - let not_program_id = Pubkey::new_unique(); - account_account.owner = not_program_id; - assert_eq!( - Err(ProgramError::IncorrectProgramId), - do_process_instruction( - burn(&program_id, &account_key, &mint_key, &owner_key, &[], 0).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - ); - account_account.owner = program_id; - - // mint not owned by program - let not_program_id = Pubkey::new_unique(); - mint_account.owner = not_program_id; - assert_eq!( - Err(ProgramError::IncorrectProgramId), - do_process_instruction( - burn(&program_id, &account_key, &mint_key, &owner_key, &[], 0).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - ); - mint_account.owner = program_id; - - // mint mismatch - assert_eq!( - Err(TokenError::MintMismatch.into()), - do_process_instruction( - burn(&program_id, &mismatch_key, &mint_key, &owner_key, &[], 42).unwrap(), - vec![&mut mismatch_account, &mut mint_account, &mut owner_account], - ) - ); - - // burn - do_process_instruction( - burn(&program_id, &account_key, &mint_key, &owner_key, &[], 21).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // burn_checked, with incorrect decimals - assert_eq!( - Err(TokenError::MintDecimalsMismatch.into()), - do_process_instruction( - burn_checked(&program_id, &account_key, &mint_key, &owner_key, &[], 21, 3).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - ); - - // burn_checked - do_process_instruction( - burn_checked(&program_id, &account_key, &mint_key, &owner_key, &[], 21, 2).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); - assert_eq!(mint.supply, 2000 - 42); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 1000 - 42); - - // insufficient funds - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - burn( - &program_id, - &account_key, - &mint_key, - &owner_key, - &[], - 100_000_000 - ) - .unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - ); - - // approve delegate - do_process_instruction( - approve( - &program_id, - &account_key, - &delegate_key, - &owner_key, - &[], - 84, - ) - .unwrap(), - vec![ - &mut account_account, - &mut delegate_account, - &mut owner_account, - ], - ) - .unwrap(); - - // not a delegate of source account - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - burn( - &program_id, - &account_key, - &mint_key, - &owner2_key, // <-- incorrect owner or delegate - &[], - 1, - ) - .unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner2_account], - ) - ); - - // insufficient funds approved via delegate - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 85).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut delegate_account - ], - ) - ); - - // burn via delegate - do_process_instruction( - burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 84).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut delegate_account, - ], - ) - .unwrap(); - - // match - 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); - - // insufficient funds approved via delegate - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 1).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut delegate_account - ], - ) - ); - } - - #[test] - fn test_burn_and_close_system_and_incinerator_tokens() { - let program_id = crate::id(); - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let incinerator_account_key = Pubkey::new_unique(); - let mut incinerator_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let system_account_key = Pubkey::new_unique(); - let mut system_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let recipient_key = Pubkey::new_unique(); - let mut recipient_account = SolanaAccount::default(); - let mut mock_incinerator_account = SolanaAccount::default(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - - // create new mint - do_process_instruction( - initialize_mint2(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account], - ) - .unwrap(); - - // create account - do_process_instruction( - initialize_account3(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account], - ) - .unwrap(); - - // create incinerator- and system-owned accounts - do_process_instruction( - initialize_account3( - &program_id, - &incinerator_account_key, - &mint_key, - &solana_program::incinerator::id(), - ) - .unwrap(), - vec![&mut incinerator_account, &mut mint_account], - ) - .unwrap(); - do_process_instruction( - initialize_account3( - &program_id, - &system_account_key, - &mint_key, - &solana_program::system_program::id(), - ) - .unwrap(), - vec![&mut system_account, &mut mint_account], - ) - .unwrap(); - - // mint to account - do_process_instruction( - mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - .unwrap(); - - // transfer half to incinerator, half to system program - do_process_instruction( - transfer( - &program_id, - &account_key, - &incinerator_account_key, - &owner_key, - &[], - 500, - ) - .unwrap(), - vec![ - &mut account_account, - &mut incinerator_account, - &mut owner_account, - ], - ) - .unwrap(); - do_process_instruction( - transfer( - &program_id, - &account_key, - &system_account_key, - &owner_key, - &[], - 500, - ) - .unwrap(), - vec![ - &mut account_account, - &mut system_account, - &mut owner_account, - ], - ) - .unwrap(); - - // close with balance fails - assert_eq!( - Err(TokenError::NonNativeHasBalance.into()), - do_process_instruction( - close_account( - &program_id, - &incinerator_account_key, - &solana_program::incinerator::id(), - &owner_key, - &[] - ) - .unwrap(), - vec![ - &mut incinerator_account, - &mut mock_incinerator_account, - &mut owner_account, - ], - ) - ); - assert_eq!( - Err(TokenError::NonNativeHasBalance.into()), - do_process_instruction( - close_account( - &program_id, - &system_account_key, - &solana_program::incinerator::id(), - &owner_key, - &[] - ) - .unwrap(), - vec![ - &mut system_account, - &mut mock_incinerator_account, - &mut owner_account, - ], - ) - ); - - // anyone can burn - do_process_instruction( - burn( - &program_id, - &incinerator_account_key, - &mint_key, - &recipient_key, - &[], - 500, - ) - .unwrap(), - vec![ - &mut incinerator_account, - &mut mint_account, - &mut recipient_account, - ], - ) - .unwrap(); - do_process_instruction( - burn( - &program_id, - &system_account_key, - &mint_key, - &recipient_key, - &[], - 500, - ) - .unwrap(), - vec![ - &mut system_account, - &mut mint_account, - &mut recipient_account, - ], - ) - .unwrap(); - - // closing fails if destination is not the incinerator - assert_eq!( - Err(ProgramError::InvalidAccountData), - do_process_instruction( - close_account( - &program_id, - &incinerator_account_key, - &recipient_key, - &owner_key, - &[] - ) - .unwrap(), - vec![ - &mut incinerator_account, - &mut recipient_account, - &mut owner_account, - ], - ) - ); - assert_eq!( - Err(ProgramError::InvalidAccountData), - do_process_instruction( - close_account( - &program_id, - &system_account_key, - &recipient_key, - &owner_key, - &[] - ) - .unwrap(), - vec![ - &mut system_account, - &mut recipient_account, - &mut owner_account, - ], - ) - ); - - // closing succeeds with incinerator recipient - do_process_instruction( - close_account( - &program_id, - &incinerator_account_key, - &solana_program::incinerator::id(), - &owner_key, - &[], - ) - .unwrap(), - vec![ - &mut incinerator_account, - &mut mock_incinerator_account, - &mut owner_account, - ], - ) - .unwrap(); - - do_process_instruction( - close_account( - &program_id, - &system_account_key, - &solana_program::incinerator::id(), - &owner_key, - &[], - ) - .unwrap(), - vec![ - &mut system_account, - &mut mock_incinerator_account, - &mut owner_account, - ], - ) - .unwrap(); - } - - #[test] - fn test_multisig() { - let program_id = crate::id(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let account_key = Pubkey::new_unique(); - let mut account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account2_key = Pubkey::new_unique(); - let mut account2_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let multisig_key = Pubkey::new_unique(); - let mut multisig_account = SolanaAccount::new(42, Multisig::get_packed_len(), &program_id); - let multisig_delegate_key = Pubkey::new_unique(); - let mut multisig_delegate_account = SolanaAccount::new( - multisig_minimum_balance(), - Multisig::get_packed_len(), - &program_id, - ); - let signer_keys = vec![Pubkey::new_unique(); MAX_SIGNERS]; - let signer_key_refs: Vec<&Pubkey> = signer_keys.iter().collect(); - let mut signer_accounts = vec![SolanaAccount::new(0, 0, &program_id); MAX_SIGNERS]; - let mut rent_sysvar = rent_sysvar(); - - // multisig is not rent exempt - let account_info_iter = &mut signer_accounts.iter_mut(); - assert_eq!( - Err(TokenError::NotRentExempt.into()), - do_process_instruction( - initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), - vec![ - &mut multisig_account, - &mut rent_sysvar, - account_info_iter.next().unwrap(), - ], - ) - ); - - multisig_account.lamports = multisig_minimum_balance(); - let mut multisig_account2 = multisig_account.clone(); - - // single signer - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), - vec![ - &mut multisig_account, - &mut rent_sysvar, - account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // single signer using `initialize_multisig2` - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - initialize_multisig2(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), - vec![&mut multisig_account2, account_info_iter.next().unwrap()], - ) - .unwrap(); - - // multiple signer - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - initialize_multisig( - &program_id, - &multisig_delegate_key, - &signer_key_refs, - MAX_SIGNERS as u8, - ) - .unwrap(), - vec![ - &mut multisig_delegate_account, - &mut rent_sysvar, - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // create new mint with multisig owner - do_process_instruction( - initialize_mint(&program_id, &mint_key, &multisig_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - // create account with multisig owner - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &multisig_key).unwrap(), - vec![ - &mut account, - &mut mint_account, - &mut multisig_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create another account with multisig owner - do_process_instruction( - initialize_account( - &program_id, - &account2_key, - &mint_key, - &multisig_delegate_key, - ) - .unwrap(), - vec![ - &mut account2_account, - &mut mint_account, - &mut multisig_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // mint to account - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account_key, - &multisig_key, - &[&signer_keys[0]], - 1000, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account, - &mut multisig_account, - account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // approve - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - approve( - &program_id, - &account_key, - &multisig_delegate_key, - &multisig_key, - &[&signer_keys[0]], - 100, - ) - .unwrap(), - vec![ - &mut account, - &mut multisig_delegate_account, - &mut multisig_account, - account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // transfer - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &multisig_key, - &[&signer_keys[0]], - 42, - ) - .unwrap(), - vec![ - &mut account, - &mut account2_account, - &mut multisig_account, - account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // transfer via delegate - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &multisig_delegate_key, - &signer_key_refs, - 42, - ) - .unwrap(), - vec![ - &mut account, - &mut account2_account, - &mut multisig_delegate_account, - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // mint to - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account2_key, - &multisig_key, - &[&signer_keys[0]], - 42, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account2_account, - &mut multisig_account, - account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // burn - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - burn( - &program_id, - &account_key, - &mint_key, - &multisig_key, - &[&signer_keys[0]], - 42, - ) - .unwrap(), - vec![ - &mut account, - &mut mint_account, - &mut multisig_account, - account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // burn via delegate - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - burn( - &program_id, - &account_key, - &mint_key, - &multisig_delegate_key, - &signer_key_refs, - 42, - ) - .unwrap(), - vec![ - &mut account, - &mut mint_account, - &mut multisig_delegate_account, - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // freeze account - let account3_key = Pubkey::new_unique(); - let mut account3_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let mint2_key = Pubkey::new_unique(); - let mut mint2_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - do_process_instruction( - initialize_mint( - &program_id, - &mint2_key, - &multisig_key, - Some(&multisig_key), - 2, - ) - .unwrap(), - vec![&mut mint2_account, &mut rent_sysvar], - ) - .unwrap(); - do_process_instruction( - initialize_account(&program_id, &account3_key, &mint2_key, &owner_key).unwrap(), - vec![ - &mut account3_account, - &mut mint2_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - mint_to( - &program_id, - &mint2_key, - &account3_key, - &multisig_key, - &[&signer_keys[0]], - 1000, - ) - .unwrap(), - vec![ - &mut mint2_account, - &mut account3_account, - &mut multisig_account, - account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - freeze_account( - &program_id, - &account3_key, - &mint2_key, - &multisig_key, - &[&signer_keys[0]], - ) - .unwrap(), - vec![ - &mut account3_account, - &mut mint2_account, - &mut multisig_account, - account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // do SetAuthority on mint - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - set_authority( - &program_id, - &mint_key, - Some(&owner_key), - AuthorityType::MintTokens, - &multisig_key, - &[&signer_keys[0]], - ) - .unwrap(), - vec![ - &mut mint_account, - &mut multisig_account, - account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // do SetAuthority on account - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - set_authority( - &program_id, - &account_key, - Some(&owner_key), - AuthorityType::AccountOwner, - &multisig_key, - &[&signer_keys[0]], - ) - .unwrap(), - vec![ - &mut account, - &mut multisig_account, - account_info_iter.next().unwrap(), - ], - ) - .unwrap(); + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, + ]; + assert_eq!(packed, expect); + let unpacked = Multisig::unpack(&packed).unwrap(); + assert_eq!(unpacked, check); } #[test] @@ -5451,1576 +1342,4 @@ mod tests { ); } } - - #[test] - fn test_owner_close_account_dups() { - let program_id = crate::id(); - let owner_key = Pubkey::new_unique(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into(); - let rent_key = rent::id(); - let mut rent_sysvar = rent_sysvar(); - let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); - - // create mint - do_process_instruction_dups( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![mint_info.clone(), rent_info.clone()], - ) - .unwrap(); - - let to_close_key = Pubkey::new_unique(); - let mut to_close_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let to_close_account_info: AccountInfo = - (&to_close_key, true, &mut to_close_account).into(); - let destination_account_key = Pubkey::new_unique(); - let mut destination_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let destination_account_info: AccountInfo = - (&destination_account_key, true, &mut destination_account).into(); - // create account - do_process_instruction_dups( - initialize_account(&program_id, &to_close_key, &mint_key, &to_close_key).unwrap(), - vec![ - to_close_account_info.clone(), - mint_info.clone(), - to_close_account_info.clone(), - rent_info.clone(), - ], - ) - .unwrap(); - - // source-owner close - do_process_instruction_dups( - close_account( - &program_id, - &to_close_key, - &destination_account_key, - &to_close_key, - &[], - ) - .unwrap(), - vec![ - to_close_account_info.clone(), - destination_account_info.clone(), - to_close_account_info.clone(), - ], - ) - .unwrap(); - assert_eq!(*to_close_account_info.data.borrow(), &[0u8; Account::LEN]); - } - - #[test] - fn test_close_authority_close_account_dups() { - let program_id = crate::id(); - let owner_key = Pubkey::new_unique(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into(); - let rent_key = rent::id(); - let mut rent_sysvar = rent_sysvar(); - let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); - - // create mint - do_process_instruction_dups( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![mint_info.clone(), rent_info.clone()], - ) - .unwrap(); - - let to_close_key = Pubkey::new_unique(); - let mut to_close_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let to_close_account_info: AccountInfo = - (&to_close_key, true, &mut to_close_account).into(); - let destination_account_key = Pubkey::new_unique(); - let mut destination_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let destination_account_info: AccountInfo = - (&destination_account_key, true, &mut destination_account).into(); - // create account - do_process_instruction_dups( - initialize_account(&program_id, &to_close_key, &mint_key, &to_close_key).unwrap(), - vec![ - to_close_account_info.clone(), - mint_info.clone(), - to_close_account_info.clone(), - rent_info.clone(), - ], - ) - .unwrap(); - let mut account = Account::unpack_unchecked(&to_close_account_info.data.borrow()).unwrap(); - account.close_authority = COption::Some(to_close_key); - account.owner = owner_key; - Account::pack(account, &mut to_close_account_info.data.borrow_mut()).unwrap(); - do_process_instruction_dups( - close_account( - &program_id, - &to_close_key, - &destination_account_key, - &to_close_key, - &[], - ) - .unwrap(), - vec![ - to_close_account_info.clone(), - destination_account_info.clone(), - to_close_account_info.clone(), - ], - ) - .unwrap(); - assert_eq!(*to_close_account_info.data.borrow(), &[0u8; Account::LEN]); - } - - #[test] - fn test_close_account() { - let program_id = crate::id(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account2_key = Pubkey::new_unique(); - let mut account2_account = SolanaAccount::new( - account_minimum_balance() + 42, - Account::get_packed_len(), - &program_id, - ); - let account3_key = Pubkey::new_unique(); - let mut account3_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = Pubkey::new_unique(); - let mut owner2_account = SolanaAccount::default(); - let mut rent_sysvar = rent_sysvar(); - - // uninitialized - assert_eq!( - Err(ProgramError::UninitializedAccount), - do_process_instruction( - close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut account3_account, - &mut owner2_account, - ], - ) - ); - - // initialize and mint to non-native account - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - do_process_instruction( - mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(), - vec![ - &mut mint_account, - &mut account_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 42); - - // initialize native account - do_process_instruction( - initialize_account( - &program_id, - &account2_key, - &crate::native_mint::id(), - &owner_key, - ) - .unwrap(), - vec![ - &mut account2_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - let account = Account::unpack_unchecked(&account2_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account.amount, 42); - - // close non-native account with balance - assert_eq!( - Err(TokenError::NonNativeHasBalance.into()), - do_process_instruction( - close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut account3_account, - &mut owner_account, - ], - ) - ); - assert_eq!(account_account.lamports, account_minimum_balance()); - - // empty account - do_process_instruction( - burn(&program_id, &account_key, &mint_key, &owner_key, &[], 42).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // wrong owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut account3_account, - &mut owner2_account, - ], - ) - ); - - // close account - do_process_instruction( - close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut account3_account, - &mut owner_account, - ], - ) - .unwrap(); - assert_eq!(account_account.lamports, 0); - assert_eq!(account3_account.lamports, 2 * account_minimum_balance()); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 0); - - // fund and initialize new non-native account to test close authority - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let owner2_key = Pubkey::new_unique(); - let mut owner2_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - account_account.lamports = 2; - - do_process_instruction( - set_authority( - &program_id, - &account_key, - Some(&owner2_key), - AuthorityType::CloseAccount, - &owner_key, - &[], - ) - .unwrap(), - vec![&mut account_account, &mut owner_account], - ) - .unwrap(); - - // account owner cannot authorize close if close_authority is set - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut account3_account, - &mut owner_account, - ], - ) - ); - - // close non-native account with close_authority - do_process_instruction( - close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut account3_account, - &mut owner2_account, - ], - ) - .unwrap(); - assert_eq!(account_account.lamports, 0); - assert_eq!(account3_account.lamports, 2 * account_minimum_balance() + 2); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 0); - - // close native account - do_process_instruction( - close_account(&program_id, &account2_key, &account3_key, &owner_key, &[]).unwrap(), - vec![ - &mut account2_account, - &mut account3_account, - &mut owner_account, - ], - ) - .unwrap(); - assert_eq!(account2_account.data, [0u8; Account::LEN]); - assert_eq!( - account3_account.lamports, - 3 * account_minimum_balance() + 2 + 42 - ); - } - - #[test] - fn test_native_token() { - let program_id = crate::id(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new( - account_minimum_balance() + 40, - Account::get_packed_len(), - &program_id, - ); - let account2_key = Pubkey::new_unique(); - let mut account2_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account3_key = Pubkey::new_unique(); - let mut account3_account = SolanaAccount::new(account_minimum_balance(), 0, &program_id); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = Pubkey::new_unique(); - let mut owner2_account = SolanaAccount::default(); - let owner3_key = Pubkey::new_unique(); - let mut rent_sysvar = rent_sysvar(); - - // initialize native account - do_process_instruction( - initialize_account( - &program_id, - &account_key, - &crate::native_mint::id(), - &owner_key, - ) - .unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account.amount, 40); - - // initialize native account - do_process_instruction( - initialize_account( - &program_id, - &account2_key, - &crate::native_mint::id(), - &owner_key, - ) - .unwrap(), - vec![ - &mut account2_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - let account = Account::unpack_unchecked(&account2_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account.amount, 0); - - // mint_to unsupported - assert_eq!( - Err(TokenError::NativeNotSupported.into()), - do_process_instruction( - mint_to( - &program_id, - &crate::native_mint::id(), - &account_key, - &owner_key, - &[], - 42 - ) - .unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - ); - - // burn unsupported - let bogus_mint_key = Pubkey::new_unique(); - let mut bogus_mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - do_process_instruction( - initialize_mint(&program_id, &bogus_mint_key, &owner_key, None, 2).unwrap(), - vec![&mut bogus_mint_account, &mut rent_sysvar], - ) - .unwrap(); - - assert_eq!( - Err(TokenError::NativeNotSupported.into()), - do_process_instruction( - burn( - &program_id, - &account_key, - &bogus_mint_key, - &owner_key, - &[], - 42 - ) - .unwrap(), - vec![ - &mut account_account, - &mut bogus_mint_account, - &mut owner_account - ], - ) - ); - - // ensure can't transfer below rent-exempt reserve - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &owner_key, - &[], - 50, - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - ); - - // transfer between native accounts - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &owner_key, - &[], - 40, - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - .unwrap(); - assert_eq!(account_account.lamports, account_minimum_balance()); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account.amount, 0); - assert_eq!(account2_account.lamports, account_minimum_balance() + 40); - let account = Account::unpack_unchecked(&account2_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account.amount, 40); - - // set close authority - do_process_instruction( - set_authority( - &program_id, - &account_key, - Some(&owner3_key), - AuthorityType::CloseAccount, - &owner_key, - &[], - ) - .unwrap(), - vec![&mut account_account, &mut owner_account], - ) - .unwrap(); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.close_authority, COption::Some(owner3_key)); - - // set new account owner - do_process_instruction( - set_authority( - &program_id, - &account_key, - Some(&owner2_key), - AuthorityType::AccountOwner, - &owner_key, - &[], - ) - .unwrap(), - vec![&mut account_account, &mut owner_account], - ) - .unwrap(); - - // close authority cleared - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.close_authority, COption::None); - - // close native account - do_process_instruction( - close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut account3_account, - &mut owner2_account, - ], - ) - .unwrap(); - assert_eq!(account_account.lamports, 0); - assert_eq!(account3_account.lamports, 2 * account_minimum_balance()); - assert_eq!(account_account.data, [0u8; Account::LEN]); - } - - #[test] - fn test_overflow() { - let program_id = crate::id(); - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account2_key = Pubkey::new_unique(); - let mut account2_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = Pubkey::new_unique(); - let mut owner2_account = SolanaAccount::default(); - let mint_owner_key = Pubkey::new_unique(); - let mut mint_owner_account = SolanaAccount::default(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mut rent_sysvar = rent_sysvar(); - - // create new mint with owner - do_process_instruction( - initialize_mint(&program_id, &mint_key, &mint_owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - // create an account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner2_key).unwrap(), - vec![ - &mut account2_account, - &mut mint_account, - &mut owner2_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // mint the max to an account - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account_key, - &mint_owner_key, - &[], - u64::MAX, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account_account, - &mut mint_owner_account, - ], - ) - .unwrap(); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, u64::MAX); - - // attempt to mint one more to account - assert_eq!( - Err(TokenError::Overflow.into()), - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account_key, - &mint_owner_key, - &[], - 1, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account_account, - &mut mint_owner_account, - ], - ) - ); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, u64::MAX); - - // attempt to mint one more to the other account - assert_eq!( - Err(TokenError::Overflow.into()), - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account2_key, - &mint_owner_key, - &[], - 1, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account2_account, - &mut mint_owner_account, - ], - ) - ); - - // burn some of the supply - do_process_instruction( - burn(&program_id, &account_key, &mint_key, &owner_key, &[], 100).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, u64::MAX - 100); - - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account_key, - &mint_owner_key, - &[], - 100, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account_account, - &mut mint_owner_account, - ], - ) - .unwrap(); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, u64::MAX); - - // manipulate account balance to attempt overflow transfer - let mut account = Account::unpack_unchecked(&account2_account.data).unwrap(); - account.amount = 1; - Account::pack(account, &mut account2_account.data).unwrap(); - - assert_eq!( - Err(TokenError::Overflow.into()), - do_process_instruction( - transfer( - &program_id, - &account2_key, - &account_key, - &owner2_key, - &[], - 1, - ) - .unwrap(), - vec![ - &mut account2_account, - &mut account_account, - &mut owner2_account, - ], - ) - ); - } - - #[test] - fn test_frozen() { - let program_id = crate::id(); - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account2_key = Pubkey::new_unique(); - let mut account2_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mut rent_sysvar = rent_sysvar(); - - // create new mint and fund first account - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account2_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // fund first account - do_process_instruction( - mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - .unwrap(); - - // no transfer if either account is frozen - let mut account = Account::unpack_unchecked(&account2_account.data).unwrap(); - account.state = AccountState::Frozen; - Account::pack(account, &mut account2_account.data).unwrap(); - assert_eq!( - Err(TokenError::AccountFrozen.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &owner_key, - &[], - 500, - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - ); - - let mut account = Account::unpack_unchecked(&account_account.data).unwrap(); - account.state = AccountState::Initialized; - Account::pack(account, &mut account_account.data).unwrap(); - let mut account = Account::unpack_unchecked(&account2_account.data).unwrap(); - account.state = AccountState::Frozen; - Account::pack(account, &mut account2_account.data).unwrap(); - assert_eq!( - Err(TokenError::AccountFrozen.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &owner_key, - &[], - 500, - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - ); - - // no approve if account is frozen - let mut account = Account::unpack_unchecked(&account_account.data).unwrap(); - account.state = AccountState::Frozen; - Account::pack(account, &mut account_account.data).unwrap(); - let delegate_key = Pubkey::new_unique(); - let mut delegate_account = SolanaAccount::default(); - assert_eq!( - Err(TokenError::AccountFrozen.into()), - do_process_instruction( - approve( - &program_id, - &account_key, - &delegate_key, - &owner_key, - &[], - 100 - ) - .unwrap(), - vec![ - &mut account_account, - &mut delegate_account, - &mut owner_account, - ], - ) - ); - - // no revoke if account is frozen - let mut account = Account::unpack_unchecked(&account_account.data).unwrap(); - account.delegate = COption::Some(delegate_key); - account.delegated_amount = 100; - Account::pack(account, &mut account_account.data).unwrap(); - assert_eq!( - Err(TokenError::AccountFrozen.into()), - do_process_instruction( - revoke(&program_id, &account_key, &owner_key, &[]).unwrap(), - vec![&mut account_account, &mut owner_account], - ) - ); - - // no set authority if account is frozen - let new_owner_key = Pubkey::new_unique(); - assert_eq!( - Err(TokenError::AccountFrozen.into()), - do_process_instruction( - set_authority( - &program_id, - &account_key, - Some(&new_owner_key), - AuthorityType::AccountOwner, - &owner_key, - &[] - ) - .unwrap(), - vec![&mut account_account, &mut owner_account,], - ) - ); - - // no mint_to if destination account is frozen - assert_eq!( - Err(TokenError::AccountFrozen.into()), - do_process_instruction( - mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 100).unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account,], - ) - ); - - // no burn if account is frozen - assert_eq!( - Err(TokenError::AccountFrozen.into()), - do_process_instruction( - burn(&program_id, &account_key, &mint_key, &owner_key, &[], 100).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - ); - } - - #[test] - fn test_freeze_thaw_dups() { - let program_id = crate::id(); - let account1_key = Pubkey::new_unique(); - let mut account1_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); - let owner_key = Pubkey::new_unique(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into(); - let rent_key = rent::id(); - let mut rent_sysvar = rent_sysvar(); - let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); - - // create mint - do_process_instruction_dups( - initialize_mint(&program_id, &mint_key, &owner_key, Some(&account1_key), 2).unwrap(), - vec![mint_info.clone(), rent_info.clone()], - ) - .unwrap(); - - // create account - do_process_instruction_dups( - initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), - vec![ - account1_info.clone(), - mint_info.clone(), - account1_info.clone(), - rent_info.clone(), - ], - ) - .unwrap(); - - // freeze where mint freeze_authority is account - do_process_instruction_dups( - freeze_account(&program_id, &account1_key, &mint_key, &account1_key, &[]).unwrap(), - vec![ - account1_info.clone(), - mint_info.clone(), - account1_info.clone(), - ], - ) - .unwrap(); - - // thaw where mint freeze_authority is account - let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); - account.state = AccountState::Frozen; - Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); - do_process_instruction_dups( - thaw_account(&program_id, &account1_key, &mint_key, &account1_key, &[]).unwrap(), - vec![ - account1_info.clone(), - mint_info.clone(), - account1_info.clone(), - ], - ) - .unwrap(); - } - - #[test] - fn test_freeze_account() { - let program_id = crate::id(); - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account_owner_key = Pubkey::new_unique(); - let mut account_owner_account = SolanaAccount::default(); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = Pubkey::new_unique(); - let mut owner2_account = SolanaAccount::default(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mut rent_sysvar = rent_sysvar(); - - // create new mint with owner different from account owner - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &account_owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut account_owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // mint to account - do_process_instruction( - mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - .unwrap(); - - // mint cannot freeze - assert_eq!( - Err(TokenError::MintCannotFreeze.into()), - do_process_instruction( - freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - ); - - // missing freeze_authority - let mut mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); - mint.freeze_authority = COption::Some(owner_key); - Mint::pack(mint, &mut mint_account.data).unwrap(); - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - freeze_account(&program_id, &account_key, &mint_key, &owner2_key, &[]).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner2_account], - ) - ); - - // check explicit thaw - assert_eq!( - Err(TokenError::InvalidState.into()), - do_process_instruction( - thaw_account(&program_id, &account_key, &mint_key, &owner2_key, &[]).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner2_account], - ) - ); - - // freeze - do_process_instruction( - freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.state, AccountState::Frozen); - - // check explicit freeze - assert_eq!( - Err(TokenError::InvalidState.into()), - do_process_instruction( - freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - ); - - // check thaw authority - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - thaw_account(&program_id, &account_key, &mint_key, &owner2_key, &[]).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner2_account], - ) - ); - - // thaw - do_process_instruction( - thaw_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.state, AccountState::Initialized); - } - - #[test] - fn test_initialize_account2_and_3() { - let program_id = crate::id(); - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let mut account2_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let mut account3_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mut rent_sysvar = rent_sysvar(); - - // create mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - do_process_instruction( - initialize_account2(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account2_account, &mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - assert_eq!(account_account, account2_account); - - do_process_instruction( - initialize_account3(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account3_account, &mut mint_account], - ) - .unwrap(); - - assert_eq!(account_account, account3_account); - } - - #[test] - fn test_sync_native() { - let program_id = crate::id(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let native_account_key = Pubkey::new_unique(); - let lamports = 40; - let mut native_account = SolanaAccount::new( - account_minimum_balance() + lamports, - Account::get_packed_len(), - &program_id, - ); - let non_native_account_key = Pubkey::new_unique(); - let mut non_native_account = SolanaAccount::new( - account_minimum_balance() + 50, - Account::get_packed_len(), - &program_id, - ); - - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let mut rent_sysvar = rent_sysvar(); - - // initialize non-native mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - // initialize non-native account - do_process_instruction( - initialize_account(&program_id, &non_native_account_key, &mint_key, &owner_key) - .unwrap(), - vec![ - &mut non_native_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - let account = Account::unpack_unchecked(&non_native_account.data).unwrap(); - assert!(!account.is_native()); - assert_eq!(account.amount, 0); - - // fail sync non-native - assert_eq!( - Err(TokenError::NonNativeNotSupported.into()), - do_process_instruction( - sync_native(&program_id, &non_native_account_key,).unwrap(), - vec![&mut non_native_account], - ) - ); - - // fail sync uninitialized - assert_eq!( - Err(ProgramError::UninitializedAccount), - do_process_instruction( - sync_native(&program_id, &native_account_key,).unwrap(), - vec![&mut native_account], - ) - ); - - // wrap native account - do_process_instruction( - initialize_account( - &program_id, - &native_account_key, - &crate::native_mint::id(), - &owner_key, - ) - .unwrap(), - vec![ - &mut native_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // fail sync, not owned by program - let not_program_id = Pubkey::new_unique(); - native_account.owner = not_program_id; - assert_eq!( - Err(ProgramError::IncorrectProgramId), - do_process_instruction( - sync_native(&program_id, &native_account_key,).unwrap(), - vec![&mut native_account], - ) - ); - native_account.owner = program_id; - - let account = Account::unpack_unchecked(&native_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account.amount, lamports); - - // sync, no change - do_process_instruction( - sync_native(&program_id, &native_account_key).unwrap(), - vec![&mut native_account], - ) - .unwrap(); - let account = Account::unpack_unchecked(&native_account.data).unwrap(); - assert_eq!(account.amount, lamports); - - // transfer sol - let new_lamports = lamports + 50; - native_account.lamports = account_minimum_balance() + new_lamports; - - // success sync - do_process_instruction( - sync_native(&program_id, &native_account_key).unwrap(), - vec![&mut native_account], - ) - .unwrap(); - let account = Account::unpack_unchecked(&native_account.data).unwrap(); - assert_eq!(account.amount, new_lamports); - - // reduce sol - native_account.lamports -= 1; - - // fail sync - assert_eq!( - Err(TokenError::InvalidState.into()), - do_process_instruction( - sync_native(&program_id, &native_account_key,).unwrap(), - vec![&mut native_account], - ) - ); - } - - #[test] - #[serial] - fn test_get_account_data_size() { - // see integration tests for return-data validity - let program_id = crate::id(); - let owner_key = Pubkey::new_unique(); - let mut rent_sysvar = rent_sysvar(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint_key = Pubkey::new_unique(); - // fail if an invalid mint is passed in - assert_eq!( - Err(TokenError::InvalidMint.into()), - do_process_instruction( - get_account_data_size(&program_id, &mint_key).unwrap(), - vec![&mut mint_account], - ) - ); - - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - set_expected_data(Account::LEN.to_le_bytes().to_vec()); - do_process_instruction( - get_account_data_size(&program_id, &mint_key).unwrap(), - vec![&mut mint_account], - ) - .unwrap(); - } - - #[test] - fn test_initialize_immutable_owner() { - let program_id = crate::id(); - let account_key = Pubkey::new_unique(); - let mut account_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let owner_key = Pubkey::new_unique(); - let mut owner_account = SolanaAccount::default(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mut rent_sysvar = rent_sysvar(); - - // create mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - // success initialize immutable - do_process_instruction( - initialize_immutable_owner(&program_id, &account_key).unwrap(), - vec![&mut account_account], - ) - .unwrap(); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut mint_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - - // fail post-init - assert_eq!( - Err(TokenError::AlreadyInUse.into()), - do_process_instruction( - initialize_immutable_owner(&program_id, &account_key).unwrap(), - vec![&mut account_account], - ) - ); - } - - #[test] - #[serial] - fn test_amount_to_ui_amount() { - let program_id = crate::id(); - let owner_key = Pubkey::new_unique(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mut rent_sysvar = rent_sysvar(); - - // fail if an invalid mint is passed in - assert_eq!( - Err(TokenError::InvalidMint.into()), - do_process_instruction( - amount_to_ui_amount(&program_id, &mint_key, 110).unwrap(), - vec![&mut mint_account], - ) - ); - - // create mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - set_expected_data("0.23".as_bytes().to_vec()); - do_process_instruction( - amount_to_ui_amount(&program_id, &mint_key, 23).unwrap(), - vec![&mut mint_account], - ) - .unwrap(); - - set_expected_data("1.1".as_bytes().to_vec()); - do_process_instruction( - amount_to_ui_amount(&program_id, &mint_key, 110).unwrap(), - vec![&mut mint_account], - ) - .unwrap(); - - set_expected_data("42".as_bytes().to_vec()); - do_process_instruction( - amount_to_ui_amount(&program_id, &mint_key, 4200).unwrap(), - vec![&mut mint_account], - ) - .unwrap(); - - set_expected_data("0".as_bytes().to_vec()); - do_process_instruction( - amount_to_ui_amount(&program_id, &mint_key, 0).unwrap(), - vec![&mut mint_account], - ) - .unwrap(); - } - - #[test] - #[serial] - fn test_ui_amount_to_amount() { - let program_id = crate::id(); - let owner_key = Pubkey::new_unique(); - let mint_key = Pubkey::new_unique(); - let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mut rent_sysvar = rent_sysvar(); - - // fail if an invalid mint is passed in - assert_eq!( - Err(TokenError::InvalidMint.into()), - do_process_instruction( - ui_amount_to_amount(&program_id, &mint_key, "1.1").unwrap(), - vec![&mut mint_account], - ) - ); - - // create mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - - set_expected_data(23u64.to_le_bytes().to_vec()); - do_process_instruction( - ui_amount_to_amount(&program_id, &mint_key, "0.23").unwrap(), - vec![&mut mint_account], - ) - .unwrap(); - - set_expected_data(20u64.to_le_bytes().to_vec()); - do_process_instruction( - ui_amount_to_amount(&program_id, &mint_key, "0.20").unwrap(), - vec![&mut mint_account], - ) - .unwrap(); - - set_expected_data(20u64.to_le_bytes().to_vec()); - do_process_instruction( - ui_amount_to_amount(&program_id, &mint_key, "0.2000").unwrap(), - vec![&mut mint_account], - ) - .unwrap(); - - set_expected_data(20u64.to_le_bytes().to_vec()); - do_process_instruction( - ui_amount_to_amount(&program_id, &mint_key, ".20").unwrap(), - vec![&mut mint_account], - ) - .unwrap(); - - set_expected_data(110u64.to_le_bytes().to_vec()); - do_process_instruction( - ui_amount_to_amount(&program_id, &mint_key, "1.1").unwrap(), - vec![&mut mint_account], - ) - .unwrap(); - - set_expected_data(110u64.to_le_bytes().to_vec()); - do_process_instruction( - ui_amount_to_amount(&program_id, &mint_key, "1.10").unwrap(), - vec![&mut mint_account], - ) - .unwrap(); - - set_expected_data(4200u64.to_le_bytes().to_vec()); - do_process_instruction( - ui_amount_to_amount(&program_id, &mint_key, "42").unwrap(), - vec![&mut mint_account], - ) - .unwrap(); - - set_expected_data(4200u64.to_le_bytes().to_vec()); - do_process_instruction( - ui_amount_to_amount(&program_id, &mint_key, "42.").unwrap(), - vec![&mut mint_account], - ) - .unwrap(); - - set_expected_data(0u64.to_le_bytes().to_vec()); - do_process_instruction( - ui_amount_to_amount(&program_id, &mint_key, "0").unwrap(), - vec![&mut mint_account], - ) - .unwrap(); - - // fail if invalid ui_amount passed in - assert_eq!( - Err(ProgramError::InvalidArgument), - do_process_instruction( - ui_amount_to_amount(&program_id, &mint_key, "").unwrap(), - vec![&mut mint_account], - ) - ); - assert_eq!( - Err(ProgramError::InvalidArgument), - do_process_instruction( - ui_amount_to_amount(&program_id, &mint_key, ".").unwrap(), - vec![&mut mint_account], - ) - ); - assert_eq!( - Err(ProgramError::InvalidArgument), - do_process_instruction( - ui_amount_to_amount(&program_id, &mint_key, "0.111").unwrap(), - vec![&mut mint_account], - ) - ); - assert_eq!( - Err(ProgramError::InvalidArgument), - do_process_instruction( - ui_amount_to_amount(&program_id, &mint_key, "0.t").unwrap(), - vec![&mut mint_account], - ) - ); - } } diff --git a/program/tests/processor.rs b/program/tests/processor.rs new file mode 100644 index 0000000..2d17315 --- /dev/null +++ b/program/tests/processor.rs @@ -0,0 +1,5705 @@ +#![cfg(feature = "test-sbf")] + +use { + serial_test::serial, + solana_program::{ + account_info::IntoAccountInfo, instruction::Instruction, program_error, sysvar::rent, + }, + solana_sdk::{ + account::{ + create_account_for_test, create_is_signer_account_infos, Account as SolanaAccount, + }, + account_info::AccountInfo, + entrypoint::ProgramResult, + program_error::ProgramError, + program_option::COption, + program_pack::Pack, + pubkey::Pubkey, + rent::Rent, + }, + spl_token::{ + error::TokenError, + instruction::{ + amount_to_ui_amount, approve, approve_checked, burn, burn_checked, close_account, + freeze_account, get_account_data_size, initialize_account, initialize_account2, + initialize_account3, initialize_immutable_owner, initialize_mint, initialize_mint2, + initialize_multisig, initialize_multisig2, mint_to, mint_to_checked, revoke, + set_authority, sync_native, thaw_account, transfer, transfer_checked, + ui_amount_to_amount, AuthorityType, MAX_SIGNERS, + }, + processor::Processor, + state::{Account, AccountState, Mint, Multisig}, + }, + std::sync::{Arc, RwLock}, +}; + +lazy_static::lazy_static! { + static ref EXPECTED_DATA: Arc>> = Arc::new(RwLock::new(Vec::new())); +} + +fn set_expected_data(expected_data: Vec) { + *EXPECTED_DATA.write().unwrap() = expected_data; +} + +struct SyscallStubs {} +impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { + fn sol_log(&self, _message: &str) {} + + fn sol_invoke_signed( + &self, + _instruction: &Instruction, + _account_infos: &[AccountInfo], + _signers_seeds: &[&[&[u8]]], + ) -> ProgramResult { + Err(ProgramError::Custom(42)) // Not supported + } + + fn sol_get_clock_sysvar(&self, _var_addr: *mut u8) -> u64 { + program_error::UNSUPPORTED_SYSVAR + } + + fn sol_get_epoch_schedule_sysvar(&self, _var_addr: *mut u8) -> u64 { + program_error::UNSUPPORTED_SYSVAR + } + + #[allow(deprecated)] + fn sol_get_fees_sysvar(&self, _var_addr: *mut u8) -> u64 { + program_error::UNSUPPORTED_SYSVAR + } + + fn sol_get_rent_sysvar(&self, var_addr: *mut u8) -> u64 { + unsafe { + *(var_addr as *mut _ as *mut Rent) = Rent::default(); + } + solana_program::entrypoint::SUCCESS + } + + fn sol_set_return_data(&self, data: &[u8]) { + assert_eq!(&*EXPECTED_DATA.write().unwrap(), data) + } +} + +fn do_process_instruction( + instruction: Instruction, + accounts: Vec<&mut SolanaAccount>, +) -> ProgramResult { + { + use std::sync::Once; + static ONCE: Once = Once::new(); + + ONCE.call_once(|| { + solana_sdk::program_stubs::set_syscall_stubs(Box::new(SyscallStubs {})); + }); + } + + let mut meta = instruction + .accounts + .iter() + .zip(accounts) + .map(|(account_meta, account)| (&account_meta.pubkey, account_meta.is_signer, account)) + .collect::>(); + + let account_infos = create_is_signer_account_infos(&mut meta); + Processor::process(&instruction.program_id, &account_infos, &instruction.data) +} + +fn do_process_instruction_dups( + instruction: Instruction, + account_infos: Vec, +) -> ProgramResult { + Processor::process(&instruction.program_id, &account_infos, &instruction.data) +} + +fn rent_sysvar() -> SolanaAccount { + create_account_for_test(&Rent::default()) +} + +fn mint_minimum_balance() -> u64 { + Rent::default().minimum_balance(Mint::get_packed_len()) +} + +fn account_minimum_balance() -> u64 { + Rent::default().minimum_balance(Account::get_packed_len()) +} + +fn multisig_minimum_balance() -> u64 { + Rent::default().minimum_balance(Multisig::get_packed_len()) +} + +#[test] +fn test_initialize_mint() { + let program_id = spl_token::id(); + let owner_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = SolanaAccount::new(42, Mint::get_packed_len(), &program_id); + let mint2_key = Pubkey::new_unique(); + let mut mint2_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // mint is not rent exempt + assert_eq!( + Err(TokenError::NotRentExempt.into()), + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar] + ) + ); + + mint_account.lamports = mint_minimum_balance(); + + // create new mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // create twice + assert_eq!( + Err(TokenError::AlreadyInUse.into()), + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2,).unwrap(), + vec![&mut mint_account, &mut rent_sysvar] + ) + ); + + // create another mint that can freeze + do_process_instruction( + initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(), + vec![&mut mint2_account, &mut rent_sysvar], + ) + .unwrap(); + let mint = Mint::unpack_unchecked(&mint2_account.data).unwrap(); + assert_eq!(mint.freeze_authority, COption::Some(owner_key)); +} + +#[test] +fn test_initialize_mint2() { + let program_id = spl_token::id(); + let owner_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = SolanaAccount::new(42, Mint::get_packed_len(), &program_id); + let mint2_key = Pubkey::new_unique(); + let mut mint2_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + + // mint is not rent exempt + assert_eq!( + Err(TokenError::NotRentExempt.into()), + do_process_instruction( + initialize_mint2(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account] + ) + ); + + mint_account.lamports = mint_minimum_balance(); + + // create new mint + do_process_instruction( + initialize_mint2(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + // create twice + assert_eq!( + Err(TokenError::AlreadyInUse.into()), + do_process_instruction( + initialize_mint2(&program_id, &mint_key, &owner_key, None, 2,).unwrap(), + vec![&mut mint_account] + ) + ); + + // create another mint that can freeze + do_process_instruction( + initialize_mint2(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(), + vec![&mut mint2_account], + ) + .unwrap(); + let mint = Mint::unpack_unchecked(&mint2_account.data).unwrap(); + assert_eq!(mint.freeze_authority, COption::Some(owner_key)); +} + +#[test] +fn test_initialize_mint_account() { + let program_id = spl_token::id(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new(42, Account::get_packed_len(), &program_id); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // account is not rent exempt + assert_eq!( + Err(TokenError::NotRentExempt.into()), + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar + ], + ) + ); + + account_account.lamports = account_minimum_balance(); + + // mint is not valid (not initialized) + assert_eq!( + Err(TokenError::InvalidMint.into()), + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar + ], + ) + ); + + // create mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // mint not owned by program + let not_program_id = Pubkey::new_unique(); + mint_account.owner = not_program_id; + assert_eq!( + Err(ProgramError::IncorrectProgramId), + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar + ], + ) + ); + mint_account.owner = program_id; + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create twice + assert_eq!( + Err(TokenError::AlreadyInUse.into()), + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar + ], + ) + ); +} + +#[test] +fn test_transfer_dups() { + let program_id = spl_token::id(); + let account1_key = Pubkey::new_unique(); + let mut account1_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let mut account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); + let account2_key = Pubkey::new_unique(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let mut account2_info: AccountInfo = (&account2_key, false, &mut account2_account).into(); + let account3_key = Pubkey::new_unique(); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account3_info: AccountInfo = (&account3_key, false, &mut account3_account).into(); + let account4_key = Pubkey::new_unique(); + let mut account4_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account4_info: AccountInfo = (&account4_key, true, &mut account4_account).into(); + let multisig_key = Pubkey::new_unique(); + let mut multisig_account = SolanaAccount::new( + multisig_minimum_balance(), + Multisig::get_packed_len(), + &program_id, + ); + let multisig_info: AccountInfo = (&multisig_key, true, &mut multisig_account).into(); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into(); + let rent_key = rent::id(); + let mut rent_sysvar = rent_sysvar(); + let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); + + // create mint + do_process_instruction_dups( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![mint_info.clone(), rent_info.clone()], + ) + .unwrap(); + + // create account + do_process_instruction_dups( + initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // create another account + do_process_instruction_dups( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![ + account2_info.clone(), + mint_info.clone(), + owner_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // mint to account + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], + ) + .unwrap(); + + // source-owner transfer + do_process_instruction_dups( + transfer( + &program_id, + &account1_key, + &account2_key, + &account1_key, + &[], + 500, + ) + .unwrap(), + vec![ + account1_info.clone(), + account2_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // source-owner TransferChecked + do_process_instruction_dups( + transfer_checked( + &program_id, + &account1_key, + &mint_key, + &account2_key, + &account1_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account2_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // source-delegate transfer + 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( + transfer( + &program_id, + &account1_key, + &account2_key, + &account1_key, + &[], + 500, + ) + .unwrap(), + vec![ + account1_info.clone(), + account2_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // source-delegate TransferChecked + do_process_instruction_dups( + transfer_checked( + &program_id, + &account1_key, + &mint_key, + &account2_key, + &account1_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account2_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // test destination-owner transfer + do_process_instruction_dups( + initialize_account(&program_id, &account3_key, &mint_key, &account2_key).unwrap(), + vec![ + account3_info.clone(), + mint_info.clone(), + account2_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account3_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account3_info.clone(), owner_info.clone()], + ) + .unwrap(); + + account1_info.is_signer = false; + account2_info.is_signer = true; + do_process_instruction_dups( + transfer( + &program_id, + &account3_key, + &account2_key, + &account2_key, + &[], + 500, + ) + .unwrap(), + vec![ + account3_info.clone(), + account2_info.clone(), + account2_info.clone(), + ], + ) + .unwrap(); + + // destination-owner TransferChecked + do_process_instruction_dups( + transfer_checked( + &program_id, + &account3_key, + &mint_key, + &account2_key, + &account2_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![ + account3_info.clone(), + mint_info.clone(), + account2_info.clone(), + account2_info.clone(), + ], + ) + .unwrap(); + + // test source-multisig signer + do_process_instruction_dups( + initialize_multisig(&program_id, &multisig_key, &[&account4_key], 1).unwrap(), + vec![ + multisig_info.clone(), + rent_info.clone(), + account4_info.clone(), + ], + ) + .unwrap(); + + do_process_instruction_dups( + initialize_account(&program_id, &account4_key, &mint_key, &multisig_key).unwrap(), + vec![ + account4_info.clone(), + mint_info.clone(), + multisig_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account4_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account4_info.clone(), owner_info.clone()], + ) + .unwrap(); + + // source-multisig-signer transfer + do_process_instruction_dups( + transfer( + &program_id, + &account4_key, + &account2_key, + &multisig_key, + &[&account4_key], + 500, + ) + .unwrap(), + vec![ + account4_info.clone(), + account2_info.clone(), + multisig_info.clone(), + account4_info.clone(), + ], + ) + .unwrap(); + + // source-multisig-signer TransferChecked + do_process_instruction_dups( + transfer_checked( + &program_id, + &account4_key, + &mint_key, + &account2_key, + &multisig_key, + &[&account4_key], + 500, + 2, + ) + .unwrap(), + vec![ + account4_info.clone(), + mint_info.clone(), + account2_info.clone(), + multisig_info.clone(), + account4_info.clone(), + ], + ) + .unwrap(); +} + +#[test] +fn test_transfer() { + let program_id = spl_token::id(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account2_key = Pubkey::new_unique(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account3_key = Pubkey::new_unique(); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let delegate_key = Pubkey::new_unique(); + let mut delegate_account = SolanaAccount::default(); + let mismatch_key = Pubkey::new_unique(); + let mut mismatch_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = Pubkey::new_unique(); + let mut owner2_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint2_key = Pubkey::new_unique(); + let mut rent_sysvar = rent_sysvar(); + + // create mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account3_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create mismatch account + do_process_instruction( + initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut mismatch_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap(); + account.mint = mint2_key; + Account::pack(account, &mut mismatch_account.data).unwrap(); + + // mint to account + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + // missing signer + let mut instruction = transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 1000, + ) + .unwrap(); + instruction.accounts[2].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + do_process_instruction( + instruction, + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + ); + + // mismatch mint + assert_eq!( + Err(TokenError::MintMismatch.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &mismatch_key, + &owner_key, + &[], + 1000 + ) + .unwrap(), + vec![ + &mut account_account, + &mut mismatch_account, + &mut owner_account, + ], + ) + ); + + // missing owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner2_key, + &[], + 1000 + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner2_account, + ], + ) + ); + + // account not owned by program + let not_program_id = Pubkey::new_unique(); + account_account.owner = not_program_id; + assert_eq!( + Err(ProgramError::IncorrectProgramId), + do_process_instruction( + transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 0,).unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner2_account, + ], + ) + ); + account_account.owner = program_id; + + // account 2 not owned by program + let not_program_id = Pubkey::new_unique(); + account2_account.owner = not_program_id; + assert_eq!( + Err(ProgramError::IncorrectProgramId), + do_process_instruction( + transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 0,).unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner2_account, + ], + ) + ); + account2_account.owner = program_id; + + // transfer + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 1000, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + .unwrap(); + + // insufficient funds + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 1).unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + ); + + // transfer half back + do_process_instruction( + transfer( + &program_id, + &account2_key, + &account_key, + &owner_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut account2_account, + &mut account_account, + &mut owner_account, + ], + ) + .unwrap(); + + // incorrect decimals + assert_eq!( + Err(TokenError::MintDecimalsMismatch.into()), + do_process_instruction( + transfer_checked( + &program_id, + &account2_key, + &mint_key, + &account_key, + &owner_key, + &[], + 1, + 10 // <-- incorrect decimals + ) + .unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut account_account, + &mut owner_account, + ], + ) + ); + + // incorrect mint + assert_eq!( + Err(TokenError::MintMismatch.into()), + do_process_instruction( + transfer_checked( + &program_id, + &account2_key, + &account3_key, // <-- incorrect mint + &account_key, + &owner_key, + &[], + 1, + 2 + ) + .unwrap(), + vec![ + &mut account2_account, + &mut account3_account, // <-- incorrect mint + &mut account_account, + &mut owner_account, + ], + ) + ); + // transfer rest with explicit decimals + do_process_instruction( + transfer_checked( + &program_id, + &account2_key, + &mint_key, + &account_key, + &owner_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut account_account, + &mut owner_account, + ], + ) + .unwrap(); + + // insufficient funds + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + transfer(&program_id, &account2_key, &account_key, &owner_key, &[], 1).unwrap(), + vec![ + &mut account2_account, + &mut account_account, + &mut owner_account, + ], + ) + ); + + // approve delegate + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 100, + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + .unwrap(); + + // not a delegate of source account + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner2_key, // <-- incorrect owner or delegate + &[], + 1, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner2_account, + ], + ) + ); + + // insufficient funds approved via delegate + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &delegate_key, + &[], + 101 + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut delegate_account, + ], + ) + ); + + // transfer via delegate + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &delegate_key, + &[], + 100, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut delegate_account, + ], + ) + .unwrap(); + + // insufficient funds approved via delegate + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &delegate_key, + &[], + 1 + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut delegate_account, + ], + ) + ); + + // transfer rest + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 900, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + .unwrap(); + + // approve delegate + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 100, + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + .unwrap(); + + // insufficient funds in source account via delegate + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &delegate_key, + &[], + 100 + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut delegate_account, + ], + ) + ); +} + +#[test] +fn test_self_transfer() { + let program_id = spl_token::id(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account2_key = Pubkey::new_unique(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account3_key = Pubkey::new_unique(); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let delegate_key = Pubkey::new_unique(); + let mut delegate_account = SolanaAccount::default(); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = Pubkey::new_unique(); + let mut owner2_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // create mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account3_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // mint to account + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + let account_info = (&account_key, false, &mut account_account).into_account_info(); + let account3_info = (&account3_key, false, &mut account3_account).into_account_info(); + let delegate_info = (&delegate_key, true, &mut delegate_account).into_account_info(); + let owner_info = (&owner_key, true, &mut owner_account).into_account_info(); + let owner2_info = (&owner2_key, true, &mut owner2_account).into_account_info(); + let mint_info = (&mint_key, false, &mut mint_account).into_account_info(); + + // transfer + let instruction = transfer( + &program_id, + account_info.key, + account_info.key, + owner_info.key, + &[], + 1000, + ) + .unwrap(); + assert_eq!( + Ok(()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + account_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + ); + // no balance change... + let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); + assert_eq!(account.amount, 1000); + + // transfer checked + let instruction = transfer_checked( + &program_id, + account_info.key, + mint_info.key, + account_info.key, + owner_info.key, + &[], + 1000, + 2, + ) + .unwrap(); + assert_eq!( + Ok(()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + mint_info.clone(), + account_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + ); + // no balance change... + let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); + assert_eq!(account.amount, 1000); + + // missing signer + let mut owner_no_sign_info = owner_info.clone(); + let mut instruction = transfer( + &program_id, + account_info.key, + account_info.key, + owner_no_sign_info.key, + &[], + 1000, + ) + .unwrap(); + instruction.accounts[2].is_signer = false; + owner_no_sign_info.is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + account_info.clone(), + owner_no_sign_info.clone(), + ], + &instruction.data, + ) + ); + + // missing signer checked + let mut instruction = transfer_checked( + &program_id, + account_info.key, + mint_info.key, + account_info.key, + owner_no_sign_info.key, + &[], + 1000, + 2, + ) + .unwrap(); + instruction.accounts[3].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + mint_info.clone(), + account_info.clone(), + owner_no_sign_info, + ], + &instruction.data, + ) + ); + + // missing owner + let instruction = transfer( + &program_id, + account_info.key, + account_info.key, + owner2_info.key, + &[], + 1000, + ) + .unwrap(); + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + account_info.clone(), + owner2_info.clone(), + ], + &instruction.data, + ) + ); + + // missing owner checked + let instruction = transfer_checked( + &program_id, + account_info.key, + mint_info.key, + account_info.key, + owner2_info.key, + &[], + 1000, + 2, + ) + .unwrap(); + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + mint_info.clone(), + account_info.clone(), + owner2_info.clone(), + ], + &instruction.data, + ) + ); + + // insufficient funds + let instruction = transfer( + &program_id, + account_info.key, + account_info.key, + owner_info.key, + &[], + 1001, + ) + .unwrap(); + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + account_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + ); + + // insufficient funds checked + let instruction = transfer_checked( + &program_id, + account_info.key, + mint_info.key, + account_info.key, + owner_info.key, + &[], + 1001, + 2, + ) + .unwrap(); + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + mint_info.clone(), + account_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + ); + + // incorrect decimals + let instruction = transfer_checked( + &program_id, + account_info.key, + mint_info.key, + account_info.key, + owner_info.key, + &[], + 1, + 10, // <-- incorrect decimals + ) + .unwrap(); + assert_eq!( + Err(TokenError::MintDecimalsMismatch.into()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + mint_info.clone(), + account_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + ); + + // incorrect mint + let instruction = transfer_checked( + &program_id, + account_info.key, + account3_info.key, // <-- incorrect mint + account_info.key, + owner_info.key, + &[], + 1, + 2, + ) + .unwrap(); + assert_eq!( + Err(TokenError::MintMismatch.into()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + account3_info.clone(), // <-- incorrect mint + account_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + ); + + // approve delegate + let instruction = approve( + &program_id, + account_info.key, + delegate_info.key, + owner_info.key, + &[], + 100, + ) + .unwrap(); + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + delegate_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + .unwrap(); + + // delegate transfer + let instruction = transfer( + &program_id, + account_info.key, + account_info.key, + delegate_info.key, + &[], + 100, + ) + .unwrap(); + assert_eq!( + Ok(()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + account_info.clone(), + delegate_info.clone(), + ], + &instruction.data, + ) + ); + // no balance change... + let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); + assert_eq!(account.amount, 1000); + assert_eq!(account.delegated_amount, 100); + + // delegate transfer checked + let instruction = transfer_checked( + &program_id, + account_info.key, + mint_info.key, + account_info.key, + delegate_info.key, + &[], + 100, + 2, + ) + .unwrap(); + assert_eq!( + Ok(()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + mint_info.clone(), + account_info.clone(), + delegate_info.clone(), + ], + &instruction.data, + ) + ); + // no balance change... + let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); + assert_eq!(account.amount, 1000); + assert_eq!(account.delegated_amount, 100); + + // delegate insufficient funds + let instruction = transfer( + &program_id, + account_info.key, + account_info.key, + delegate_info.key, + &[], + 101, + ) + .unwrap(); + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + account_info.clone(), + delegate_info.clone(), + ], + &instruction.data, + ) + ); + + // delegate insufficient funds checked + let instruction = transfer_checked( + &program_id, + account_info.key, + mint_info.key, + account_info.key, + delegate_info.key, + &[], + 101, + 2, + ) + .unwrap(); + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + mint_info.clone(), + account_info.clone(), + delegate_info.clone(), + ], + &instruction.data, + ) + ); + + // owner transfer with delegate assigned + let instruction = transfer( + &program_id, + account_info.key, + account_info.key, + owner_info.key, + &[], + 1000, + ) + .unwrap(); + assert_eq!( + Ok(()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + account_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + ); + // no balance change... + let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); + assert_eq!(account.amount, 1000); + + // owner transfer with delegate assigned checked + let instruction = transfer_checked( + &program_id, + account_info.key, + mint_info.key, + account_info.key, + owner_info.key, + &[], + 1000, + 2, + ) + .unwrap(); + assert_eq!( + Ok(()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + mint_info.clone(), + account_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + ); + // no balance change... + let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); + assert_eq!(account.amount, 1000); +} + +#[test] +fn test_mintable_token_with_zero_supply() { + let program_id = spl_token::id(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // create mint-able token with zero supply + let decimals = 2; + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, decimals).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); + assert_eq!( + mint, + Mint { + mint_authority: COption::Some(owner_key), + supply: 0, + decimals, + is_initialized: true, + freeze_authority: COption::None, + } + ); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // mint to + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + let _ = Mint::unpack(&mint_account.data).unwrap(); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.amount, 42); + + // mint to 2, with incorrect decimals + assert_eq!( + Err(TokenError::MintDecimalsMismatch.into()), + do_process_instruction( + mint_to_checked( + &program_id, + &mint_key, + &account_key, + &owner_key, + &[], + 42, + decimals + 1 + ) + .unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + ); + + let _ = Mint::unpack(&mint_account.data).unwrap(); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.amount, 42); + + // mint to 2 + do_process_instruction( + mint_to_checked( + &program_id, + &mint_key, + &account_key, + &owner_key, + &[], + 42, + decimals, + ) + .unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + let _ = Mint::unpack(&mint_account.data).unwrap(); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.amount, 84); +} + +#[test] +fn test_approve_dups() { + let program_id = spl_token::id(); + let account1_key = Pubkey::new_unique(); + let mut account1_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); + let account2_key = Pubkey::new_unique(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account2_info: AccountInfo = (&account2_key, false, &mut account2_account).into(); + let account3_key = Pubkey::new_unique(); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account3_info: AccountInfo = (&account3_key, true, &mut account3_account).into(); + let multisig_key = Pubkey::new_unique(); + let mut multisig_account = SolanaAccount::new( + multisig_minimum_balance(), + Multisig::get_packed_len(), + &program_id, + ); + let multisig_info: AccountInfo = (&multisig_key, true, &mut multisig_account).into(); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into(); + let rent_key = rent::id(); + let mut rent_sysvar = rent_sysvar(); + let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); + + // create mint + do_process_instruction_dups( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![mint_info.clone(), rent_info.clone()], + ) + .unwrap(); + + // create account + do_process_instruction_dups( + initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // create another account + do_process_instruction_dups( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![ + account2_info.clone(), + mint_info.clone(), + owner_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // mint to account + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], + ) + .unwrap(); + + // source-owner approve + do_process_instruction_dups( + approve( + &program_id, + &account1_key, + &account2_key, + &account1_key, + &[], + 500, + ) + .unwrap(), + vec![ + account1_info.clone(), + account2_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // source-owner approve_checked + do_process_instruction_dups( + approve_checked( + &program_id, + &account1_key, + &mint_key, + &account2_key, + &account1_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account2_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // source-owner revoke + do_process_instruction_dups( + revoke(&program_id, &account1_key, &account1_key, &[]).unwrap(), + vec![account1_info.clone(), account1_info.clone()], + ) + .unwrap(); + + // test source-multisig signer + do_process_instruction_dups( + initialize_multisig(&program_id, &multisig_key, &[&account3_key], 1).unwrap(), + vec![ + multisig_info.clone(), + rent_info.clone(), + account3_info.clone(), + ], + ) + .unwrap(); + + do_process_instruction_dups( + initialize_account(&program_id, &account3_key, &mint_key, &multisig_key).unwrap(), + vec![ + account3_info.clone(), + mint_info.clone(), + multisig_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account3_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account3_info.clone(), owner_info.clone()], + ) + .unwrap(); + + // source-multisig-signer approve + do_process_instruction_dups( + approve( + &program_id, + &account3_key, + &account2_key, + &multisig_key, + &[&account3_key], + 500, + ) + .unwrap(), + vec![ + account3_info.clone(), + account2_info.clone(), + multisig_info.clone(), + account3_info.clone(), + ], + ) + .unwrap(); + + // source-multisig-signer approve_checked + do_process_instruction_dups( + approve_checked( + &program_id, + &account3_key, + &mint_key, + &account2_key, + &multisig_key, + &[&account3_key], + 500, + 2, + ) + .unwrap(), + vec![ + account3_info.clone(), + mint_info.clone(), + account2_info.clone(), + multisig_info.clone(), + account3_info.clone(), + ], + ) + .unwrap(); + + // source-owner multisig-signer + do_process_instruction_dups( + revoke(&program_id, &account3_key, &multisig_key, &[&account3_key]).unwrap(), + vec![ + account3_info.clone(), + multisig_info.clone(), + account3_info.clone(), + ], + ) + .unwrap(); +} + +#[test] +fn test_approve() { + let program_id = spl_token::id(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account2_key = Pubkey::new_unique(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let delegate_key = Pubkey::new_unique(); + let mut delegate_account = SolanaAccount::default(); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = Pubkey::new_unique(); + let mut owner2_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // create mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // mint to account + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + // missing signer + let mut instruction = approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 100, + ) + .unwrap(); + instruction.accounts[2].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + do_process_instruction( + instruction, + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + ); + + // no owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner2_key, + &[], + 100 + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner2_account, + ], + ) + ); + + // approve delegate + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 100, + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + .unwrap(); + + // approve delegate 2, with incorrect decimals + assert_eq!( + Err(TokenError::MintDecimalsMismatch.into()), + do_process_instruction( + approve_checked( + &program_id, + &account_key, + &mint_key, + &delegate_key, + &owner_key, + &[], + 100, + 0 // <-- incorrect decimals + ) + .unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut delegate_account, + &mut owner_account, + ], + ) + ); + + // approve delegate 2, with incorrect mint + assert_eq!( + Err(TokenError::MintMismatch.into()), + do_process_instruction( + approve_checked( + &program_id, + &account_key, + &account2_key, // <-- bad mint + &delegate_key, + &owner_key, + &[], + 100, + 0 + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, // <-- bad mint + &mut delegate_account, + &mut owner_account, + ], + ) + ); + + // approve delegate 2 + do_process_instruction( + approve_checked( + &program_id, + &account_key, + &mint_key, + &delegate_key, + &owner_key, + &[], + 100, + 2, + ) + .unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut delegate_account, + &mut owner_account, + ], + ) + .unwrap(); + + // revoke delegate + do_process_instruction( + revoke(&program_id, &account_key, &owner_key, &[]).unwrap(), + vec![&mut account_account, &mut owner_account], + ) + .unwrap(); +} + +#[test] +fn test_set_authority_dups() { + let program_id = spl_token::id(); + let account1_key = Pubkey::new_unique(); + let mut account1_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); + let owner_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into(); + let rent_key = rent::id(); + let mut rent_sysvar = rent_sysvar(); + let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); + + // create mint + do_process_instruction_dups( + initialize_mint(&program_id, &mint_key, &mint_key, Some(&mint_key), 2).unwrap(), + vec![mint_info.clone(), rent_info.clone()], + ) + .unwrap(); + + // create account + do_process_instruction_dups( + initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // set mint_authority when currently self + do_process_instruction_dups( + set_authority( + &program_id, + &mint_key, + Some(&owner_key), + AuthorityType::MintTokens, + &mint_key, + &[], + ) + .unwrap(), + vec![mint_info.clone(), mint_info.clone()], + ) + .unwrap(); + + // set freeze_authority when currently self + do_process_instruction_dups( + set_authority( + &program_id, + &mint_key, + Some(&owner_key), + AuthorityType::FreezeAccount, + &mint_key, + &[], + ) + .unwrap(), + vec![mint_info.clone(), mint_info.clone()], + ) + .unwrap(); + + // set account owner when currently self + do_process_instruction_dups( + set_authority( + &program_id, + &account1_key, + Some(&owner_key), + AuthorityType::AccountOwner, + &account1_key, + &[], + ) + .unwrap(), + vec![account1_info.clone(), account1_info.clone()], + ) + .unwrap(); + + // set close_authority when currently self + let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); + account.close_authority = COption::Some(account1_key); + Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); + + do_process_instruction_dups( + set_authority( + &program_id, + &account1_key, + Some(&owner_key), + AuthorityType::CloseAccount, + &account1_key, + &[], + ) + .unwrap(), + vec![account1_info.clone(), account1_info.clone()], + ) + .unwrap(); +} + +#[test] +fn test_set_authority() { + let program_id = spl_token::id(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account2_key = Pubkey::new_unique(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = Pubkey::new_unique(); + let mut owner2_account = SolanaAccount::default(); + let owner3_key = Pubkey::new_unique(); + let mut owner3_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint2_key = Pubkey::new_unique(); + let mut mint2_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // create new mint with owner + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // create mint with owner and freeze_authority + do_process_instruction( + initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(), + vec![&mut mint2_account, &mut rent_sysvar], + ) + .unwrap(); + + // invalid account + assert_eq!( + Err(ProgramError::UninitializedAccount), + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner2_key), + AuthorityType::AccountOwner, + &owner_key, + &[] + ) + .unwrap(), + vec![&mut account_account, &mut owner_account], + ) + ); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint2_key, &owner_key).unwrap(), + vec![ + &mut account2_account, + &mut mint2_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // missing owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner_key), + AuthorityType::AccountOwner, + &owner2_key, + &[] + ) + .unwrap(), + vec![&mut account_account, &mut owner2_account], + ) + ); + + // owner did not sign + let mut instruction = set_authority( + &program_id, + &account_key, + Some(&owner2_key), + AuthorityType::AccountOwner, + &owner_key, + &[], + ) + .unwrap(); + instruction.accounts[1].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + do_process_instruction(instruction, vec![&mut account_account, &mut owner_account,],) + ); + + // wrong authority type + assert_eq!( + Err(TokenError::AuthorityTypeNotSupported.into()), + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner2_key), + AuthorityType::FreezeAccount, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner_account], + ) + ); + + // account owner may not be set to None + assert_eq!( + Err(TokenError::InvalidInstruction.into()), + do_process_instruction( + set_authority( + &program_id, + &account_key, + None, + AuthorityType::AccountOwner, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner_account], + ) + ); + + // set delegate + do_process_instruction( + approve( + &program_id, + &account_key, + &owner2_key, + &owner_key, + &[], + u64::MAX, + ) + .unwrap(), + vec![ + &mut account_account, + &mut owner2_account, + &mut owner_account, + ], + ) + .unwrap(); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.delegate, COption::Some(owner2_key)); + assert_eq!(account.delegated_amount, u64::MAX); + + // set owner + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner3_key), + AuthorityType::AccountOwner, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner_account], + ) + .unwrap(); + + // check delegate cleared + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.delegate, COption::None); + assert_eq!(account.delegated_amount, 0); + + // set owner without existing delegate + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner2_key), + AuthorityType::AccountOwner, + &owner3_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner3_account], + ) + .unwrap(); + + // set close_authority + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner2_key), + AuthorityType::CloseAccount, + &owner2_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner2_account], + ) + .unwrap(); + + // close_authority may be set to None + do_process_instruction( + set_authority( + &program_id, + &account_key, + None, + AuthorityType::CloseAccount, + &owner2_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner2_account], + ) + .unwrap(); + + // wrong owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + set_authority( + &program_id, + &mint_key, + Some(&owner3_key), + AuthorityType::MintTokens, + &owner2_key, + &[] + ) + .unwrap(), + vec![&mut mint_account, &mut owner2_account], + ) + ); + + // owner did not sign + let mut instruction = set_authority( + &program_id, + &mint_key, + Some(&owner2_key), + AuthorityType::MintTokens, + &owner_key, + &[], + ) + .unwrap(); + instruction.accounts[1].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + do_process_instruction(instruction, vec![&mut mint_account, &mut owner_account],) + ); + + // cannot freeze + assert_eq!( + Err(TokenError::MintCannotFreeze.into()), + do_process_instruction( + set_authority( + &program_id, + &mint_key, + Some(&owner2_key), + AuthorityType::FreezeAccount, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut mint_account, &mut owner_account], + ) + ); + + // set owner + do_process_instruction( + set_authority( + &program_id, + &mint_key, + Some(&owner2_key), + AuthorityType::MintTokens, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut mint_account, &mut owner_account], + ) + .unwrap(); + + // set owner to None + do_process_instruction( + set_authority( + &program_id, + &mint_key, + None, + AuthorityType::MintTokens, + &owner2_key, + &[], + ) + .unwrap(), + vec![&mut mint_account, &mut owner2_account], + ) + .unwrap(); + + // test unsetting mint_authority is one-way operation + assert_eq!( + Err(TokenError::FixedSupply.into()), + do_process_instruction( + set_authority( + &program_id, + &mint2_key, + Some(&owner2_key), + AuthorityType::MintTokens, + &owner_key, + &[] + ) + .unwrap(), + vec![&mut mint_account, &mut owner_account], + ) + ); + + // set freeze_authority + do_process_instruction( + set_authority( + &program_id, + &mint2_key, + Some(&owner2_key), + AuthorityType::FreezeAccount, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut mint2_account, &mut owner_account], + ) + .unwrap(); + + // test unsetting freeze_authority is one-way operation + do_process_instruction( + set_authority( + &program_id, + &mint2_key, + None, + AuthorityType::FreezeAccount, + &owner2_key, + &[], + ) + .unwrap(), + vec![&mut mint2_account, &mut owner2_account], + ) + .unwrap(); + + assert_eq!( + Err(TokenError::MintCannotFreeze.into()), + do_process_instruction( + set_authority( + &program_id, + &mint2_key, + Some(&owner2_key), + AuthorityType::FreezeAccount, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut mint2_account, &mut owner2_account], + ) + ); +} + +#[test] +fn test_mint_to_dups() { + let program_id = spl_token::id(); + let account1_key = Pubkey::new_unique(); + let mut account1_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into(); + let rent_key = rent::id(); + let mut rent_sysvar = rent_sysvar(); + let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); + + // create mint + do_process_instruction_dups( + initialize_mint(&program_id, &mint_key, &mint_key, None, 2).unwrap(), + vec![mint_info.clone(), rent_info.clone()], + ) + .unwrap(); + + // create account + do_process_instruction_dups( + initialize_account(&program_id, &account1_key, &mint_key, &owner_key).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + owner_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // mint_to when mint_authority is self + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account1_key, &mint_key, &[], 42).unwrap(), + vec![mint_info.clone(), account1_info.clone(), mint_info.clone()], + ) + .unwrap(); + + // mint_to_checked when mint_authority is self + do_process_instruction_dups( + mint_to_checked(&program_id, &mint_key, &account1_key, &mint_key, &[], 42, 2).unwrap(), + vec![mint_info.clone(), account1_info.clone(), mint_info.clone()], + ) + .unwrap(); + + // mint_to when mint_authority is account owner + let mut mint = Mint::unpack_unchecked(&mint_info.data.borrow()).unwrap(); + mint.mint_authority = COption::Some(account1_key); + Mint::pack(mint, &mut mint_info.data.borrow_mut()).unwrap(); + do_process_instruction_dups( + mint_to( + &program_id, + &mint_key, + &account1_key, + &account1_key, + &[], + 42, + ) + .unwrap(), + vec![ + mint_info.clone(), + account1_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // mint_to_checked when mint_authority is account owner + do_process_instruction_dups( + mint_to( + &program_id, + &mint_key, + &account1_key, + &account1_key, + &[], + 42, + ) + .unwrap(), + vec![ + mint_info.clone(), + account1_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); +} + +#[test] +fn test_mint_to() { + let program_id = spl_token::id(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account2_key = Pubkey::new_unique(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account3_key = Pubkey::new_unique(); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let mismatch_key = Pubkey::new_unique(); + let mut mismatch_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = Pubkey::new_unique(); + let mut owner2_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint2_key = Pubkey::new_unique(); + let uninitialized_key = Pubkey::new_unique(); + let mut uninitialized_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let mut rent_sysvar = rent_sysvar(); + + // create new mint with owner + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account3_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create mismatch account + do_process_instruction( + initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut mismatch_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap(); + account.mint = mint2_key; + Account::pack(account, &mut mismatch_account.data).unwrap(); + + // mint to + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); + assert_eq!(mint.supply, 42); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.amount, 42); + + // mint to another account to test supply accumulation + do_process_instruction( + mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(), + vec![&mut mint_account, &mut account2_account, &mut owner_account], + ) + .unwrap(); + + let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); + assert_eq!(mint.supply, 84); + let account = Account::unpack_unchecked(&account2_account.data).unwrap(); + assert_eq!(account.amount, 42); + + // missing signer + let mut instruction = + mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(); + instruction.accounts[2].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + do_process_instruction( + instruction, + vec![&mut mint_account, &mut account2_account, &mut owner_account], + ) + ); + + // mismatch account + assert_eq!( + Err(TokenError::MintMismatch.into()), + do_process_instruction( + mint_to(&program_id, &mint_key, &mismatch_key, &owner_key, &[], 42).unwrap(), + vec![&mut mint_account, &mut mismatch_account, &mut owner_account], + ) + ); + + // missing owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + mint_to(&program_id, &mint_key, &account2_key, &owner2_key, &[], 42).unwrap(), + vec![ + &mut mint_account, + &mut account2_account, + &mut owner2_account, + ], + ) + ); + + // mint not owned by program + let not_program_id = Pubkey::new_unique(); + mint_account.owner = not_program_id; + assert_eq!( + Err(ProgramError::IncorrectProgramId), + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 0).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + ); + mint_account.owner = program_id; + + // account not owned by program + let not_program_id = Pubkey::new_unique(); + account_account.owner = not_program_id; + assert_eq!( + Err(ProgramError::IncorrectProgramId), + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 0).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + ); + account_account.owner = program_id; + + // uninitialized destination account + assert_eq!( + Err(ProgramError::UninitializedAccount), + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &uninitialized_key, + &owner_key, + &[], + 42 + ) + .unwrap(), + vec![ + &mut mint_account, + &mut uninitialized_account, + &mut owner_account, + ], + ) + ); + + // unset mint_authority and test minting fails + do_process_instruction( + set_authority( + &program_id, + &mint_key, + None, + AuthorityType::MintTokens, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut mint_account, &mut owner_account], + ) + .unwrap(); + assert_eq!( + Err(TokenError::FixedSupply.into()), + do_process_instruction( + mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(), + vec![&mut mint_account, &mut account2_account, &mut owner_account], + ) + ); +} + +#[test] +fn test_burn_dups() { + let program_id = spl_token::id(); + let account1_key = Pubkey::new_unique(); + let mut account1_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into(); + let rent_key = rent::id(); + let mut rent_sysvar = rent_sysvar(); + let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); + + // create mint + do_process_instruction_dups( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![mint_info.clone(), rent_info.clone()], + ) + .unwrap(); + + // create account + do_process_instruction_dups( + initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // mint to account + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], + ) + .unwrap(); + + // source-owner burn + do_process_instruction_dups( + burn( + &program_id, + &mint_key, + &account1_key, + &account1_key, + &[], + 500, + ) + .unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // source-owner burn_checked + do_process_instruction_dups( + burn_checked( + &program_id, + &account1_key, + &mint_key, + &account1_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // mint-owner burn + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], + ) + .unwrap(); + let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); + account.owner = mint_key; + Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); + do_process_instruction_dups( + burn(&program_id, &account1_key, &mint_key, &mint_key, &[], 500).unwrap(), + vec![account1_info.clone(), mint_info.clone(), mint_info.clone()], + ) + .unwrap(); + + // mint-owner burn_checked + do_process_instruction_dups( + burn_checked( + &program_id, + &account1_key, + &mint_key, + &mint_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![account1_info.clone(), mint_info.clone(), mint_info.clone()], + ) + .unwrap(); + + // source-delegate burn + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], + ) + .unwrap(); + let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); + account.delegated_amount = 1000; + account.delegate = COption::Some(account1_key); + account.owner = owner_key; + Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); + do_process_instruction_dups( + burn( + &program_id, + &account1_key, + &mint_key, + &account1_key, + &[], + 500, + ) + .unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // source-delegate burn_checked + do_process_instruction_dups( + burn_checked( + &program_id, + &account1_key, + &mint_key, + &account1_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // mint-delegate burn + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], + ) + .unwrap(); + let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); + account.delegated_amount = 1000; + account.delegate = COption::Some(mint_key); + account.owner = owner_key; + Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); + do_process_instruction_dups( + burn(&program_id, &account1_key, &mint_key, &mint_key, &[], 500).unwrap(), + vec![account1_info.clone(), mint_info.clone(), mint_info.clone()], + ) + .unwrap(); + + // mint-delegate burn_checked + do_process_instruction_dups( + burn_checked( + &program_id, + &account1_key, + &mint_key, + &mint_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![account1_info.clone(), mint_info.clone(), mint_info.clone()], + ) + .unwrap(); +} + +#[test] +fn test_burn() { + let program_id = spl_token::id(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account2_key = Pubkey::new_unique(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account3_key = Pubkey::new_unique(); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let delegate_key = Pubkey::new_unique(); + let mut delegate_account = SolanaAccount::default(); + let mismatch_key = Pubkey::new_unique(); + let mut mismatch_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = Pubkey::new_unique(); + let mut owner2_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint2_key = Pubkey::new_unique(); + let mut rent_sysvar = rent_sysvar(); + + // create new mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account3_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create mismatch account + do_process_instruction( + initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut mismatch_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // mint to account + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + // mint to mismatch account and change mint key + do_process_instruction( + mint_to(&program_id, &mint_key, &mismatch_key, &owner_key, &[], 1000).unwrap(), + vec![&mut mint_account, &mut mismatch_account, &mut owner_account], + ) + .unwrap(); + let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap(); + account.mint = mint2_key; + Account::pack(account, &mut mismatch_account.data).unwrap(); + + // missing signer + let mut instruction = + burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 42).unwrap(); + instruction.accounts[1].is_signer = false; + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + instruction, + vec![ + &mut account_account, + &mut mint_account, + &mut delegate_account + ], + ) + ); + + // missing owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + burn(&program_id, &account_key, &mint_key, &owner2_key, &[], 42).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner2_account], + ) + ); + + // account not owned by program + let not_program_id = Pubkey::new_unique(); + account_account.owner = not_program_id; + assert_eq!( + Err(ProgramError::IncorrectProgramId), + do_process_instruction( + burn(&program_id, &account_key, &mint_key, &owner_key, &[], 0).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + ); + account_account.owner = program_id; + + // mint not owned by program + let not_program_id = Pubkey::new_unique(); + mint_account.owner = not_program_id; + assert_eq!( + Err(ProgramError::IncorrectProgramId), + do_process_instruction( + burn(&program_id, &account_key, &mint_key, &owner_key, &[], 0).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + ); + mint_account.owner = program_id; + + // mint mismatch + assert_eq!( + Err(TokenError::MintMismatch.into()), + do_process_instruction( + burn(&program_id, &mismatch_key, &mint_key, &owner_key, &[], 42).unwrap(), + vec![&mut mismatch_account, &mut mint_account, &mut owner_account], + ) + ); + + // burn + do_process_instruction( + burn(&program_id, &account_key, &mint_key, &owner_key, &[], 21).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // burn_checked, with incorrect decimals + assert_eq!( + Err(TokenError::MintDecimalsMismatch.into()), + do_process_instruction( + burn_checked(&program_id, &account_key, &mint_key, &owner_key, &[], 21, 3).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + ); + + // burn_checked + do_process_instruction( + burn_checked(&program_id, &account_key, &mint_key, &owner_key, &[], 21, 2).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); + assert_eq!(mint.supply, 2000 - 42); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.amount, 1000 - 42); + + // insufficient funds + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + burn( + &program_id, + &account_key, + &mint_key, + &owner_key, + &[], + 100_000_000 + ) + .unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + ); + + // approve delegate + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 84, + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + .unwrap(); + + // not a delegate of source account + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + burn( + &program_id, + &account_key, + &mint_key, + &owner2_key, // <-- incorrect owner or delegate + &[], + 1, + ) + .unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner2_account], + ) + ); + + // insufficient funds approved via delegate + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 85).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut delegate_account + ], + ) + ); + + // burn via delegate + do_process_instruction( + burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 84).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut delegate_account, + ], + ) + .unwrap(); + + // match + 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); + + // insufficient funds approved via delegate + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 1).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut delegate_account + ], + ) + ); +} + +#[test] +fn test_burn_and_close_system_and_incinerator_tokens() { + let program_id = spl_token::id(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let incinerator_account_key = Pubkey::new_unique(); + let mut incinerator_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let system_account_key = Pubkey::new_unique(); + let mut system_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let recipient_key = Pubkey::new_unique(); + let mut recipient_account = SolanaAccount::default(); + let mut mock_incinerator_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + + // create new mint + do_process_instruction( + initialize_mint2(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + // create account + do_process_instruction( + initialize_account3(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account], + ) + .unwrap(); + + // create incinerator- and system-owned accounts + do_process_instruction( + initialize_account3( + &program_id, + &incinerator_account_key, + &mint_key, + &solana_program::incinerator::id(), + ) + .unwrap(), + vec![&mut incinerator_account, &mut mint_account], + ) + .unwrap(); + do_process_instruction( + initialize_account3( + &program_id, + &system_account_key, + &mint_key, + &solana_program::system_program::id(), + ) + .unwrap(), + vec![&mut system_account, &mut mint_account], + ) + .unwrap(); + + // mint to account + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + // transfer half to incinerator, half to system program + do_process_instruction( + transfer( + &program_id, + &account_key, + &incinerator_account_key, + &owner_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut account_account, + &mut incinerator_account, + &mut owner_account, + ], + ) + .unwrap(); + do_process_instruction( + transfer( + &program_id, + &account_key, + &system_account_key, + &owner_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut account_account, + &mut system_account, + &mut owner_account, + ], + ) + .unwrap(); + + // close with balance fails + assert_eq!( + Err(TokenError::NonNativeHasBalance.into()), + do_process_instruction( + close_account( + &program_id, + &incinerator_account_key, + &solana_program::incinerator::id(), + &owner_key, + &[] + ) + .unwrap(), + vec![ + &mut incinerator_account, + &mut mock_incinerator_account, + &mut owner_account, + ], + ) + ); + assert_eq!( + Err(TokenError::NonNativeHasBalance.into()), + do_process_instruction( + close_account( + &program_id, + &system_account_key, + &solana_program::incinerator::id(), + &owner_key, + &[] + ) + .unwrap(), + vec![ + &mut system_account, + &mut mock_incinerator_account, + &mut owner_account, + ], + ) + ); + + // anyone can burn + do_process_instruction( + burn( + &program_id, + &incinerator_account_key, + &mint_key, + &recipient_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut incinerator_account, + &mut mint_account, + &mut recipient_account, + ], + ) + .unwrap(); + do_process_instruction( + burn( + &program_id, + &system_account_key, + &mint_key, + &recipient_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut system_account, + &mut mint_account, + &mut recipient_account, + ], + ) + .unwrap(); + + // closing fails if destination is not the incinerator + assert_eq!( + Err(ProgramError::InvalidAccountData), + do_process_instruction( + close_account( + &program_id, + &incinerator_account_key, + &recipient_key, + &owner_key, + &[] + ) + .unwrap(), + vec![ + &mut incinerator_account, + &mut recipient_account, + &mut owner_account, + ], + ) + ); + assert_eq!( + Err(ProgramError::InvalidAccountData), + do_process_instruction( + close_account( + &program_id, + &system_account_key, + &recipient_key, + &owner_key, + &[] + ) + .unwrap(), + vec![ + &mut system_account, + &mut recipient_account, + &mut owner_account, + ], + ) + ); + + // closing succeeds with incinerator recipient + do_process_instruction( + close_account( + &program_id, + &incinerator_account_key, + &solana_program::incinerator::id(), + &owner_key, + &[], + ) + .unwrap(), + vec![ + &mut incinerator_account, + &mut mock_incinerator_account, + &mut owner_account, + ], + ) + .unwrap(); + + do_process_instruction( + close_account( + &program_id, + &system_account_key, + &solana_program::incinerator::id(), + &owner_key, + &[], + ) + .unwrap(), + vec![ + &mut system_account, + &mut mock_incinerator_account, + &mut owner_account, + ], + ) + .unwrap(); +} + +#[test] +fn test_multisig() { + let program_id = spl_token::id(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let account_key = Pubkey::new_unique(); + let mut account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account2_key = Pubkey::new_unique(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let multisig_key = Pubkey::new_unique(); + let mut multisig_account = SolanaAccount::new(42, Multisig::get_packed_len(), &program_id); + let multisig_delegate_key = Pubkey::new_unique(); + let mut multisig_delegate_account = SolanaAccount::new( + multisig_minimum_balance(), + Multisig::get_packed_len(), + &program_id, + ); + let signer_keys = vec![Pubkey::new_unique(); MAX_SIGNERS]; + let signer_key_refs: Vec<&Pubkey> = signer_keys.iter().collect(); + let mut signer_accounts = vec![SolanaAccount::new(0, 0, &program_id); MAX_SIGNERS]; + let mut rent_sysvar = rent_sysvar(); + + // multisig is not rent exempt + let account_info_iter = &mut signer_accounts.iter_mut(); + assert_eq!( + Err(TokenError::NotRentExempt.into()), + do_process_instruction( + initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), + vec![ + &mut multisig_account, + &mut rent_sysvar, + account_info_iter.next().unwrap(), + ], + ) + ); + + multisig_account.lamports = multisig_minimum_balance(); + let mut multisig_account2 = multisig_account.clone(); + + // single signer + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), + vec![ + &mut multisig_account, + &mut rent_sysvar, + account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // single signer using `initialize_multisig2` + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + initialize_multisig2(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), + vec![&mut multisig_account2, account_info_iter.next().unwrap()], + ) + .unwrap(); + + // multiple signer + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + initialize_multisig( + &program_id, + &multisig_delegate_key, + &signer_key_refs, + MAX_SIGNERS as u8, + ) + .unwrap(), + vec![ + &mut multisig_delegate_account, + &mut rent_sysvar, + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // create new mint with multisig owner + do_process_instruction( + initialize_mint(&program_id, &mint_key, &multisig_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // create account with multisig owner + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &multisig_key).unwrap(), + vec![ + &mut account, + &mut mint_account, + &mut multisig_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create another account with multisig owner + do_process_instruction( + initialize_account( + &program_id, + &account2_key, + &mint_key, + &multisig_delegate_key, + ) + .unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut multisig_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // mint to account + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account_key, + &multisig_key, + &[&signer_keys[0]], + 1000, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account, + &mut multisig_account, + account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // approve + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + approve( + &program_id, + &account_key, + &multisig_delegate_key, + &multisig_key, + &[&signer_keys[0]], + 100, + ) + .unwrap(), + vec![ + &mut account, + &mut multisig_delegate_account, + &mut multisig_account, + account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // transfer + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &multisig_key, + &[&signer_keys[0]], + 42, + ) + .unwrap(), + vec![ + &mut account, + &mut account2_account, + &mut multisig_account, + account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // transfer via delegate + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &multisig_delegate_key, + &signer_key_refs, + 42, + ) + .unwrap(), + vec![ + &mut account, + &mut account2_account, + &mut multisig_delegate_account, + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // mint to + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account2_key, + &multisig_key, + &[&signer_keys[0]], + 42, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account2_account, + &mut multisig_account, + account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // burn + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + burn( + &program_id, + &account_key, + &mint_key, + &multisig_key, + &[&signer_keys[0]], + 42, + ) + .unwrap(), + vec![ + &mut account, + &mut mint_account, + &mut multisig_account, + account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // burn via delegate + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + burn( + &program_id, + &account_key, + &mint_key, + &multisig_delegate_key, + &signer_key_refs, + 42, + ) + .unwrap(), + vec![ + &mut account, + &mut mint_account, + &mut multisig_delegate_account, + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // freeze account + let account3_key = Pubkey::new_unique(); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let mint2_key = Pubkey::new_unique(); + let mut mint2_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + do_process_instruction( + initialize_mint( + &program_id, + &mint2_key, + &multisig_key, + Some(&multisig_key), + 2, + ) + .unwrap(), + vec![&mut mint2_account, &mut rent_sysvar], + ) + .unwrap(); + do_process_instruction( + initialize_account(&program_id, &account3_key, &mint2_key, &owner_key).unwrap(), + vec![ + &mut account3_account, + &mut mint2_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + mint_to( + &program_id, + &mint2_key, + &account3_key, + &multisig_key, + &[&signer_keys[0]], + 1000, + ) + .unwrap(), + vec![ + &mut mint2_account, + &mut account3_account, + &mut multisig_account, + account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + freeze_account( + &program_id, + &account3_key, + &mint2_key, + &multisig_key, + &[&signer_keys[0]], + ) + .unwrap(), + vec![ + &mut account3_account, + &mut mint2_account, + &mut multisig_account, + account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // do SetAuthority on mint + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + set_authority( + &program_id, + &mint_key, + Some(&owner_key), + AuthorityType::MintTokens, + &multisig_key, + &[&signer_keys[0]], + ) + .unwrap(), + vec![ + &mut mint_account, + &mut multisig_account, + account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // do SetAuthority on account + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner_key), + AuthorityType::AccountOwner, + &multisig_key, + &[&signer_keys[0]], + ) + .unwrap(), + vec![ + &mut account, + &mut multisig_account, + account_info_iter.next().unwrap(), + ], + ) + .unwrap(); +} + +#[test] +fn test_owner_close_account_dups() { + let program_id = spl_token::id(); + let owner_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into(); + let rent_key = rent::id(); + let mut rent_sysvar = rent_sysvar(); + let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); + + // create mint + do_process_instruction_dups( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![mint_info.clone(), rent_info.clone()], + ) + .unwrap(); + + let to_close_key = Pubkey::new_unique(); + let mut to_close_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let to_close_account_info: AccountInfo = (&to_close_key, true, &mut to_close_account).into(); + let destination_account_key = Pubkey::new_unique(); + let mut destination_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let destination_account_info: AccountInfo = + (&destination_account_key, true, &mut destination_account).into(); + // create account + do_process_instruction_dups( + initialize_account(&program_id, &to_close_key, &mint_key, &to_close_key).unwrap(), + vec![ + to_close_account_info.clone(), + mint_info.clone(), + to_close_account_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // source-owner close + do_process_instruction_dups( + close_account( + &program_id, + &to_close_key, + &destination_account_key, + &to_close_key, + &[], + ) + .unwrap(), + vec![ + to_close_account_info.clone(), + destination_account_info.clone(), + to_close_account_info.clone(), + ], + ) + .unwrap(); + assert_eq!(*to_close_account_info.data.borrow(), &[0u8; Account::LEN]); +} + +#[test] +fn test_close_authority_close_account_dups() { + let program_id = spl_token::id(); + let owner_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into(); + let rent_key = rent::id(); + let mut rent_sysvar = rent_sysvar(); + let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); + + // create mint + do_process_instruction_dups( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![mint_info.clone(), rent_info.clone()], + ) + .unwrap(); + + let to_close_key = Pubkey::new_unique(); + let mut to_close_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let to_close_account_info: AccountInfo = (&to_close_key, true, &mut to_close_account).into(); + let destination_account_key = Pubkey::new_unique(); + let mut destination_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let destination_account_info: AccountInfo = + (&destination_account_key, true, &mut destination_account).into(); + // create account + do_process_instruction_dups( + initialize_account(&program_id, &to_close_key, &mint_key, &to_close_key).unwrap(), + vec![ + to_close_account_info.clone(), + mint_info.clone(), + to_close_account_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + let mut account = Account::unpack_unchecked(&to_close_account_info.data.borrow()).unwrap(); + account.close_authority = COption::Some(to_close_key); + account.owner = owner_key; + Account::pack(account, &mut to_close_account_info.data.borrow_mut()).unwrap(); + do_process_instruction_dups( + close_account( + &program_id, + &to_close_key, + &destination_account_key, + &to_close_key, + &[], + ) + .unwrap(), + vec![ + to_close_account_info.clone(), + destination_account_info.clone(), + to_close_account_info.clone(), + ], + ) + .unwrap(); + assert_eq!(*to_close_account_info.data.borrow(), &[0u8; Account::LEN]); +} + +#[test] +fn test_close_account() { + let program_id = spl_token::id(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account2_key = Pubkey::new_unique(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance() + 42, + Account::get_packed_len(), + &program_id, + ); + let account3_key = Pubkey::new_unique(); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = Pubkey::new_unique(); + let mut owner2_account = SolanaAccount::default(); + let mut rent_sysvar = rent_sysvar(); + + // uninitialized + assert_eq!( + Err(ProgramError::UninitializedAccount), + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner2_account, + ], + ) + ); + + // initialize and mint to non-native account + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(), + vec![ + &mut mint_account, + &mut account_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.amount, 42); + + // initialize native account + do_process_instruction( + initialize_account( + &program_id, + &account2_key, + &spl_token::native_mint::id(), + &owner_key, + ) + .unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + let account = Account::unpack_unchecked(&account2_account.data).unwrap(); + assert!(account.is_native()); + assert_eq!(account.amount, 42); + + // close non-native account with balance + assert_eq!( + Err(TokenError::NonNativeHasBalance.into()), + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner_account, + ], + ) + ); + assert_eq!(account_account.lamports, account_minimum_balance()); + + // empty account + do_process_instruction( + burn(&program_id, &account_key, &mint_key, &owner_key, &[], 42).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // wrong owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner2_account, + ], + ) + ); + + // close account + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner_account, + ], + ) + .unwrap(); + assert_eq!(account_account.lamports, 0); + assert_eq!(account3_account.lamports, 2 * account_minimum_balance()); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.amount, 0); + + // fund and initialize new non-native account to test close authority + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let owner2_key = Pubkey::new_unique(); + let mut owner2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + account_account.lamports = 2; + + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner2_key), + AuthorityType::CloseAccount, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner_account], + ) + .unwrap(); + + // account owner cannot authorize close if close_authority is set + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner_account, + ], + ) + ); + + // close non-native account with close_authority + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner2_account, + ], + ) + .unwrap(); + assert_eq!(account_account.lamports, 0); + assert_eq!(account3_account.lamports, 2 * account_minimum_balance() + 2); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.amount, 0); + + // close native account + do_process_instruction( + close_account(&program_id, &account2_key, &account3_key, &owner_key, &[]).unwrap(), + vec![ + &mut account2_account, + &mut account3_account, + &mut owner_account, + ], + ) + .unwrap(); + assert_eq!(account2_account.data, [0u8; Account::LEN]); + assert_eq!( + account3_account.lamports, + 3 * account_minimum_balance() + 2 + 42 + ); +} + +#[test] +fn test_native_token() { + let program_id = spl_token::id(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance() + 40, + Account::get_packed_len(), + &program_id, + ); + let account2_key = Pubkey::new_unique(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account3_key = Pubkey::new_unique(); + let mut account3_account = SolanaAccount::new(account_minimum_balance(), 0, &program_id); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = Pubkey::new_unique(); + let mut owner2_account = SolanaAccount::default(); + let owner3_key = Pubkey::new_unique(); + let mut rent_sysvar = rent_sysvar(); + + // initialize native account + do_process_instruction( + initialize_account( + &program_id, + &account_key, + &spl_token::native_mint::id(), + &owner_key, + ) + .unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert!(account.is_native()); + assert_eq!(account.amount, 40); + + // initialize native account + do_process_instruction( + initialize_account( + &program_id, + &account2_key, + &spl_token::native_mint::id(), + &owner_key, + ) + .unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + let account = Account::unpack_unchecked(&account2_account.data).unwrap(); + assert!(account.is_native()); + assert_eq!(account.amount, 0); + + // mint_to unsupported + assert_eq!( + Err(TokenError::NativeNotSupported.into()), + do_process_instruction( + mint_to( + &program_id, + &spl_token::native_mint::id(), + &account_key, + &owner_key, + &[], + 42 + ) + .unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + ); + + // burn unsupported + let bogus_mint_key = Pubkey::new_unique(); + let mut bogus_mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + do_process_instruction( + initialize_mint(&program_id, &bogus_mint_key, &owner_key, None, 2).unwrap(), + vec![&mut bogus_mint_account, &mut rent_sysvar], + ) + .unwrap(); + + assert_eq!( + Err(TokenError::NativeNotSupported.into()), + do_process_instruction( + burn( + &program_id, + &account_key, + &bogus_mint_key, + &owner_key, + &[], + 42 + ) + .unwrap(), + vec![ + &mut account_account, + &mut bogus_mint_account, + &mut owner_account + ], + ) + ); + + // ensure can't transfer below rent-exempt reserve + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 50, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + ); + + // transfer between native accounts + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 40, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + .unwrap(); + assert_eq!(account_account.lamports, account_minimum_balance()); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert!(account.is_native()); + assert_eq!(account.amount, 0); + assert_eq!(account2_account.lamports, account_minimum_balance() + 40); + let account = Account::unpack_unchecked(&account2_account.data).unwrap(); + assert!(account.is_native()); + assert_eq!(account.amount, 40); + + // set close authority + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner3_key), + AuthorityType::CloseAccount, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner_account], + ) + .unwrap(); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.close_authority, COption::Some(owner3_key)); + + // set new account owner + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner2_key), + AuthorityType::AccountOwner, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner_account], + ) + .unwrap(); + + // close authority cleared + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.close_authority, COption::None); + + // close native account + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner2_account, + ], + ) + .unwrap(); + assert_eq!(account_account.lamports, 0); + assert_eq!(account3_account.lamports, 2 * account_minimum_balance()); + assert_eq!(account_account.data, [0u8; Account::LEN]); +} + +#[test] +fn test_overflow() { + let program_id = spl_token::id(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account2_key = Pubkey::new_unique(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = Pubkey::new_unique(); + let mut owner2_account = SolanaAccount::default(); + let mint_owner_key = Pubkey::new_unique(); + let mut mint_owner_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // create new mint with owner + do_process_instruction( + initialize_mint(&program_id, &mint_key, &mint_owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // create an account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner2_key).unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut owner2_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // mint the max to an account + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account_key, + &mint_owner_key, + &[], + u64::MAX, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account_account, + &mut mint_owner_account, + ], + ) + .unwrap(); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.amount, u64::MAX); + + // attempt to mint one more to account + assert_eq!( + Err(TokenError::Overflow.into()), + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account_key, + &mint_owner_key, + &[], + 1, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account_account, + &mut mint_owner_account, + ], + ) + ); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.amount, u64::MAX); + + // attempt to mint one more to the other account + assert_eq!( + Err(TokenError::Overflow.into()), + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account2_key, + &mint_owner_key, + &[], + 1, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account2_account, + &mut mint_owner_account, + ], + ) + ); + + // burn some of the supply + do_process_instruction( + burn(&program_id, &account_key, &mint_key, &owner_key, &[], 100).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.amount, u64::MAX - 100); + + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account_key, + &mint_owner_key, + &[], + 100, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account_account, + &mut mint_owner_account, + ], + ) + .unwrap(); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.amount, u64::MAX); + + // manipulate account balance to attempt overflow transfer + let mut account = Account::unpack_unchecked(&account2_account.data).unwrap(); + account.amount = 1; + Account::pack(account, &mut account2_account.data).unwrap(); + + assert_eq!( + Err(TokenError::Overflow.into()), + do_process_instruction( + transfer( + &program_id, + &account2_key, + &account_key, + &owner2_key, + &[], + 1, + ) + .unwrap(), + vec![ + &mut account2_account, + &mut account_account, + &mut owner2_account, + ], + ) + ); +} + +#[test] +fn test_frozen() { + let program_id = spl_token::id(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account2_key = Pubkey::new_unique(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // create new mint and fund first account + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // fund first account + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + // no transfer if either account is frozen + let mut account = Account::unpack_unchecked(&account2_account.data).unwrap(); + account.state = AccountState::Frozen; + Account::pack(account, &mut account2_account.data).unwrap(); + assert_eq!( + Err(TokenError::AccountFrozen.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + ); + + let mut account = Account::unpack_unchecked(&account_account.data).unwrap(); + account.state = AccountState::Initialized; + Account::pack(account, &mut account_account.data).unwrap(); + let mut account = Account::unpack_unchecked(&account2_account.data).unwrap(); + account.state = AccountState::Frozen; + Account::pack(account, &mut account2_account.data).unwrap(); + assert_eq!( + Err(TokenError::AccountFrozen.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + ); + + // no approve if account is frozen + let mut account = Account::unpack_unchecked(&account_account.data).unwrap(); + account.state = AccountState::Frozen; + Account::pack(account, &mut account_account.data).unwrap(); + let delegate_key = Pubkey::new_unique(); + let mut delegate_account = SolanaAccount::default(); + assert_eq!( + Err(TokenError::AccountFrozen.into()), + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 100 + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + ); + + // no revoke if account is frozen + let mut account = Account::unpack_unchecked(&account_account.data).unwrap(); + account.delegate = COption::Some(delegate_key); + account.delegated_amount = 100; + Account::pack(account, &mut account_account.data).unwrap(); + assert_eq!( + Err(TokenError::AccountFrozen.into()), + do_process_instruction( + revoke(&program_id, &account_key, &owner_key, &[]).unwrap(), + vec![&mut account_account, &mut owner_account], + ) + ); + + // no set authority if account is frozen + let new_owner_key = Pubkey::new_unique(); + assert_eq!( + Err(TokenError::AccountFrozen.into()), + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&new_owner_key), + AuthorityType::AccountOwner, + &owner_key, + &[] + ) + .unwrap(), + vec![&mut account_account, &mut owner_account,], + ) + ); + + // no mint_to if destination account is frozen + assert_eq!( + Err(TokenError::AccountFrozen.into()), + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 100).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account,], + ) + ); + + // no burn if account is frozen + assert_eq!( + Err(TokenError::AccountFrozen.into()), + do_process_instruction( + burn(&program_id, &account_key, &mint_key, &owner_key, &[], 100).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + ); +} + +#[test] +fn test_freeze_thaw_dups() { + let program_id = spl_token::id(); + let account1_key = Pubkey::new_unique(); + let mut account1_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); + let owner_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into(); + let rent_key = rent::id(); + let mut rent_sysvar = rent_sysvar(); + let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); + + // create mint + do_process_instruction_dups( + initialize_mint(&program_id, &mint_key, &owner_key, Some(&account1_key), 2).unwrap(), + vec![mint_info.clone(), rent_info.clone()], + ) + .unwrap(); + + // create account + do_process_instruction_dups( + initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // freeze where mint freeze_authority is account + do_process_instruction_dups( + freeze_account(&program_id, &account1_key, &mint_key, &account1_key, &[]).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // thaw where mint freeze_authority is account + let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); + account.state = AccountState::Frozen; + Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); + do_process_instruction_dups( + thaw_account(&program_id, &account1_key, &mint_key, &account1_key, &[]).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); +} + +#[test] +fn test_freeze_account() { + let program_id = spl_token::id(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account_owner_key = Pubkey::new_unique(); + let mut account_owner_account = SolanaAccount::default(); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = Pubkey::new_unique(); + let mut owner2_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // create new mint with owner different from account owner + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &account_owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut account_owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // mint to account + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + // mint cannot freeze + assert_eq!( + Err(TokenError::MintCannotFreeze.into()), + do_process_instruction( + freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + ); + + // missing freeze_authority + let mut mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); + mint.freeze_authority = COption::Some(owner_key); + Mint::pack(mint, &mut mint_account.data).unwrap(); + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + freeze_account(&program_id, &account_key, &mint_key, &owner2_key, &[]).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner2_account], + ) + ); + + // check explicit thaw + assert_eq!( + Err(TokenError::InvalidState.into()), + do_process_instruction( + thaw_account(&program_id, &account_key, &mint_key, &owner2_key, &[]).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner2_account], + ) + ); + + // freeze + do_process_instruction( + freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.state, AccountState::Frozen); + + // check explicit freeze + assert_eq!( + Err(TokenError::InvalidState.into()), + do_process_instruction( + freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + ); + + // check thaw authority + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + thaw_account(&program_id, &account_key, &mint_key, &owner2_key, &[]).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner2_account], + ) + ); + + // thaw + do_process_instruction( + thaw_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.state, AccountState::Initialized); +} + +#[test] +fn test_initialize_account2_and_3() { + let program_id = spl_token::id(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // create mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + do_process_instruction( + initialize_account2(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account2_account, &mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + assert_eq!(account_account, account2_account); + + do_process_instruction( + initialize_account3(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account3_account, &mut mint_account], + ) + .unwrap(); + + assert_eq!(account_account, account3_account); +} + +#[test] +fn test_sync_native() { + let program_id = spl_token::id(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let native_account_key = Pubkey::new_unique(); + let lamports = 40; + let mut native_account = SolanaAccount::new( + account_minimum_balance() + lamports, + Account::get_packed_len(), + &program_id, + ); + let non_native_account_key = Pubkey::new_unique(); + let mut non_native_account = SolanaAccount::new( + account_minimum_balance() + 50, + Account::get_packed_len(), + &program_id, + ); + + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let mut rent_sysvar = rent_sysvar(); + + // initialize non-native mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // initialize non-native account + do_process_instruction( + initialize_account(&program_id, &non_native_account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut non_native_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + let account = Account::unpack_unchecked(&non_native_account.data).unwrap(); + assert!(!account.is_native()); + assert_eq!(account.amount, 0); + + // fail sync non-native + assert_eq!( + Err(TokenError::NonNativeNotSupported.into()), + do_process_instruction( + sync_native(&program_id, &non_native_account_key,).unwrap(), + vec![&mut non_native_account], + ) + ); + + // fail sync uninitialized + assert_eq!( + Err(ProgramError::UninitializedAccount), + do_process_instruction( + sync_native(&program_id, &native_account_key,).unwrap(), + vec![&mut native_account], + ) + ); + + // wrap native account + do_process_instruction( + initialize_account( + &program_id, + &native_account_key, + &spl_token::native_mint::id(), + &owner_key, + ) + .unwrap(), + vec![ + &mut native_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // fail sync, not owned by program + let not_program_id = Pubkey::new_unique(); + native_account.owner = not_program_id; + assert_eq!( + Err(ProgramError::IncorrectProgramId), + do_process_instruction( + sync_native(&program_id, &native_account_key,).unwrap(), + vec![&mut native_account], + ) + ); + native_account.owner = program_id; + + let account = Account::unpack_unchecked(&native_account.data).unwrap(); + assert!(account.is_native()); + assert_eq!(account.amount, lamports); + + // sync, no change + do_process_instruction( + sync_native(&program_id, &native_account_key).unwrap(), + vec![&mut native_account], + ) + .unwrap(); + let account = Account::unpack_unchecked(&native_account.data).unwrap(); + assert_eq!(account.amount, lamports); + + // transfer sol + let new_lamports = lamports + 50; + native_account.lamports = account_minimum_balance() + new_lamports; + + // success sync + do_process_instruction( + sync_native(&program_id, &native_account_key).unwrap(), + vec![&mut native_account], + ) + .unwrap(); + let account = Account::unpack_unchecked(&native_account.data).unwrap(); + assert_eq!(account.amount, new_lamports); + + // reduce sol + native_account.lamports -= 1; + + // fail sync + assert_eq!( + Err(TokenError::InvalidState.into()), + do_process_instruction( + sync_native(&program_id, &native_account_key,).unwrap(), + vec![&mut native_account], + ) + ); +} + +#[test] +#[serial] +fn test_get_account_data_size() { + // see integration tests for return-data validity + let program_id = spl_token::id(); + let owner_key = Pubkey::new_unique(); + let mut rent_sysvar = rent_sysvar(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_key = Pubkey::new_unique(); + // fail if an invalid mint is passed in + assert_eq!( + Err(TokenError::InvalidMint.into()), + do_process_instruction( + get_account_data_size(&program_id, &mint_key).unwrap(), + vec![&mut mint_account], + ) + ); + + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + set_expected_data(Account::LEN.to_le_bytes().to_vec()); + do_process_instruction( + get_account_data_size(&program_id, &mint_key).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); +} + +#[test] +fn test_initialize_immutable_owner() { + let program_id = spl_token::id(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // create mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // success initialize immutable + do_process_instruction( + initialize_immutable_owner(&program_id, &account_key).unwrap(), + vec![&mut account_account], + ) + .unwrap(); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // fail post-init + assert_eq!( + Err(TokenError::AlreadyInUse.into()), + do_process_instruction( + initialize_immutable_owner(&program_id, &account_key).unwrap(), + vec![&mut account_account], + ) + ); +} + +#[test] +#[serial] +fn test_amount_to_ui_amount() { + let program_id = spl_token::id(); + let owner_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // fail if an invalid mint is passed in + assert_eq!( + Err(TokenError::InvalidMint.into()), + do_process_instruction( + amount_to_ui_amount(&program_id, &mint_key, 110).unwrap(), + vec![&mut mint_account], + ) + ); + + // create mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + set_expected_data("0.23".as_bytes().to_vec()); + do_process_instruction( + amount_to_ui_amount(&program_id, &mint_key, 23).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data("1.1".as_bytes().to_vec()); + do_process_instruction( + amount_to_ui_amount(&program_id, &mint_key, 110).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data("42".as_bytes().to_vec()); + do_process_instruction( + amount_to_ui_amount(&program_id, &mint_key, 4200).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data("0".as_bytes().to_vec()); + do_process_instruction( + amount_to_ui_amount(&program_id, &mint_key, 0).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); +} + +#[test] +#[serial] +fn test_ui_amount_to_amount() { + let program_id = spl_token::id(); + let owner_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // fail if an invalid mint is passed in + assert_eq!( + Err(TokenError::InvalidMint.into()), + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "1.1").unwrap(), + vec![&mut mint_account], + ) + ); + + // create mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + set_expected_data(23u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "0.23").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data(20u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "0.20").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data(20u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "0.2000").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data(20u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, ".20").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data(110u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "1.1").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data(110u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "1.10").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data(4200u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "42").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data(4200u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "42.").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data(0u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "0").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + // fail if invalid ui_amount passed in + assert_eq!( + Err(ProgramError::InvalidArgument), + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "").unwrap(), + vec![&mut mint_account], + ) + ); + assert_eq!( + Err(ProgramError::InvalidArgument), + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, ".").unwrap(), + vec![&mut mint_account], + ) + ); + assert_eq!( + Err(ProgramError::InvalidArgument), + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "0.111").unwrap(), + vec![&mut mint_account], + ) + ); + assert_eq!( + Err(ProgramError::InvalidArgument), + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "0.t").unwrap(), + vec![&mut mint_account], + ) + ); +}