diff --git a/token-swap/program/src/error.rs b/token-swap/program/src/error.rs index 18f1414b..5761456e 100644 --- a/token-swap/program/src/error.rs +++ b/token-swap/program/src/error.rs @@ -8,41 +8,56 @@ use thiserror::Error; #[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)] pub enum SwapError { /// The account cannot be initialized because it is already being used. - #[error("AlreadyInUse")] + #[error("Swap account already in use")] AlreadyInUse, /// The program address provided doesn't match the value generated by the program. - #[error("InvalidProgramAddress")] + #[error("Invalid program address generated from nonce and key")] InvalidProgramAddress, /// The owner of the input isn't set to the program address generated by the program. - #[error("InvalidOwner")] + #[error("Input account owner is not the program address")] InvalidOwner, - /// The deserialization of the Token state returned something besides State::Token. - #[error("ExpectedToken")] - ExpectedToken, - /// The deserialization of the Token state returned something besides State::Account. - #[error("ExpectedAccount")] + /// The owner of the pool token output is set to the program address generated by the program. + #[error("Output pool account owner cannot be the program address")] + InvalidOutputOwner, + /// The deserialization of the account returned something besides State::Mint. + #[error("Deserialized account is not an SPL Token mint")] + ExpectedMint, + /// The deserialization of the account returned something besides State::Account. + #[error("Deserialized account is not an SPL Token account")] ExpectedAccount, - /// The initialized pool had a non zero supply. - #[error("InvalidSupply")] + /// The input token account is empty. + #[error("Input token account empty")] + EmptySupply, + /// The pool token mint has a non-zero supply. + #[error("Pool token mint has a non-zero supply")] InvalidSupply, - /// The initialized token has a delegate. - #[error("InvalidDelegate")] + /// The provided token account has a delegate. + #[error("Token account has a delegate")] InvalidDelegate, - /// The token swap state is invalid. - #[error("InvalidState")] - InvalidState, + /// The swap info is invalid. + #[error("Swap info invalid")] + InvalidSwapInfo, /// The input token is invalid for swap. #[error("InvalidInput")] InvalidInput, + /// Address of the provided swap token account is incorrect. + #[error("Address of the provided swap token account is incorrect")] + IncorrectSwapAccount, + /// Address of the provided pool token mint is incorrect + #[error("Address of the provided pool token mint is incorrect")] + IncorrectPoolMint, /// The output token is invalid for swap. #[error("InvalidOutput")] InvalidOutput, /// The calculation failed. #[error("CalculationFailure")] CalculationFailure, - /// Invalid instruction number passed in + /// Invalid instruction number passed in. #[error("Invalid instruction")] InvalidInstruction, + /// Swap input token accounts have the same mint + #[error("Swap input token accounts have the same mint")] + RepeatedMint, } impl From for ProgramError { fn from(e: SwapError) -> Self { diff --git a/token-swap/program/src/instruction.rs b/token-swap/program/src/instruction.rs index 8eb28f98..a01ec39c 100644 --- a/token-swap/program/src/instruction.rs +++ b/token-swap/program/src/instruction.rs @@ -21,8 +21,8 @@ pub enum SwapInstruction { /// 1. `[]` $authority derived from `create_program_address(&[Token-swap account])` /// 2. `[]` token_a Account. Must be non zero, owned by $authority. /// 3. `[]` token_b Account. Must be non zero, owned by $authority. - /// 4. `[writable]` pool Token. Must be empty, owned by $authority. - /// 5. `[writable]` Pool Account to deposit the generated tokens, user is the owner. + /// 4. `[writable]` Pool Token Mint. Must be empty, owned by $authority. + /// 5. `[writable]` Pool Token Account to deposit the minted tokens. Must be empty, owned by user. /// 6. '[]` Token program id Initialize { /// swap pool fee numerator diff --git a/token-swap/program/src/processor.rs b/token-swap/program/src/processor.rs index 68553f4a..700d7d3d 100644 --- a/token-swap/program/src/processor.rs +++ b/token-swap/program/src/processor.rs @@ -21,7 +21,7 @@ use solana_sdk::{ program_error::ProgramError, pubkey::Pubkey, }; -use spl_token::pack::Pack; +use spl_token::{option::COption, pack::Pack}; // Test program id for the swap program. #[cfg(not(target_arch = "bpf"))] @@ -40,7 +40,7 @@ impl Processor { /// Unpacks a spl_token `Mint`. pub fn unpack_mint(data: &[u8]) -> Result { - spl_token::state::Mint::unpack(data).map_err(|_| SwapError::ExpectedAccount) + spl_token::state::Mint::unpack(data).map_err(|_| SwapError::ExpectedMint) } /// Calculates the authority id by generating a program address. @@ -150,7 +150,7 @@ impl Processor { let token_a_info = next_account_info(account_info_iter)?; let token_b_info = next_account_info(account_info_iter)?; let pool_info = next_account_info(account_info_iter)?; - let user_destination_info = next_account_info(account_info_iter)?; + let destination_info = next_account_info(account_info_iter)?; let token_program_info = next_account_info(account_info_iter)?; let token_swap = SwapInfo::unpack_unchecked(&swap_info.data.borrow())?; @@ -163,6 +163,7 @@ impl Processor { } let token_a = Self::unpack_token_account(&token_a_info.data.borrow())?; let token_b = Self::unpack_token_account(&token_b_info.data.borrow())?; + let destination = Self::unpack_token_account(&destination_info.data.borrow())?; let pool_mint = Self::unpack_mint(&pool_info.data.borrow())?; if *authority_info.key != token_a.owner { return Err(SwapError::InvalidOwner.into()); @@ -170,14 +171,20 @@ impl Processor { if *authority_info.key != token_b.owner { return Err(SwapError::InvalidOwner.into()); } - if spl_token::option::COption::Some(*authority_info.key) != pool_mint.mint_authority { + if *authority_info.key == destination.owner { + return Err(SwapError::InvalidOutputOwner.into()); + } + if COption::Some(*authority_info.key) != pool_mint.mint_authority { return Err(SwapError::InvalidOwner.into()); } + if token_a.mint == token_b.mint { + return Err(SwapError::RepeatedMint.into()); + } if token_b.amount == 0 { - return Err(SwapError::InvalidSupply.into()); + return Err(SwapError::EmptySupply.into()); } if token_a.amount == 0 { - return Err(SwapError::InvalidSupply.into()); + return Err(SwapError::EmptySupply.into()); } if token_a.delegate.is_some() { return Err(SwapError::InvalidDelegate.into()); @@ -185,6 +192,9 @@ impl Processor { if token_b.delegate.is_some() { return Err(SwapError::InvalidDelegate.into()); } + if pool_mint.supply != 0 { + return Err(SwapError::InvalidSupply.into()); + } // liquidity is measured in terms of token_a's value since both sides of // the pool are equal @@ -193,7 +203,7 @@ impl Processor { swap_info.key, token_program_info.clone(), pool_info.clone(), - user_destination_info.clone(), + destination_info.clone(), authority_info.clone(), nonce, amount, @@ -235,12 +245,12 @@ impl Processor { if !(*swap_source_info.key == token_swap.token_a || *swap_source_info.key == token_swap.token_b) { - return Err(SwapError::InvalidInput.into()); + return Err(SwapError::IncorrectSwapAccount.into()); } if !(*swap_destination_info.key == token_swap.token_a || *swap_destination_info.key == token_swap.token_b) { - return Err(SwapError::InvalidOutput.into()); + return Err(SwapError::IncorrectSwapAccount.into()); } if *swap_source_info.key == *swap_destination_info.key { return Err(SwapError::InvalidInput.into()); @@ -312,14 +322,15 @@ impl Processor { return Err(SwapError::InvalidProgramAddress.into()); } if *token_a_info.key != token_swap.token_a { - return Err(SwapError::InvalidInput.into()); + return Err(SwapError::IncorrectSwapAccount.into()); } if *token_b_info.key != token_swap.token_b { - return Err(SwapError::InvalidInput.into()); + return Err(SwapError::IncorrectSwapAccount.into()); } if *pool_info.key != token_swap.pool_mint { - return Err(SwapError::InvalidInput.into()); + return Err(SwapError::IncorrectPoolMint.into()); } + let token_a = Self::unpack_token_account(&token_a_info.data.borrow())?; let token_b = Self::unpack_token_account(&token_b_info.data.borrow())?; @@ -390,10 +401,13 @@ impl Processor { return Err(SwapError::InvalidProgramAddress.into()); } if *token_a_info.key != token_swap.token_a { - return Err(SwapError::InvalidInput.into()); + return Err(SwapError::IncorrectSwapAccount.into()); } if *token_b_info.key != token_swap.token_b { - return Err(SwapError::InvalidInput.into()); + return Err(SwapError::IncorrectSwapAccount.into()); + } + if *pool_mint_info.key != token_swap.pool_mint { + return Err(SwapError::IncorrectPoolMint.into()); } let token_a = Self::unpack_token_account(&token_a_info.data.borrow())?; @@ -517,15 +531,34 @@ impl PrintProgramError for SwapError { E: 'static + std::error::Error + DecodeError + PrintProgramError + FromPrimitive, { match self { - SwapError::AlreadyInUse => info!("Error: AlreadyInUse"), - SwapError::InvalidProgramAddress => info!("Error: InvalidProgramAddress"), - SwapError::InvalidOwner => info!("Error: InvalidOwner"), - SwapError::ExpectedToken => info!("Error: ExpectedToken"), - SwapError::ExpectedAccount => info!("Error: ExpectedAccount"), - SwapError::InvalidSupply => info!("Error: InvalidSupply"), - SwapError::InvalidDelegate => info!("Error: InvalidDelegate"), - SwapError::InvalidState => info!("Error: InvalidState"), + SwapError::AlreadyInUse => info!("Error: Swap account already in use"), + SwapError::InvalidProgramAddress => { + info!("Error: Invalid program address generated from nonce and key") + } + SwapError::InvalidOwner => { + info!("Error: The input account owner is not the program address") + } + SwapError::InvalidOutputOwner => { + info!("Error: Output pool account owner cannot be the program address") + } + SwapError::ExpectedMint => { + info!("Error: Deserialized account is not an SPL Token mint") + } + SwapError::ExpectedAccount => { + info!("Error: Deserialized account is not an SPL Token account") + } + SwapError::EmptySupply => info!("Error: Input token account empty"), + SwapError::InvalidSupply => info!("Error: Pool token mint has a non-zero supply"), + SwapError::RepeatedMint => info!("Error: Swap input token accounts have the same mint"), + SwapError::InvalidDelegate => info!("Error: Token account has a delegate"), + SwapError::InvalidSwapInfo => info!("Error: Swap info invalid"), SwapError::InvalidInput => info!("Error: InvalidInput"), + SwapError::IncorrectSwapAccount => { + info!("Error: Address of the provided swap token account is incorrect") + } + SwapError::IncorrectPoolMint => { + info!("Error: Address of the provided pool token mint is incorrect") + } SwapError::InvalidOutput => info!("Error: InvalidOutput"), SwapError::CalculationFailure => info!("Error: CalculationFailure"), SwapError::InvalidInstruction => info!("Error: InvalidInstruction"), @@ -549,7 +582,8 @@ mod tests { rent::Rent, sysvar::rent, }; use spl_token::{ - instruction::{initialize_account, initialize_mint, mint_to}, + error::TokenError, + instruction::{approve, initialize_account, initialize_mint, mint_to, revoke}, pack::Pack, processor::Processor as SplProcessor, state::{Account as SplAccount, Mint as SplMint}, @@ -558,6 +592,9 @@ mod tests { struct SwapAccountInfo { nonce: u8, + authority_key: Pubkey, + fee_numerator: u64, + fee_denominator: u64, swap_key: Pubkey, swap_account: Account, pool_mint_key: Pubkey, @@ -574,6 +611,364 @@ mod tests { token_b_mint_account: Account, } + impl SwapAccountInfo { + pub fn new( + user_key: &Pubkey, + fee_numerator: u64, + fee_denominator: u64, + token_a_amount: u64, + token_b_amount: u64, + ) -> Self { + let swap_key = pubkey_rand(); + let swap_account = Account::new(0, size_of::(), &SWAP_PROGRAM_ID); + let (authority_key, nonce) = + Pubkey::find_program_address(&[&swap_key.to_bytes()[..]], &SWAP_PROGRAM_ID); + + let (pool_mint_key, mut pool_mint_account) = + create_mint(&TOKEN_PROGRAM_ID, &authority_key); + let (pool_token_key, pool_token_account) = mint_token( + &TOKEN_PROGRAM_ID, + &pool_mint_key, + &mut pool_mint_account, + &authority_key, + &user_key, + 0, + ); + let (token_a_mint_key, mut token_a_mint_account) = + create_mint(&TOKEN_PROGRAM_ID, &user_key); + let (token_a_key, token_a_account) = mint_token( + &TOKEN_PROGRAM_ID, + &token_a_mint_key, + &mut token_a_mint_account, + &user_key, + &authority_key, + token_a_amount, + ); + let (token_b_mint_key, mut token_b_mint_account) = + create_mint(&TOKEN_PROGRAM_ID, &user_key); + let (token_b_key, token_b_account) = mint_token( + &TOKEN_PROGRAM_ID, + &token_b_mint_key, + &mut token_b_mint_account, + &user_key, + &authority_key, + token_b_amount, + ); + + SwapAccountInfo { + nonce, + authority_key, + fee_numerator, + fee_denominator, + swap_key, + swap_account, + pool_mint_key, + pool_mint_account, + pool_token_key, + pool_token_account, + token_a_key, + token_a_account, + token_a_mint_key, + token_a_mint_account, + token_b_key, + token_b_account, + token_b_mint_key, + token_b_mint_account, + } + } + + pub fn initialize_swap(&mut self) -> ProgramResult { + do_process_instruction( + initialize( + &SWAP_PROGRAM_ID, + &TOKEN_PROGRAM_ID, + &self.swap_key, + &self.authority_key, + &self.token_a_key, + &self.token_b_key, + &self.pool_mint_key, + &self.pool_token_key, + self.nonce, + self.fee_numerator, + self.fee_denominator, + ) + .unwrap(), + vec![ + &mut self.swap_account, + &mut Account::default(), + &mut self.token_a_account, + &mut self.token_b_account, + &mut self.pool_mint_account, + &mut self.pool_token_account, + &mut Account::default(), + ], + ) + } + + pub fn setup_token_accounts( + &mut self, + mint_owner: &Pubkey, + account_owner: &Pubkey, + a_amount: u64, + b_amount: u64, + pool_amount: u64, + ) -> (Pubkey, Account, Pubkey, Account, Pubkey, Account) { + let (token_a_key, token_a_account) = mint_token( + &TOKEN_PROGRAM_ID, + &self.token_a_mint_key, + &mut self.token_a_mint_account, + &mint_owner, + &account_owner, + a_amount, + ); + let (token_b_key, token_b_account) = mint_token( + &TOKEN_PROGRAM_ID, + &self.token_b_mint_key, + &mut self.token_b_mint_account, + &mint_owner, + &account_owner, + b_amount, + ); + let (pool_key, pool_account) = mint_token( + &TOKEN_PROGRAM_ID, + &self.pool_mint_key, + &mut self.pool_mint_account, + &self.authority_key, + &account_owner, + pool_amount, + ); + ( + token_a_key, + token_a_account, + token_b_key, + token_b_account, + pool_key, + pool_account, + ) + } + + fn get_token_account(&self, account_key: &Pubkey) -> &Account { + if *account_key == self.token_a_key { + return &self.token_a_account; + } else if *account_key == self.token_b_key { + return &self.token_b_account; + } + panic!("Could not find matching swap token account"); + } + + fn set_token_account(&mut self, account_key: &Pubkey, account: Account) { + if *account_key == self.token_a_key { + self.token_a_account = account; + return; + } else if *account_key == self.token_b_key { + self.token_b_account = account; + return; + } + panic!("Could not find matching swap token account"); + } + + pub fn swap( + &mut self, + user_key: &Pubkey, + user_source_key: &Pubkey, + mut user_source_account: &mut Account, + swap_source_key: &Pubkey, + swap_destination_key: &Pubkey, + user_destination_key: &Pubkey, + mut user_destination_account: &mut Account, + amount: u64, + ) -> ProgramResult { + // approve moving from user source account + do_process_instruction( + approve( + &TOKEN_PROGRAM_ID, + &user_source_key, + &self.authority_key, + &user_key, + &[], + amount, + ) + .unwrap(), + vec![ + &mut user_source_account, + &mut Account::default(), + &mut Account::default(), + ], + ) + .unwrap(); + + let mut swap_source_account = self.get_token_account(swap_source_key).clone(); + let mut swap_destination_account = self.get_token_account(swap_destination_key).clone(); + + // perform the swap + do_process_instruction( + swap( + &SWAP_PROGRAM_ID, + &TOKEN_PROGRAM_ID, + &self.swap_key, + &self.authority_key, + &user_source_key, + &swap_source_key, + &swap_destination_key, + &user_destination_key, + amount, + ) + .unwrap(), + vec![ + &mut self.swap_account, + &mut Account::default(), + &mut user_source_account, + &mut swap_source_account, + &mut swap_destination_account, + &mut user_destination_account, + &mut Account::default(), + ], + )?; + + self.set_token_account(swap_source_key, swap_source_account); + self.set_token_account(swap_destination_key, swap_destination_account); + + Ok(()) + } + + pub fn deposit( + &mut self, + depositor_key: &Pubkey, + depositor_token_a_key: &Pubkey, + mut depositor_token_a_account: &mut Account, + depositor_token_b_key: &Pubkey, + mut depositor_token_b_account: &mut Account, + depositor_pool_key: &Pubkey, + mut depositor_pool_account: &mut Account, + amount_a: u64, + amount_b: u64, + ) -> ProgramResult { + do_process_instruction( + approve( + &TOKEN_PROGRAM_ID, + &depositor_token_a_key, + &self.authority_key, + &depositor_key, + &[], + amount_a, + ) + .unwrap(), + vec![ + &mut depositor_token_a_account, + &mut Account::default(), + &mut Account::default(), + ], + ) + .unwrap(); + + do_process_instruction( + approve( + &TOKEN_PROGRAM_ID, + &depositor_token_b_key, + &self.authority_key, + &depositor_key, + &[], + amount_b, + ) + .unwrap(), + vec![ + &mut depositor_token_b_account, + &mut Account::default(), + &mut Account::default(), + ], + ) + .unwrap(); + + do_process_instruction( + deposit( + &SWAP_PROGRAM_ID, + &TOKEN_PROGRAM_ID, + &self.swap_key, + &self.authority_key, + &depositor_token_a_key, + &depositor_token_b_key, + &self.token_a_key, + &self.token_b_key, + &self.pool_mint_key, + &depositor_pool_key, + amount_a, + ) + .unwrap(), + vec![ + &mut self.swap_account, + &mut Account::default(), + &mut depositor_token_a_account, + &mut depositor_token_b_account, + &mut self.token_a_account, + &mut self.token_b_account, + &mut self.pool_mint_account, + &mut depositor_pool_account, + &mut Account::default(), + ], + ) + } + + pub fn withdraw( + &mut self, + user_key: &Pubkey, + pool_key: &Pubkey, + mut pool_account: &mut Account, + token_a_key: &Pubkey, + mut token_a_account: &mut Account, + token_b_key: &Pubkey, + mut token_b_account: &mut Account, + amount: u64, + ) -> ProgramResult { + // approve swap program to take out pool tokens + do_process_instruction( + approve( + &TOKEN_PROGRAM_ID, + &pool_key, + &self.authority_key, + &user_key, + &[], + amount, + ) + .unwrap(), + vec![ + &mut pool_account, + &mut Account::default(), + &mut Account::default(), + ], + ) + .unwrap(); + + // withraw token a and b correctly + do_process_instruction( + withdraw( + &SWAP_PROGRAM_ID, + &TOKEN_PROGRAM_ID, + &self.swap_key, + &self.authority_key, + &self.pool_mint_key, + &pool_key, + &self.token_a_key, + &self.token_b_key, + &token_a_key, + &token_b_key, + amount, + ) + .unwrap(), + vec![ + &mut self.swap_account, + &mut Account::default(), + &mut self.pool_mint_account, + &mut pool_account, + &mut self.token_a_account, + &mut self.token_b_account, + &mut token_a_account, + &mut token_b_account, + &mut Account::default(), + ], + ) + } + } + fn mint_minimum_balance() -> u64 { Rent::default().minimum_balance(SplMint::get_packed_len()) } @@ -590,26 +985,49 @@ mod tests { instruction: Instruction, accounts: Vec<&mut Account>, ) -> ProgramResult { + // approximate the logic in the actual runtime which runs the instruction + // and only updates accounts if the instruction is successful + let mut account_clones = accounts.iter().map(|x| (*x).clone()).collect::>(); let mut meta = instruction .accounts .iter() - .zip(accounts) + .zip(account_clones.iter_mut()) .map(|(account_meta, account)| (&account_meta.pubkey, account_meta.is_signer, account)) .collect::>(); - - let account_infos = create_is_signer_account_infos(&mut meta); - if instruction.program_id == SWAP_PROGRAM_ID { + let mut account_infos = create_is_signer_account_infos(&mut meta); + let res = if instruction.program_id == SWAP_PROGRAM_ID { Processor::process(&instruction.program_id, &account_infos, &instruction.data) } else { SplProcessor::process(&instruction.program_id, &account_infos, &instruction.data) + }; + + if res.is_ok() { + let mut account_metas = instruction + .accounts + .iter() + .zip(accounts) + .map(|(account_meta, account)| (&account_meta.pubkey, account)) + .collect::>(); + for account_info in account_infos.iter_mut() { + for account_meta in account_metas.iter_mut() { + if account_info.key == account_meta.0 { + let account = &mut account_meta.1; + account.owner = *account_info.owner; + account.lamports = **account_info.lamports.borrow(); + account.data = account_info.data.borrow().to_vec(); + } + } + } } + res } fn mint_token( program_id: &Pubkey, mint_key: &Pubkey, mut mint_account: &mut Account, - authority_key: &Pubkey, + mint_authority_key: &Pubkey, + account_owner_key: &Pubkey, amount: u64, ) -> (Pubkey, Account) { let account_key = pubkey_rand(); @@ -618,38 +1036,39 @@ mod tests { SplAccount::get_packed_len(), &program_id, ); - let mut authority_account = Account::default(); + let mut mint_authority_account = Account::default(); let mut rent_sysvar_account = rent::create_account(1, &Rent::free()); - // create account do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, authority_key).unwrap(), + initialize_account(&program_id, &account_key, &mint_key, account_owner_key).unwrap(), vec![ &mut account_account, &mut mint_account, - &mut authority_account, + &mut mint_authority_account, &mut rent_sysvar_account, ], ) .unwrap(); - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account_key, - &authority_key, - &[], - amount, + if amount > 0 { + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account_key, + &mint_authority_key, + &[], + amount, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account_account, + &mut mint_authority_account, + ], ) - .unwrap(), - vec![ - &mut mint_account, - &mut account_account, - &mut authority_account, - ], - ) - .unwrap(); + .unwrap(); + } (account_key, account_account) } @@ -663,7 +1082,6 @@ mod tests { ); let mut rent_sysvar_account = rent::create_account(1, &Rent::free()); - // create token mint do_process_instruction( initialize_mint(&program_id, &mint_key, authority_key, None, 2).unwrap(), vec![&mut mint_account, &mut rent_sysvar_account], @@ -685,7 +1103,7 @@ mod tests { let swap_bytes = swap_key.to_bytes(); let authority_signature_seeds = [&swap_bytes[..32], &[nonce]]; let signers = &[&authority_signature_seeds[..]]; - let ix = spl_token::instruction::mint_to( + let ix = mint_to( &token_program.0, &mint.0, &destination.0, @@ -702,415 +1120,1595 @@ mod tests { assert_eq!(err, ProgramError::InvalidAccountData); } - fn initialize_swap<'a>( - fee_numerator: u64, - fee_denominator: u64, - token_a_amount: u64, - token_b_amount: u64, - ) -> SwapAccountInfo { - let swap_key = pubkey_rand(); - let mut swap_account = Account::new(0, size_of::(), &SWAP_PROGRAM_ID); - let (authority_key, nonce) = - Pubkey::find_program_address(&[&swap_key.to_bytes()[..]], &SWAP_PROGRAM_ID); - - let (pool_mint_key, mut pool_mint_account) = create_mint(&TOKEN_PROGRAM_ID, &authority_key); - let (pool_token_key, mut pool_token_account) = mint_token( - &TOKEN_PROGRAM_ID, - &pool_mint_key, - &mut pool_mint_account, - &authority_key, - 0, - ); - let (token_a_mint_key, mut token_a_mint_account) = - create_mint(&TOKEN_PROGRAM_ID, &authority_key); - let (token_a_key, mut token_a_account) = mint_token( - &TOKEN_PROGRAM_ID, - &token_a_mint_key, - &mut token_a_mint_account, - &authority_key, - token_a_amount, - ); - let (token_b_mint_key, mut token_b_mint_account) = - create_mint(&TOKEN_PROGRAM_ID, &authority_key); - let (token_b_key, mut token_b_account) = mint_token( - &TOKEN_PROGRAM_ID, - &token_b_mint_key, - &mut token_b_mint_account, - &authority_key, - token_b_amount, - ); - - let mut authority_account = Account::default(); - do_process_instruction( - initialize( - &SWAP_PROGRAM_ID, - &TOKEN_PROGRAM_ID, - &swap_key, - &authority_key, - &token_a_key, - &token_b_key, - &pool_mint_key, - &pool_token_key, - nonce, - fee_numerator, - fee_denominator, - ) - .unwrap(), - vec![ - &mut swap_account, - &mut authority_account, - &mut token_a_account, - &mut token_b_account, - &mut pool_mint_account, - &mut pool_token_account, - &mut Account::default(), - ], - ) - .unwrap(); - SwapAccountInfo { - nonce, - swap_key, - swap_account, - pool_mint_key, - pool_mint_account, - pool_token_key, - pool_token_account, - token_a_key, - token_a_account, - token_a_mint_key, - token_a_mint_account, - token_b_key, - token_b_account, - token_b_mint_key, - token_b_mint_account, - } - } - #[test] fn test_initialize() { + let user_key = pubkey_rand(); let fee_numerator = 1; let fee_denominator = 2; let token_a_amount = 1000; let token_b_amount = 2000; - let swap_accounts = initialize_swap( + let pool_token_amount = 10; + + let mut accounts = SwapAccountInfo::new( + &user_key, fee_numerator, fee_denominator, token_a_amount, token_b_amount, ); - let swap_info = SwapInfo::unpack(&swap_accounts.swap_account.data).unwrap(); + + // wrong nonce for authority_key + { + let old_nonce = accounts.nonce; + accounts.nonce = old_nonce - 1; + assert_eq!( + Err(SwapError::InvalidProgramAddress.into()), + accounts.initialize_swap() + ); + accounts.nonce = old_nonce; + } + + // uninitialized token a account + { + let old_account = accounts.token_a_account; + accounts.token_a_account = Account::default(); + assert_eq!( + Err(SwapError::ExpectedAccount.into()), + accounts.initialize_swap() + ); + accounts.token_a_account = old_account; + } + + // uninitialized token b account + { + let old_account = accounts.token_b_account; + accounts.token_b_account = Account::default(); + assert_eq!( + Err(SwapError::ExpectedAccount.into()), + accounts.initialize_swap() + ); + accounts.token_b_account = old_account; + } + + // uninitialized pool mint + { + let old_account = accounts.pool_mint_account; + accounts.pool_mint_account = Account::default(); + assert_eq!( + Err(SwapError::ExpectedMint.into()), + accounts.initialize_swap() + ); + accounts.pool_mint_account = old_account; + } + + // token A account owner is not swap authority + { + let (_token_a_key, token_a_account) = mint_token( + &TOKEN_PROGRAM_ID, + &accounts.token_a_mint_key, + &mut accounts.token_a_mint_account, + &user_key, + &user_key, + 0, + ); + let old_account = accounts.token_a_account; + accounts.token_a_account = token_a_account; + assert_eq!( + Err(SwapError::InvalidOwner.into()), + accounts.initialize_swap() + ); + accounts.token_a_account = old_account; + } + + // token B account owner is not swap authority + { + let (_token_b_key, token_b_account) = mint_token( + &TOKEN_PROGRAM_ID, + &accounts.token_b_mint_key, + &mut accounts.token_b_mint_account, + &user_key, + &user_key, + 0, + ); + let old_account = accounts.token_b_account; + accounts.token_b_account = token_b_account; + assert_eq!( + Err(SwapError::InvalidOwner.into()), + accounts.initialize_swap() + ); + accounts.token_b_account = old_account; + } + + // pool token account owner is swap authority + { + let (_pool_token_key, pool_token_account) = mint_token( + &TOKEN_PROGRAM_ID, + &accounts.pool_mint_key, + &mut accounts.pool_mint_account, + &accounts.authority_key, + &accounts.authority_key, + 0, + ); + let old_account = accounts.pool_token_account; + accounts.pool_token_account = pool_token_account; + assert_eq!( + Err(SwapError::InvalidOutputOwner.into()), + accounts.initialize_swap() + ); + accounts.pool_token_account = old_account; + } + + // pool mint authority is not swap authority + { + let (_pool_mint_key, pool_mint_account) = create_mint(&TOKEN_PROGRAM_ID, &user_key); + let old_mint = accounts.pool_mint_account; + accounts.pool_mint_account = pool_mint_account; + assert_eq!( + Err(SwapError::InvalidOwner.into()), + accounts.initialize_swap() + ); + accounts.pool_mint_account = old_mint; + } + + // empty token A account + { + let (_token_a_key, token_a_account) = mint_token( + &TOKEN_PROGRAM_ID, + &accounts.token_a_mint_key, + &mut accounts.token_a_mint_account, + &user_key, + &accounts.authority_key, + 0, + ); + let old_account = accounts.token_a_account; + accounts.token_a_account = token_a_account; + assert_eq!( + Err(SwapError::EmptySupply.into()), + accounts.initialize_swap() + ); + accounts.token_a_account = old_account; + } + + // empty token B account + { + let (_token_b_key, token_b_account) = mint_token( + &TOKEN_PROGRAM_ID, + &accounts.token_b_mint_key, + &mut accounts.token_b_mint_account, + &user_key, + &accounts.authority_key, + 0, + ); + let old_account = accounts.token_b_account; + accounts.token_b_account = token_b_account; + assert_eq!( + Err(SwapError::EmptySupply.into()), + accounts.initialize_swap() + ); + accounts.token_b_account = old_account; + } + + // invalid pool tokens + { + let old_mint = accounts.pool_mint_account; + let old_pool_account = accounts.pool_token_account; + + let (_pool_mint_key, pool_mint_account) = + create_mint(&TOKEN_PROGRAM_ID, &accounts.authority_key); + accounts.pool_mint_account = pool_mint_account; + + let (_empty_pool_token_key, empty_pool_token_account) = mint_token( + &TOKEN_PROGRAM_ID, + &accounts.pool_mint_key, + &mut accounts.pool_mint_account, + &accounts.authority_key, + &user_key, + 0, + ); + + let (_pool_token_key, pool_token_account) = mint_token( + &TOKEN_PROGRAM_ID, + &accounts.pool_mint_key, + &mut accounts.pool_mint_account, + &accounts.authority_key, + &user_key, + pool_token_amount, + ); + + // non-empty pool token account + accounts.pool_token_account = pool_token_account; + assert_eq!( + Err(SwapError::InvalidSupply.into()), + accounts.initialize_swap() + ); + + // pool tokens already in circulation + accounts.pool_token_account = empty_pool_token_account; + assert_eq!( + Err(SwapError::InvalidSupply.into()), + accounts.initialize_swap() + ); + + accounts.pool_mint_account = old_mint; + accounts.pool_token_account = old_pool_account; + } + + // token A account is delegated + { + do_process_instruction( + approve( + &TOKEN_PROGRAM_ID, + &accounts.token_a_key, + &user_key, + &accounts.authority_key, + &[], + 1, + ) + .unwrap(), + vec![ + &mut accounts.token_a_account, + &mut Account::default(), + &mut Account::default(), + ], + ) + .unwrap(); + assert_eq!( + Err(SwapError::InvalidDelegate.into()), + accounts.initialize_swap() + ); + + do_process_instruction( + revoke( + &TOKEN_PROGRAM_ID, + &accounts.token_a_key, + &accounts.authority_key, + &[], + ) + .unwrap(), + vec![&mut accounts.token_a_account, &mut Account::default()], + ) + .unwrap(); + } + + // token B account is delegated + { + do_process_instruction( + approve( + &TOKEN_PROGRAM_ID, + &accounts.token_b_key, + &user_key, + &accounts.authority_key, + &[], + 1, + ) + .unwrap(), + vec![ + &mut accounts.token_b_account, + &mut Account::default(), + &mut Account::default(), + ], + ) + .unwrap(); + assert_eq!( + Err(SwapError::InvalidDelegate.into()), + accounts.initialize_swap() + ); + + do_process_instruction( + revoke( + &TOKEN_PROGRAM_ID, + &accounts.token_b_key, + &accounts.authority_key, + &[], + ) + .unwrap(), + vec![&mut accounts.token_b_account, &mut Account::default()], + ) + .unwrap(); + } + + // wrong token program id + { + let wrong_program_id = pubkey_rand(); + assert_eq!( + Err(ProgramError::InvalidAccountData), + do_process_instruction( + initialize( + &SWAP_PROGRAM_ID, + &wrong_program_id, + &accounts.swap_key, + &accounts.authority_key, + &accounts.token_a_key, + &accounts.token_b_key, + &accounts.pool_mint_key, + &accounts.pool_token_key, + accounts.nonce, + accounts.fee_numerator, + accounts.fee_denominator, + ) + .unwrap(), + vec![ + &mut accounts.swap_account, + &mut Account::default(), + &mut accounts.token_a_account, + &mut accounts.token_b_account, + &mut accounts.pool_mint_account, + &mut accounts.pool_token_account, + &mut Account::default(), + ], + ) + ); + } + + // create swap with same token A and B + { + let (_token_a_repeat_key, token_a_repeat_account) = mint_token( + &TOKEN_PROGRAM_ID, + &accounts.token_a_mint_key, + &mut accounts.token_a_mint_account, + &user_key, + &accounts.authority_key, + 10, + ); + let old_account = accounts.token_b_account; + accounts.token_b_account = token_a_repeat_account; + assert_eq!( + Err(SwapError::RepeatedMint.into()), + accounts.initialize_swap() + ); + accounts.token_b_account = old_account; + } + + // create valid swap + accounts.initialize_swap().unwrap(); + + // create again + { + assert_eq!( + Err(SwapError::AlreadyInUse.into()), + accounts.initialize_swap() + ); + } + let swap_info = SwapInfo::unpack(&accounts.swap_account.data).unwrap(); assert_eq!(swap_info.is_initialized, true); - assert_eq!(swap_info.nonce, swap_accounts.nonce); - assert_eq!(swap_info.token_a, swap_accounts.token_a_key); - assert_eq!(swap_info.token_b, swap_accounts.token_b_key); - assert_eq!(swap_info.pool_mint, swap_accounts.pool_mint_key); + assert_eq!(swap_info.nonce, accounts.nonce); + assert_eq!(swap_info.token_a, accounts.token_a_key); + assert_eq!(swap_info.token_b, accounts.token_b_key); + assert_eq!(swap_info.pool_mint, accounts.pool_mint_key); assert_eq!(swap_info.fee_denominator, fee_denominator); assert_eq!(swap_info.fee_numerator, fee_numerator); - let token_a = Processor::unpack_token_account(&swap_accounts.token_a_account.data).unwrap(); + let token_a = Processor::unpack_token_account(&accounts.token_a_account.data).unwrap(); assert_eq!(token_a.amount, token_a_amount); - let token_b = Processor::unpack_token_account(&swap_accounts.token_b_account.data).unwrap(); + let token_b = Processor::unpack_token_account(&accounts.token_b_account.data).unwrap(); assert_eq!(token_b.amount, token_b_amount); let pool_account = - Processor::unpack_token_account(&swap_accounts.pool_token_account.data).unwrap(); - let pool_mint = Processor::unpack_mint(&swap_accounts.pool_mint_account.data).unwrap(); + Processor::unpack_token_account(&accounts.pool_token_account.data).unwrap(); + let pool_mint = Processor::unpack_mint(&accounts.pool_mint_account.data).unwrap(); assert_eq!(pool_mint.supply, pool_account.amount); } #[test] fn test_deposit() { + let user_key = pubkey_rand(); + let depositor_key = pubkey_rand(); let fee_numerator = 1; let fee_denominator = 2; let token_a_amount = 1000; let token_b_amount = 8000; - let mut accounts = initialize_swap( + let mut accounts = SwapAccountInfo::new( + &user_key, fee_numerator, fee_denominator, token_a_amount, token_b_amount, ); - let seeds = [&accounts.swap_key.to_bytes()[..32], &[accounts.nonce]]; - let authority_key = Pubkey::create_program_address(&seeds, &SWAP_PROGRAM_ID).unwrap(); - let deposit_a = token_a_amount / 10; - let (depositor_token_a_key, mut depositor_token_a_account) = mint_token( - &TOKEN_PROGRAM_ID, - &accounts.token_a_mint_key, - &mut accounts.token_a_mint_account, - &authority_key, - deposit_a, - ); - let deposit_b = token_b_amount / 10; - let (depositor_token_b_key, mut depositor_token_b_account) = mint_token( - &TOKEN_PROGRAM_ID, - &accounts.token_b_mint_key, - &mut accounts.token_b_mint_account, - &authority_key, - deposit_b, - ); - let initial_pool = 10; - let (depositor_pool_key, mut depositor_pool_account) = mint_token( - &TOKEN_PROGRAM_ID, - &accounts.pool_mint_key, - &mut accounts.pool_mint_account, - &authority_key, - initial_pool, - ); - do_process_instruction( - deposit( - &SWAP_PROGRAM_ID, + let deposit_a = token_a_amount / 10; + let deposit_b = token_b_amount / 10; + + // swap not initialized + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0); + assert_eq!( + Err(SwapError::InvalidSwapInfo.into()), + accounts.deposit( + &depositor_key, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + &pool_key, + &mut pool_account, + deposit_a, + deposit_b, + ) + ); + } + + accounts.initialize_swap().unwrap(); + + // wrong nonce for authority_key + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0); + let old_authority = accounts.authority_key; + let (bad_authority_key, _nonce) = Pubkey::find_program_address( + &[&accounts.swap_key.to_bytes()[..]], &TOKEN_PROGRAM_ID, - &accounts.swap_key, - &authority_key, - &depositor_token_a_key, - &depositor_token_b_key, - &accounts.token_a_key, - &accounts.token_b_key, - &accounts.pool_mint_key, - &depositor_pool_key, + ); + accounts.authority_key = bad_authority_key; + assert_eq!( + Err(SwapError::InvalidProgramAddress.into()), + accounts.deposit( + &depositor_key, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + &pool_key, + &mut pool_account, + deposit_a, + deposit_b, + ) + ); + accounts.authority_key = old_authority; + } + + // not enough token A + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts( + &user_key, + &depositor_key, + deposit_a / 2, + deposit_b, + 0, + ); + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + accounts.deposit( + &depositor_key, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + &pool_key, + &mut pool_account, + deposit_a, + deposit_b, + ) + ); + } + + // not enough token B + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts( + &user_key, + &depositor_key, deposit_a, - ) - .unwrap(), - vec![ - &mut accounts.swap_account, - &mut Account::default(), - &mut depositor_token_a_account, - &mut depositor_token_b_account, - &mut accounts.token_a_account, - &mut accounts.token_b_account, - &mut accounts.pool_mint_account, - &mut depositor_pool_account, - &mut Account::default(), - ], - ) - .unwrap(); - let token_a = Processor::unpack_token_account(&accounts.token_a_account.data).unwrap(); - assert_eq!(token_a.amount, deposit_a + token_a_amount); - let token_b = Processor::unpack_token_account(&accounts.token_b_account.data).unwrap(); - assert_eq!(token_b.amount, deposit_b + token_b_amount); - let depositor_token_a = - Processor::unpack_token_account(&depositor_token_a_account.data).unwrap(); - assert_eq!(depositor_token_a.amount, 0); - let depositor_token_b = - Processor::unpack_token_account(&depositor_token_b_account.data).unwrap(); - assert_eq!(depositor_token_b.amount, 0); - let depositor_pool_account = - Processor::unpack_token_account(&depositor_pool_account.data).unwrap(); - let pool_account = - Processor::unpack_token_account(&accounts.pool_token_account.data).unwrap(); - let pool_mint = Processor::unpack_mint(&accounts.pool_mint_account.data).unwrap(); - assert_eq!( - pool_mint.supply, - pool_account.amount + depositor_pool_account.amount - ); + deposit_b / 2, + 0, + ); + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + accounts.deposit( + &depositor_key, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + &pool_key, + &mut pool_account, + deposit_a, + deposit_b, + ) + ); + } + + // wrong swap token accounts + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0); + assert_eq!( + Err(TokenError::MintMismatch.into()), + accounts.deposit( + &depositor_key, + &token_b_key, + &mut token_b_account, + &token_a_key, + &mut token_a_account, + &pool_key, + &mut pool_account, + deposit_a, + deposit_b, + ) + ); + } + + // wrong pool token account + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + _pool_key, + mut _pool_account, + ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0); + let ( + wrong_token_key, + mut wrong_token_account, + _token_b_key, + mut _token_b_account, + _pool_key, + mut _pool_account, + ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0); + assert_eq!( + Err(TokenError::MintMismatch.into()), + accounts.deposit( + &depositor_key, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + &wrong_token_key, + &mut wrong_token_account, + deposit_a, + deposit_b, + ) + ); + } + + // no approval + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0); + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + deposit( + &SWAP_PROGRAM_ID, + &TOKEN_PROGRAM_ID, + &accounts.swap_key, + &accounts.authority_key, + &token_a_key, + &token_b_key, + &accounts.token_a_key, + &accounts.token_b_key, + &accounts.pool_mint_key, + &pool_key, + deposit_a, + ) + .unwrap(), + vec![ + &mut accounts.swap_account, + &mut Account::default(), + &mut token_a_account, + &mut token_b_account, + &mut accounts.token_a_account, + &mut accounts.token_b_account, + &mut accounts.pool_mint_account, + &mut pool_account, + &mut Account::default(), + ], + ) + ); + } + + // wrong token program id + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0); + let wrong_key = pubkey_rand(); + assert_eq!( + Err(ProgramError::InvalidAccountData), + do_process_instruction( + deposit( + &SWAP_PROGRAM_ID, + &wrong_key, + &accounts.swap_key, + &accounts.authority_key, + &token_a_key, + &token_b_key, + &accounts.token_a_key, + &accounts.token_b_key, + &accounts.pool_mint_key, + &pool_key, + deposit_a, + ) + .unwrap(), + vec![ + &mut accounts.swap_account, + &mut Account::default(), + &mut token_a_account, + &mut token_b_account, + &mut accounts.token_a_account, + &mut accounts.token_b_account, + &mut accounts.pool_mint_account, + &mut pool_account, + &mut Account::default(), + ], + ) + ); + } + + // wrong swap token accounts + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0); + + let old_a_key = accounts.token_a_key; + let old_a_account = accounts.token_a_account; + + accounts.token_a_key = token_a_key.clone(); + accounts.token_a_account = token_a_account.clone(); + + // wrong swap token a account + assert_eq!( + Err(SwapError::IncorrectSwapAccount.into()), + accounts.deposit( + &depositor_key, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + &pool_key, + &mut pool_account, + deposit_a, + deposit_b, + ) + ); + + accounts.token_a_key = old_a_key; + accounts.token_a_account = old_a_account; + + let old_b_key = accounts.token_b_key; + let old_b_account = accounts.token_b_account; + + accounts.token_b_key = token_b_key.clone(); + accounts.token_b_account = token_b_account.clone(); + + // wrong swap token b account + assert_eq!( + Err(SwapError::IncorrectSwapAccount.into()), + accounts.deposit( + &depositor_key, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + &pool_key, + &mut pool_account, + deposit_a, + deposit_b, + ) + ); + + accounts.token_b_key = old_b_key; + accounts.token_b_account = old_b_account; + } + + // wrong mint + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0); + let (pool_mint_key, pool_mint_account) = + create_mint(&TOKEN_PROGRAM_ID, &accounts.authority_key); + let old_pool_key = accounts.pool_mint_key; + let old_pool_account = accounts.pool_mint_account; + accounts.pool_mint_key = pool_mint_key; + accounts.pool_mint_account = pool_mint_account; + + assert_eq!( + Err(SwapError::IncorrectPoolMint.into()), + accounts.deposit( + &depositor_key, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + &pool_key, + &mut pool_account, + deposit_a, + deposit_b, + ) + ); + + accounts.pool_mint_key = old_pool_key; + accounts.pool_mint_account = old_pool_account; + } + + // correctly deposit + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0); + accounts + .deposit( + &depositor_key, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + &pool_key, + &mut pool_account, + deposit_a, + deposit_b, + ) + .unwrap(); + + let swap_token_a = + Processor::unpack_token_account(&accounts.token_a_account.data).unwrap(); + assert_eq!(swap_token_a.amount, deposit_a + token_a_amount); + let swap_token_b = + Processor::unpack_token_account(&accounts.token_b_account.data).unwrap(); + assert_eq!(swap_token_b.amount, deposit_b + token_b_amount); + let token_a = Processor::unpack_token_account(&token_a_account.data).unwrap(); + assert_eq!(token_a.amount, 0); + let token_b = Processor::unpack_token_account(&token_b_account.data).unwrap(); + assert_eq!(token_b.amount, 0); + let pool_account = Processor::unpack_token_account(&pool_account.data).unwrap(); + let swap_pool_account = + Processor::unpack_token_account(&accounts.pool_token_account.data).unwrap(); + let pool_mint = Processor::unpack_mint(&accounts.pool_mint_account.data).unwrap(); + assert_eq!( + pool_mint.supply, + pool_account.amount + swap_pool_account.amount + ); + } } #[test] fn test_withdraw() { + let user_key = pubkey_rand(); let fee_numerator = 1; let fee_denominator = 2; let token_a_amount = 1000; let token_b_amount = 2000; - let mut accounts = initialize_swap( + let mut accounts = SwapAccountInfo::new( + &user_key, fee_numerator, fee_denominator, token_a_amount, token_b_amount, ); - let seeds = [&accounts.swap_key.to_bytes()[..32], &[accounts.nonce]]; - let authority_key = Pubkey::create_program_address(&seeds, &SWAP_PROGRAM_ID).unwrap(); + let withdrawer_key = pubkey_rand(); let initial_a = token_a_amount / 10; - let (withdraw_token_a_key, mut withdraw_token_a_account) = mint_token( - &TOKEN_PROGRAM_ID, - &accounts.token_a_mint_key, - &mut accounts.token_a_mint_account, - &authority_key, - initial_a, - ); let initial_b = token_b_amount / 10; - let (withdraw_token_b_key, mut withdraw_token_b_account) = mint_token( - &TOKEN_PROGRAM_ID, - &accounts.token_b_mint_key, - &mut accounts.token_b_mint_account, - &authority_key, - initial_b, - ); - + let initial_pool = token_a_amount; let withdraw_amount = token_a_amount / 4; - do_process_instruction( - withdraw( - &SWAP_PROGRAM_ID, - &TOKEN_PROGRAM_ID, - &accounts.swap_key, - &authority_key, - &accounts.pool_mint_key, - &accounts.pool_token_key, - &accounts.token_a_key, - &accounts.token_b_key, - &withdraw_token_a_key, - &withdraw_token_b_key, - withdraw_amount, - ) - .unwrap(), - vec![ - &mut accounts.swap_account, - &mut Account::default(), - &mut accounts.pool_mint_account, - &mut accounts.pool_token_account, - &mut accounts.token_a_account, - &mut accounts.token_b_account, - &mut withdraw_token_a_account, - &mut withdraw_token_b_account, - &mut Account::default(), - ], - ) - .unwrap(); - let token_a = Processor::unpack_token_account(&accounts.token_a_account.data).unwrap(); - assert_eq!(token_a.amount, token_a_amount - withdraw_amount); - let token_b = Processor::unpack_token_account(&accounts.token_b_account.data).unwrap(); - assert_eq!(token_b.amount, token_b_amount - (withdraw_amount * 2)); - let withdraw_token_a = - Processor::unpack_token_account(&withdraw_token_a_account.data).unwrap(); - assert_eq!(withdraw_token_a.amount, initial_a + withdraw_amount); - let withdraw_token_b = - Processor::unpack_token_account(&withdraw_token_b_account.data).unwrap(); - assert_eq!(withdraw_token_b.amount, initial_b + (withdraw_amount * 2)); - let pool_account = - Processor::unpack_token_account(&accounts.pool_token_account.data).unwrap(); - let pool_mint = Processor::unpack_mint(&accounts.pool_mint_account.data).unwrap(); - assert_eq!(pool_mint.supply, pool_account.amount); + // swap not initialized + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts(&user_key, &withdrawer_key, initial_a, initial_b, 0); + assert_eq!( + Err(SwapError::InvalidSwapInfo.into()), + accounts.withdraw( + &withdrawer_key, + &pool_key, + &mut pool_account, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + withdraw_amount, + ) + ); + } + + accounts.initialize_swap().unwrap(); + + // wrong nonce for authority_key + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts(&user_key, &withdrawer_key, initial_a, initial_b, 0); + let old_authority = accounts.authority_key; + let (bad_authority_key, _nonce) = Pubkey::find_program_address( + &[&accounts.swap_key.to_bytes()[..]], + &TOKEN_PROGRAM_ID, + ); + accounts.authority_key = bad_authority_key; + assert_eq!( + Err(SwapError::InvalidProgramAddress.into()), + accounts.withdraw( + &withdrawer_key, + &pool_key, + &mut pool_account, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + withdraw_amount, + ) + ); + accounts.authority_key = old_authority; + } + + // not enough pool tokens + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts( + &user_key, + &withdrawer_key, + initial_a, + initial_b, + withdraw_amount / 2, + ); + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + accounts.withdraw( + &withdrawer_key, + &pool_key, + &mut pool_account, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + withdraw_amount, + ) + ); + } + + // wrong token a / b accounts + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts( + &user_key, + &withdrawer_key, + initial_a, + initial_b, + withdraw_amount, + ); + assert_eq!( + Err(TokenError::MintMismatch.into()), + accounts.withdraw( + &withdrawer_key, + &pool_key, + &mut pool_account, + &token_b_key, + &mut token_b_account, + &token_a_key, + &mut token_a_account, + withdraw_amount, + ) + ); + } + + // wrong pool token account + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + _pool_key, + _pool_account, + ) = accounts.setup_token_accounts( + &user_key, + &withdrawer_key, + initial_a, + initial_b, + withdraw_amount, + ); + let ( + wrong_token_a_key, + mut wrong_token_a_account, + _token_b_key, + _token_b_account, + _pool_key, + _pool_account, + ) = accounts.setup_token_accounts( + &user_key, + &withdrawer_key, + initial_a, + initial_b, + withdraw_amount, + ); + assert_eq!( + Err(TokenError::MintMismatch.into()), + accounts.withdraw( + &withdrawer_key, + &wrong_token_a_key, + &mut wrong_token_a_account, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + withdraw_amount, + ) + ); + } + + // no approval + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts(&user_key, &withdrawer_key, 0, 0, withdraw_amount); + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + withdraw( + &SWAP_PROGRAM_ID, + &TOKEN_PROGRAM_ID, + &accounts.swap_key, + &accounts.authority_key, + &accounts.pool_mint_key, + &pool_key, + &accounts.token_a_key, + &accounts.token_b_key, + &token_a_key, + &token_b_key, + withdraw_amount, + ) + .unwrap(), + vec![ + &mut accounts.swap_account, + &mut Account::default(), + &mut accounts.pool_mint_account, + &mut pool_account, + &mut accounts.token_a_account, + &mut accounts.token_b_account, + &mut token_a_account, + &mut token_b_account, + &mut Account::default(), + ], + ) + ); + } + + // wrong token program id + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts( + &user_key, + &withdrawer_key, + initial_a, + initial_b, + withdraw_amount, + ); + let wrong_key = pubkey_rand(); + assert_eq!( + Err(ProgramError::InvalidAccountData), + do_process_instruction( + withdraw( + &SWAP_PROGRAM_ID, + &wrong_key, + &accounts.swap_key, + &accounts.authority_key, + &accounts.pool_mint_key, + &pool_key, + &accounts.token_a_key, + &accounts.token_b_key, + &token_a_key, + &token_b_key, + withdraw_amount, + ) + .unwrap(), + vec![ + &mut accounts.swap_account, + &mut Account::default(), + &mut accounts.pool_mint_account, + &mut pool_account, + &mut accounts.token_a_account, + &mut accounts.token_b_account, + &mut token_a_account, + &mut token_b_account, + &mut Account::default(), + ], + ) + ); + } + + // wrong swap token accounts + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts( + &user_key, + &withdrawer_key, + initial_a, + initial_b, + initial_pool, + ); + + let old_a_key = accounts.token_a_key; + let old_a_account = accounts.token_a_account; + + accounts.token_a_key = token_a_key.clone(); + accounts.token_a_account = token_a_account.clone(); + + // wrong swap token a account + assert_eq!( + Err(SwapError::IncorrectSwapAccount.into()), + accounts.withdraw( + &withdrawer_key, + &pool_key, + &mut pool_account, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + withdraw_amount, + ) + ); + + accounts.token_a_key = old_a_key; + accounts.token_a_account = old_a_account; + + let old_b_key = accounts.token_b_key; + let old_b_account = accounts.token_b_account; + + accounts.token_b_key = token_b_key.clone(); + accounts.token_b_account = token_b_account.clone(); + + // wrong swap token b account + assert_eq!( + Err(SwapError::IncorrectSwapAccount.into()), + accounts.withdraw( + &withdrawer_key, + &pool_key, + &mut pool_account, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + withdraw_amount, + ) + ); + + accounts.token_b_key = old_b_key; + accounts.token_b_account = old_b_account; + } + + // wrong mint + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts( + &user_key, + &withdrawer_key, + initial_a, + initial_b, + initial_pool, + ); + let (pool_mint_key, pool_mint_account) = + create_mint(&TOKEN_PROGRAM_ID, &accounts.authority_key); + let old_pool_key = accounts.pool_mint_key; + let old_pool_account = accounts.pool_mint_account; + accounts.pool_mint_key = pool_mint_key; + accounts.pool_mint_account = pool_mint_account; + + assert_eq!( + Err(SwapError::IncorrectPoolMint.into()), + accounts.withdraw( + &withdrawer_key, + &pool_key, + &mut pool_account, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + withdraw_amount, + ) + ); + + accounts.pool_mint_key = old_pool_key; + accounts.pool_mint_account = old_pool_account; + } + + // correct withdrawal + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + pool_key, + mut pool_account, + ) = accounts.setup_token_accounts( + &user_key, + &withdrawer_key, + initial_a, + initial_b, + initial_pool, + ); + + accounts + .withdraw( + &withdrawer_key, + &pool_key, + &mut pool_account, + &token_a_key, + &mut token_a_account, + &token_b_key, + &mut token_b_account, + withdraw_amount, + ) + .unwrap(); + + let swap_token_a = + Processor::unpack_token_account(&accounts.token_a_account.data).unwrap(); + assert_eq!(swap_token_a.amount, token_a_amount - withdraw_amount); + let swap_token_b = + Processor::unpack_token_account(&accounts.token_b_account.data).unwrap(); + assert_eq!(swap_token_b.amount, token_b_amount - (withdraw_amount * 2)); + let token_a = Processor::unpack_token_account(&token_a_account.data).unwrap(); + assert_eq!(token_a.amount, initial_a + withdraw_amount); + let token_b = Processor::unpack_token_account(&token_b_account.data).unwrap(); + assert_eq!(token_b.amount, initial_b + (withdraw_amount * 2)); + let pool_account = Processor::unpack_token_account(&pool_account.data).unwrap(); + assert_eq!(pool_account.amount, initial_pool - withdraw_amount); + } } #[test] fn test_swap() { + let user_key = pubkey_rand(); + let swapper_key = pubkey_rand(); let fee_numerator = 1; let fee_denominator = 10; let token_a_amount = 1000; let token_b_amount = 5000; - let mut accounts = initialize_swap( + let mut accounts = SwapAccountInfo::new( + &user_key, fee_numerator, fee_denominator, token_a_amount, token_b_amount, ); - let seeds = [&accounts.swap_key.to_bytes()[..32], &[accounts.nonce]]; - let authority_key = Pubkey::create_program_address(&seeds, &SWAP_PROGRAM_ID).unwrap(); - let initial_a = token_a_amount / 5; - let (user_token_a_key, mut user_token_a_account) = mint_token( - &TOKEN_PROGRAM_ID, - &accounts.token_a_mint_key, - &mut accounts.token_a_mint_account, - &authority_key, - initial_a, - ); let initial_b = token_b_amount / 5; - let (user_token_b_key, mut user_token_b_account) = mint_token( - &TOKEN_PROGRAM_ID, - &accounts.token_b_mint_key, - &mut accounts.token_b_mint_account, - &authority_key, - initial_b, - ); - let a_to_b_amount = initial_a / 10; - do_process_instruction( - swap( - &SWAP_PROGRAM_ID, + let swap_token_a_key = accounts.token_a_key.clone(); + let swap_token_b_key = accounts.token_b_key.clone(); + + // swap not initialized + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + _pool_key, + _pool_account, + ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0); + assert_eq!( + Err(SwapError::InvalidSwapInfo.into()), + accounts.swap( + &swapper_key, + &token_a_key, + &mut token_a_account, + &swap_token_a_key, + &swap_token_b_key, + &token_b_key, + &mut token_b_account, + initial_a, + ) + ); + } + + accounts.initialize_swap().unwrap(); + + // wrong nonce + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + _pool_key, + _pool_account, + ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0); + let old_authority = accounts.authority_key; + let (bad_authority_key, _nonce) = Pubkey::find_program_address( + &[&accounts.swap_key.to_bytes()[..]], &TOKEN_PROGRAM_ID, - &accounts.swap_key, - &authority_key, - &user_token_a_key, - &accounts.token_a_key, - &accounts.token_b_key, - &user_token_b_key, + ); + accounts.authority_key = bad_authority_key; + assert_eq!( + Err(SwapError::InvalidProgramAddress.into()), + accounts.swap( + &swapper_key, + &token_a_key, + &mut token_a_account, + &swap_token_a_key, + &swap_token_b_key, + &token_b_key, + &mut token_b_account, + initial_a, + ) + ); + accounts.authority_key = old_authority; + } + + // wrong token program id + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + _pool_key, + _pool_account, + ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0); + let wrong_program_id = pubkey_rand(); + assert_eq!( + Err(ProgramError::InvalidAccountData), + do_process_instruction( + swap( + &SWAP_PROGRAM_ID, + &wrong_program_id, + &accounts.swap_key, + &accounts.authority_key, + &token_a_key, + &accounts.token_a_key, + &accounts.token_b_key, + &token_b_key, + initial_a, + ) + .unwrap(), + vec![ + &mut accounts.swap_account, + &mut Account::default(), + &mut token_a_account, + &mut accounts.token_a_account, + &mut accounts.token_b_account, + &mut token_b_account, + &mut Account::default(), + ], + ), + ); + } + + // not enough token a to swap + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + _pool_key, + _pool_account, + ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0); + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + accounts.swap( + &swapper_key, + &token_a_key, + &mut token_a_account, + &swap_token_a_key, + &swap_token_b_key, + &token_b_key, + &mut token_b_account, + initial_a * 2, + ) + ); + } + + // wrong swap token A / B accounts + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + _pool_key, + _pool_account, + ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0); + assert_eq!( + Err(SwapError::IncorrectSwapAccount.into()), + do_process_instruction( + swap( + &SWAP_PROGRAM_ID, + &TOKEN_PROGRAM_ID, + &accounts.swap_key, + &accounts.authority_key, + &token_a_key, + &token_a_key, + &token_b_key, + &token_b_key, + initial_a, + ) + .unwrap(), + vec![ + &mut accounts.swap_account, + &mut Account::default(), + &mut token_a_account.clone(), + &mut token_a_account, + &mut token_b_account.clone(), + &mut token_b_account, + &mut Account::default(), + ], + ), + ); + } + + // wrong user token A / B accounts + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + _pool_key, + _pool_account, + ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0); + assert_eq!( + Err(TokenError::MintMismatch.into()), + accounts.swap( + &swapper_key, + &token_b_key, + &mut token_b_account, + &swap_token_a_key, + &swap_token_b_key, + &token_a_key, + &mut token_a_account, + initial_a, + ) + ); + } + + // swap from a to a + { + let ( + token_a_key, + mut token_a_account, + _token_b_key, + _token_b_account, + _pool_key, + _pool_account, + ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0); + assert_eq!( + Err(SwapError::InvalidInput.into()), + accounts.swap( + &swapper_key, + &token_a_key, + &mut token_a_account.clone(), + &swap_token_a_key, + &swap_token_a_key, + &token_a_key, + &mut token_a_account, + initial_a, + ) + ); + } + + // no approval + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + _pool_key, + _pool_account, + ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0); + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + swap( + &SWAP_PROGRAM_ID, + &TOKEN_PROGRAM_ID, + &accounts.swap_key, + &accounts.authority_key, + &token_a_key, + &accounts.token_a_key, + &accounts.token_b_key, + &token_b_key, + initial_a, + ) + .unwrap(), + vec![ + &mut accounts.swap_account, + &mut Account::default(), + &mut token_a_account, + &mut accounts.token_a_account, + &mut accounts.token_b_account, + &mut token_b_account, + &mut Account::default(), + ], + ), + ); + } + + // correct swap + { + let ( + token_a_key, + mut token_a_account, + token_b_key, + mut token_b_account, + _pool_key, + _pool_account, + ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0); + // swap one way + let a_to_b_amount = initial_a / 10; + accounts + .swap( + &swapper_key, + &token_a_key, + &mut token_a_account, + &swap_token_a_key, + &swap_token_b_key, + &token_b_key, + &mut token_b_account, + a_to_b_amount, + ) + .unwrap(); + + let results = SwapResult::swap_to( a_to_b_amount, + token_a_amount, + token_b_amount, + fee_numerator, + fee_denominator, ) - .unwrap(), - vec![ - &mut accounts.swap_account, - &mut Account::default(), - &mut user_token_a_account, - &mut accounts.token_a_account, - &mut accounts.token_b_account, - &mut user_token_b_account, - &mut Account::default(), - ], - ) - .unwrap(); + .unwrap(); - let results = SwapResult::swap_to( - a_to_b_amount, - token_a_amount, - token_b_amount, - fee_numerator, - fee_denominator, - ) - .unwrap(); + let swap_token_a = + Processor::unpack_token_account(&accounts.token_a_account.data).unwrap(); + let token_a_amount = swap_token_a.amount; + assert_eq!(token_a_amount, results.new_source); + let token_a = Processor::unpack_token_account(&token_a_account.data).unwrap(); + assert_eq!(token_a.amount, initial_a - a_to_b_amount); - let swap_token_a = Processor::unpack_token_account(&accounts.token_a_account.data).unwrap(); - let token_a_amount = swap_token_a.amount; - assert_eq!(token_a_amount, results.new_source); - let user_token_a = Processor::unpack_token_account(&user_token_a_account.data).unwrap(); - assert_eq!(user_token_a.amount, initial_a - a_to_b_amount); + let swap_token_b = + Processor::unpack_token_account(&accounts.token_b_account.data).unwrap(); + let token_b_amount = swap_token_b.amount; + assert_eq!(token_b_amount, results.new_destination); + let token_b = Processor::unpack_token_account(&token_b_account.data).unwrap(); + assert_eq!(token_b.amount, initial_b + results.amount_swapped); - let swap_token_b = Processor::unpack_token_account(&accounts.token_b_account.data).unwrap(); - let token_b_amount = swap_token_b.amount; - assert_eq!(token_b_amount, results.new_destination); - let user_token_b = Processor::unpack_token_account(&user_token_b_account.data).unwrap(); - assert_eq!(user_token_b.amount, initial_b + results.amount_swapped); + let first_swap_amount = results.amount_swapped; - let first_swap_amount = results.amount_swapped; + // swap the other way + let b_to_a_amount = initial_b / 10; + accounts + .swap( + &swapper_key, + &token_b_key, + &mut token_b_account, + &swap_token_b_key, + &swap_token_a_key, + &token_a_key, + &mut token_a_account, + b_to_a_amount, + ) + .unwrap(); - let b_to_a_amount = initial_b / 10; - do_process_instruction( - swap( - &SWAP_PROGRAM_ID, - &TOKEN_PROGRAM_ID, - &accounts.swap_key, - &authority_key, - &user_token_b_key, - &accounts.token_b_key, - &accounts.token_a_key, - &user_token_a_key, + let results = SwapResult::swap_to( b_to_a_amount, + token_b_amount, + token_a_amount, + fee_numerator, + fee_denominator, ) - .unwrap(), - vec![ - &mut accounts.swap_account, - &mut Account::default(), - &mut user_token_b_account, - &mut accounts.token_b_account, - &mut accounts.token_a_account, - &mut user_token_a_account, - &mut Account::default(), - ], - ) - .unwrap(); + .unwrap(); - let results = SwapResult::swap_to( - b_to_a_amount, - token_b_amount, - token_a_amount, - fee_numerator, - fee_denominator, - ) - .unwrap(); + let swap_token_a = + Processor::unpack_token_account(&accounts.token_a_account.data).unwrap(); + assert_eq!(swap_token_a.amount, results.new_destination); + let token_a = Processor::unpack_token_account(&token_a_account.data).unwrap(); + assert_eq!( + token_a.amount, + initial_a - a_to_b_amount + results.amount_swapped + ); - let swap_token_a = Processor::unpack_token_account(&accounts.token_a_account.data).unwrap(); - assert_eq!(swap_token_a.amount, results.new_destination); - let user_token_a = Processor::unpack_token_account(&user_token_a_account.data).unwrap(); - assert_eq!( - user_token_a.amount, - initial_a - a_to_b_amount + results.amount_swapped - ); - - let swap_token_b = Processor::unpack_token_account(&accounts.token_b_account.data).unwrap(); - assert_eq!(swap_token_b.amount, results.new_source); - let user_token_b = Processor::unpack_token_account(&user_token_b_account.data).unwrap(); - assert_eq!( - user_token_b.amount, - initial_b + first_swap_amount - b_to_a_amount - ); + let swap_token_b = + Processor::unpack_token_account(&accounts.token_b_account.data).unwrap(); + assert_eq!(swap_token_b.amount, results.new_source); + let token_b = Processor::unpack_token_account(&token_b_account.data).unwrap(); + assert_eq!( + token_b.amount, + initial_b + first_swap_amount - b_to_a_amount + ); + } } } diff --git a/token-swap/program/src/state.rs b/token-swap/program/src/state.rs index 686beb77..3e6a6267 100644 --- a/token-swap/program/src/state.rs +++ b/token-swap/program/src/state.rs @@ -43,7 +43,7 @@ impl SwapInfo { if value.is_initialized { Ok(value) } else { - Err(SwapError::InvalidState.into()) + Err(SwapError::InvalidSwapInfo.into()) } } @@ -216,6 +216,6 @@ mod tests { let unpack_unchecked = SwapInfo::unpack_unchecked(&packed).unwrap(); assert_eq!(unpack_unchecked, swap_info); let err = SwapInfo::unpack(&packed).unwrap_err(); - assert_eq!(err, SwapError::InvalidState.into()); + assert_eq!(err, SwapError::InvalidSwapInfo.into()); } }