|
@@ -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<RwLock<Vec<u8>>> = Arc::new(RwLock::new(Vec::new()));
|
|
|
+}
|
|
|
+
|
|
|
+fn set_expected_data(expected_data: Vec<u8>) {
|
|
|
+ *EXPECTED_DATA.write().unwrap() = expected_data;
|
|
|
+}
|
|
|
+
|
|
|
+struct SyscallStubs {}
|
|
|
+impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
|
|
|
+ fn sol_log(&self, _message: &str) {}
|
|
|
+
|
|
|
+ fn sol_invoke_signed(
|
|
|
+ &self,
|
|
|
+ _instruction: &Instruction,
|
|
|
+ _account_infos: &[AccountInfo],
|
|
|
+ _signers_seeds: &[&[&[u8]]],
|
|
|
+ ) -> ProgramResult {
|
|
|
+ Err(ProgramError::Custom(42)) // 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::<Vec<_>>();
|
|
|
+
|
|
|
+ let account_infos = create_is_signer_account_infos(&mut meta);
|
|
|
+ Processor::process(&instruction.program_id, &account_infos, &instruction.data)
|
|
|
+}
|
|
|
+
|
|
|
+fn do_process_instruction_dups(
|
|
|
+ instruction: Instruction,
|
|
|
+ account_infos: Vec<AccountInfo>,
|
|
|
+) -> ProgramResult {
|
|
|
+ Processor::process(&instruction.program_id, &account_infos, &instruction.data)
|
|
|
+}
|
|
|
+
|
|
|
+fn 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],
|
|
|
+ )
|
|
|
+ );
|
|
|
+}
|