diff --git a/token/program/src/error.rs b/token/program/src/error.rs index 4a9e2236..904dc212 100644 --- a/token/program/src/error.rs +++ b/token/program/src/error.rs @@ -13,6 +13,9 @@ pub enum TokenError { /// Insufficient funds for the operation requested. #[error("Insufficient funds")] InsufficientFunds, + /// Invalid Mint. + #[error("Invalid Mint")] + InvalidMint, /// Account not associated with this Mint. #[error("Account not associated with this Mint")] MintMismatch, diff --git a/token/program/src/instruction.rs b/token/program/src/instruction.rs index 01656008..701f4bcd 100644 --- a/token/program/src/instruction.rs +++ b/token/program/src/instruction.rs @@ -37,8 +37,10 @@ pub enum TokenInstruction { /// The freeze authority/multisignature of the mint. freeze_authority: COption, }, - /// Initializes a new account to hold tokens. If this account is associated with the native mint - /// then the token balance of the initialized account will be equal to the amount of SOL in the account. + /// Initializes a new account to hold tokens. If this account is associated with the native + /// mint then the token balance of the initialized account will be equal to the amount of SOL + /// in the account. If this account is associated with another mint, that mint must be + /// initialized before this command can succeed. /// /// The `InitializeAccount` instruction requires no signers and MUST be included within /// the same Transaction as the system program's `CreateInstruction` that creates the account diff --git a/token/program/src/processor.rs b/token/program/src/processor.rs index e6dacf0f..ae390501 100644 --- a/token/program/src/processor.rs +++ b/token/program/src/processor.rs @@ -72,6 +72,12 @@ impl Processor { return Err(TokenError::NotRentExempt.into()); } + if *mint_info.key != crate::native_mint::id() { + let mut mint_info_data = mint_info.data.borrow_mut(); + let _: &mut Mint = state::unpack(&mut mint_info_data) + .map_err(|_| Into::::into(TokenError::InvalidMint))?; + } + account.mint = *mint_info.key; account.owner = *owner_info.key; account.delegate = COption::None; @@ -674,6 +680,7 @@ impl PrintProgramError for TokenError { info!("Error: Lamport balance below rent-exempt threshold") } TokenError::InsufficientFunds => info!("Error: insufficient funds"), + TokenError::InvalidMint => info!("Error: Invalid Mint"), TokenError::MintMismatch => info!("Error: Account not associated with this Mint"), TokenError::OwnerMismatch => info!("Error: owner does not match"), TokenError::FixedSupply => info!("Error: the total supply of this token is fixed"), @@ -765,7 +772,7 @@ mod tests { } #[test] - #[should_panic(expected = "Custom(2)")] + #[should_panic(expected = "Custom(3)")] fn test_error_unwrap() { Err::<(), ProgramError>(return_token_error_as_program_error()).unwrap(); } @@ -836,7 +843,8 @@ mod tests { let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mut rent_sysvar = rent_sysvar(); // account is not rent exempt @@ -855,6 +863,27 @@ mod tests { 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(); + // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), @@ -907,10 +936,15 @@ mod tests { let mut mint_account = SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mint2_key = pubkey_rand(); - let mut mint2_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &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(), @@ -949,22 +983,19 @@ mod tests { // create mismatch account do_process_instruction( - initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), + initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(), vec![ &mut mismatch_account, - &mut mint2_account, + &mut mint_account, &mut owner_account, &mut rent_sysvar, ], ) .unwrap(); + let account: &mut Account = state::unpack(&mut mismatch_account.data).unwrap(); + account.mint = mint2_key; - // create new mint & mint to account - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_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], @@ -1286,18 +1317,6 @@ mod tests { SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mut rent_sysvar = rent_sysvar(); - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut owner_account, - &mut mint_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - // create mint-able token with zero supply let decimals = 2; do_process_instruction( @@ -1317,6 +1336,18 @@ mod tests { } ); + // 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(), @@ -1349,13 +1380,20 @@ mod tests { SolanaAccount::new(mint_minimum_balance(), size_of::(), &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 owner_account, &mut mint_account, + &mut owner_account, &mut rent_sysvar, ], ) @@ -1366,19 +1404,14 @@ mod tests { initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), vec![ &mut account2_account, - &mut owner_account, &mut mint_account, + &mut owner_account, &mut rent_sysvar, ], ) .unwrap(); - // create new mint & mint to account - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_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], @@ -1478,6 +1511,20 @@ mod tests { SolanaAccount::new(mint_minimum_balance(), size_of::(), &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(TokenError::UninitializedState.into()), @@ -1631,13 +1678,6 @@ mod tests { ) .unwrap(); - // 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(); - // wrong owner assert_eq!( Err(TokenError::OwnerMismatch.into()), @@ -1735,13 +1775,6 @@ mod tests { ) ); - // 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(); - // set freeze_authority do_process_instruction( set_authority( @@ -1812,13 +1845,18 @@ mod tests { let mut mint_account = SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mint2_key = pubkey_rand(); - let mut mint2_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let uninitialized_key = pubkey_rand(); let mut uninitialized_account = SolanaAccount::new(account_minimum_balance(), size_of::(), &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(), @@ -1857,22 +1895,17 @@ mod tests { // create mismatch account do_process_instruction( - initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), + initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(), vec![ &mut mismatch_account, - &mut mint2_account, + &mut mint_account, &mut owner_account, &mut rent_sysvar, ], ) .unwrap(); - - // 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(); + let account: &mut Account = state::unpack(&mut mismatch_account.data).unwrap(); + account.mint = mint2_key; // mint to do_process_instruction( @@ -2001,10 +2034,15 @@ mod tests { let mut mint_account = SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mint2_key = pubkey_rand(); - let mut mint2_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); 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(), @@ -2043,22 +2081,19 @@ mod tests { // create mismatch account do_process_instruction( - initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), + initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(), vec![ &mut mismatch_account, - &mut mint2_account, + &mut mint_account, &mut owner_account, &mut rent_sysvar, ], ) .unwrap(); + let account: &mut Account = state::unpack(&mut mismatch_account.data).unwrap(); + account.mint = mint2_key; - // 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(); + // 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], @@ -2286,6 +2321,13 @@ mod tests { ) .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(), @@ -2316,12 +2358,7 @@ mod tests { ) .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(); + // mint to account let account_info_iter = &mut signer_accounts.iter_mut(); do_process_instruction( mint_to( @@ -2495,16 +2532,6 @@ mod tests { let mint2_key = pubkey_rand(); let mut mint2_account = SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); - 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(); do_process_instruction( initialize_mint( &program_id, @@ -2517,6 +2544,16 @@ mod tests { 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( @@ -2758,6 +2795,11 @@ mod tests { ); // 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![ @@ -2768,11 +2810,6 @@ mod tests { ], ) .unwrap(); - 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( mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(), vec![ @@ -3131,6 +3168,13 @@ mod tests { SolanaAccount::new(mint_minimum_balance(), size_of::(), &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(), @@ -3155,13 +3199,6 @@ mod tests { ) .unwrap(); - // 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(); - // mint the max to an account do_process_instruction( mint_to( @@ -3297,6 +3334,13 @@ mod tests { SolanaAccount::new(mint_minimum_balance(), size_of::(), &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(), @@ -3321,12 +3365,7 @@ mod tests { ) .unwrap(); - // 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(); + // 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], @@ -3471,6 +3510,13 @@ mod tests { SolanaAccount::new(mint_minimum_balance(), size_of::(), &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(), @@ -3483,12 +3529,7 @@ mod tests { ) .unwrap(); - // 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(); + // 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],