gpl_token_swap/
processor.rs

1//! Program state processor
2
3use crate::constraints::{SwapConstraints, SWAP_CONSTRAINTS};
4use crate::{
5    curve::{
6        base::SwapCurve,
7        calculator::{RoundDirection, TradeDirection},
8        fees::Fees,
9    },
10    error::SwapError,
11    instruction::{
12        DepositAllTokenTypes, DepositSingleTokenTypeExactAmountIn, Initialize, Swap,
13        SwapInstruction, WithdrawAllTokenTypes, WithdrawSingleTokenTypeExactAmountOut,
14    },
15    state::{SwapState, SwapV1, SwapVersion},
16};
17use num_traits::FromPrimitive;
18use gemachain_program::{
19    account_info::{next_account_info, AccountInfo},
20    decode_error::DecodeError,
21    entrypoint::ProgramResult,
22    msg,
23    program::invoke_signed,
24    program_error::{PrintProgramError, ProgramError},
25    program_option::COption,
26    program_pack::Pack,
27    pubkey::Pubkey,
28};
29use std::convert::TryInto;
30
31/// Program state handler.
32pub struct Processor {}
33impl Processor {
34    /// Unpacks a gpl_token `Account`.
35    pub fn unpack_token_account(
36        account_info: &AccountInfo,
37        token_program_id: &Pubkey,
38    ) -> Result<gpl_token::state::Account, SwapError> {
39        if account_info.owner != token_program_id {
40            Err(SwapError::IncorrectTokenProgramId)
41        } else {
42            gpl_token::state::Account::unpack(&account_info.data.borrow())
43                .map_err(|_| SwapError::ExpectedAccount)
44        }
45    }
46
47    /// Unpacks a gpl_token `Mint`.
48    pub fn unpack_mint(
49        account_info: &AccountInfo,
50        token_program_id: &Pubkey,
51    ) -> Result<gpl_token::state::Mint, SwapError> {
52        if account_info.owner != token_program_id {
53            Err(SwapError::IncorrectTokenProgramId)
54        } else {
55            gpl_token::state::Mint::unpack(&account_info.data.borrow())
56                .map_err(|_| SwapError::ExpectedMint)
57        }
58    }
59
60    /// Calculates the authority id by generating a program address.
61    pub fn authority_id(
62        program_id: &Pubkey,
63        my_info: &Pubkey,
64        bump_seed: u8,
65    ) -> Result<Pubkey, SwapError> {
66        Pubkey::create_program_address(&[&my_info.to_bytes()[..32], &[bump_seed]], program_id)
67            .or(Err(SwapError::InvalidProgramAddress))
68    }
69
70    /// Issue a gpl_token `Burn` instruction.
71    pub fn token_burn<'a>(
72        swap: &Pubkey,
73        token_program: AccountInfo<'a>,
74        burn_account: AccountInfo<'a>,
75        mint: AccountInfo<'a>,
76        authority: AccountInfo<'a>,
77        bump_seed: u8,
78        amount: u64,
79    ) -> Result<(), ProgramError> {
80        let swap_bytes = swap.to_bytes();
81        let authority_signature_seeds = [&swap_bytes[..32], &[bump_seed]];
82        let signers = &[&authority_signature_seeds[..]];
83
84        let ix = gpl_token::instruction::burn(
85            token_program.key,
86            burn_account.key,
87            mint.key,
88            authority.key,
89            &[],
90            amount,
91        )?;
92
93        invoke_signed(
94            &ix,
95            &[burn_account, mint, authority, token_program],
96            signers,
97        )
98    }
99
100    /// Issue a gpl_token `MintTo` instruction.
101    pub fn token_mint_to<'a>(
102        swap: &Pubkey,
103        token_program: AccountInfo<'a>,
104        mint: AccountInfo<'a>,
105        destination: AccountInfo<'a>,
106        authority: AccountInfo<'a>,
107        bump_seed: u8,
108        amount: u64,
109    ) -> Result<(), ProgramError> {
110        let swap_bytes = swap.to_bytes();
111        let authority_signature_seeds = [&swap_bytes[..32], &[bump_seed]];
112        let signers = &[&authority_signature_seeds[..]];
113        let ix = gpl_token::instruction::mint_to(
114            token_program.key,
115            mint.key,
116            destination.key,
117            authority.key,
118            &[],
119            amount,
120        )?;
121
122        invoke_signed(&ix, &[mint, destination, authority, token_program], signers)
123    }
124
125    /// Issue a gpl_token `Transfer` instruction.
126    pub fn token_transfer<'a>(
127        swap: &Pubkey,
128        token_program: AccountInfo<'a>,
129        source: AccountInfo<'a>,
130        destination: AccountInfo<'a>,
131        authority: AccountInfo<'a>,
132        bump_seed: u8,
133        amount: u64,
134    ) -> Result<(), ProgramError> {
135        let swap_bytes = swap.to_bytes();
136        let authority_signature_seeds = [&swap_bytes[..32], &[bump_seed]];
137        let signers = &[&authority_signature_seeds[..]];
138        let ix = gpl_token::instruction::transfer(
139            token_program.key,
140            source.key,
141            destination.key,
142            authority.key,
143            &[],
144            amount,
145        )?;
146        invoke_signed(
147            &ix,
148            &[source, destination, authority, token_program],
149            signers,
150        )
151    }
152
153    #[allow(clippy::too_many_arguments)]
154    fn check_accounts(
155        token_swap: &dyn SwapState,
156        program_id: &Pubkey,
157        swap_account_info: &AccountInfo,
158        authority_info: &AccountInfo,
159        token_a_info: &AccountInfo,
160        token_b_info: &AccountInfo,
161        pool_mint_info: &AccountInfo,
162        token_program_info: &AccountInfo,
163        user_token_a_info: Option<&AccountInfo>,
164        user_token_b_info: Option<&AccountInfo>,
165        pool_fee_account_info: Option<&AccountInfo>,
166    ) -> ProgramResult {
167        if swap_account_info.owner != program_id {
168            return Err(ProgramError::IncorrectProgramId);
169        }
170        if *authority_info.key
171            != Self::authority_id(program_id, swap_account_info.key, token_swap.bump_seed())?
172        {
173            return Err(SwapError::InvalidProgramAddress.into());
174        }
175        if *token_a_info.key != *token_swap.token_a_account() {
176            return Err(SwapError::IncorrectSwapAccount.into());
177        }
178        if *token_b_info.key != *token_swap.token_b_account() {
179            return Err(SwapError::IncorrectSwapAccount.into());
180        }
181        if *pool_mint_info.key != *token_swap.pool_mint() {
182            return Err(SwapError::IncorrectPoolMint.into());
183        }
184        if *token_program_info.key != *token_swap.token_program_id() {
185            return Err(SwapError::IncorrectTokenProgramId.into());
186        }
187        if let Some(user_token_a_info) = user_token_a_info {
188            if token_a_info.key == user_token_a_info.key {
189                return Err(SwapError::InvalidInput.into());
190            }
191        }
192        if let Some(user_token_b_info) = user_token_b_info {
193            if token_b_info.key == user_token_b_info.key {
194                return Err(SwapError::InvalidInput.into());
195            }
196        }
197        if let Some(pool_fee_account_info) = pool_fee_account_info {
198            if *pool_fee_account_info.key != *token_swap.pool_fee_account() {
199                return Err(SwapError::IncorrectFeeAccount.into());
200            }
201        }
202        Ok(())
203    }
204
205    /// Processes an [Initialize](enum.Instruction.html).
206    pub fn process_initialize(
207        program_id: &Pubkey,
208        fees: Fees,
209        swap_curve: SwapCurve,
210        accounts: &[AccountInfo],
211        swap_constraints: &Option<SwapConstraints>,
212    ) -> ProgramResult {
213        let account_info_iter = &mut accounts.iter();
214        let swap_info = next_account_info(account_info_iter)?;
215        let authority_info = next_account_info(account_info_iter)?;
216        let token_a_info = next_account_info(account_info_iter)?;
217        let token_b_info = next_account_info(account_info_iter)?;
218        let pool_mint_info = next_account_info(account_info_iter)?;
219        let fee_account_info = next_account_info(account_info_iter)?;
220        let destination_info = next_account_info(account_info_iter)?;
221        let token_program_info = next_account_info(account_info_iter)?;
222
223        let token_program_id = *token_program_info.key;
224        if SwapVersion::is_initialized(&swap_info.data.borrow()) {
225            return Err(SwapError::AlreadyInUse.into());
226        }
227
228        let (swap_authority, bump_seed) =
229            Pubkey::find_program_address(&[&swap_info.key.to_bytes()], program_id);
230        if *authority_info.key != swap_authority {
231            return Err(SwapError::InvalidProgramAddress.into());
232        }
233        let token_a = Self::unpack_token_account(token_a_info, &token_program_id)?;
234        let token_b = Self::unpack_token_account(token_b_info, &token_program_id)?;
235        let fee_account = Self::unpack_token_account(fee_account_info, &token_program_id)?;
236        let destination = Self::unpack_token_account(destination_info, &token_program_id)?;
237        let pool_mint = Self::unpack_mint(pool_mint_info, &token_program_id)?;
238        if *authority_info.key != token_a.owner {
239            return Err(SwapError::InvalidOwner.into());
240        }
241        if *authority_info.key != token_b.owner {
242            return Err(SwapError::InvalidOwner.into());
243        }
244        if *authority_info.key == destination.owner {
245            return Err(SwapError::InvalidOutputOwner.into());
246        }
247        if *authority_info.key == fee_account.owner {
248            return Err(SwapError::InvalidOutputOwner.into());
249        }
250        if COption::Some(*authority_info.key) != pool_mint.mint_authority {
251            return Err(SwapError::InvalidOwner.into());
252        }
253
254        if token_a.mint == token_b.mint {
255            return Err(SwapError::RepeatedMint.into());
256        }
257        swap_curve
258            .calculator
259            .validate_supply(token_a.amount, token_b.amount)?;
260        if token_a.delegate.is_some() {
261            return Err(SwapError::InvalidDelegate.into());
262        }
263        if token_b.delegate.is_some() {
264            return Err(SwapError::InvalidDelegate.into());
265        }
266        if token_a.close_authority.is_some() {
267            return Err(SwapError::InvalidCloseAuthority.into());
268        }
269        if token_b.close_authority.is_some() {
270            return Err(SwapError::InvalidCloseAuthority.into());
271        }
272
273        if pool_mint.supply != 0 {
274            return Err(SwapError::InvalidSupply.into());
275        }
276        if pool_mint.freeze_authority.is_some() {
277            return Err(SwapError::InvalidFreezeAuthority.into());
278        }
279        if *pool_mint_info.key != fee_account.mint {
280            return Err(SwapError::IncorrectPoolMint.into());
281        }
282
283        if let Some(swap_constraints) = swap_constraints {
284            let owner_key = swap_constraints
285                .owner_key
286                .parse::<Pubkey>()
287                .map_err(|_| SwapError::InvalidOwner)?;
288            if fee_account.owner != owner_key {
289                return Err(SwapError::InvalidOwner.into());
290            }
291            swap_constraints.validate_curve(&swap_curve)?;
292            swap_constraints.validate_fees(&fees)?;
293        }
294        fees.validate()?;
295        swap_curve.calculator.validate()?;
296
297        let initial_amount = swap_curve.calculator.new_pool_supply();
298
299        Self::token_mint_to(
300            swap_info.key,
301            token_program_info.clone(),
302            pool_mint_info.clone(),
303            destination_info.clone(),
304            authority_info.clone(),
305            bump_seed,
306            to_u64(initial_amount)?,
307        )?;
308
309        let obj = SwapVersion::SwapV1(SwapV1 {
310            is_initialized: true,
311            bump_seed,
312            token_program_id,
313            token_a: *token_a_info.key,
314            token_b: *token_b_info.key,
315            pool_mint: *pool_mint_info.key,
316            token_a_mint: token_a.mint,
317            token_b_mint: token_b.mint,
318            pool_fee_account: *fee_account_info.key,
319            fees,
320            swap_curve,
321        });
322        SwapVersion::pack(obj, &mut swap_info.data.borrow_mut())?;
323        Ok(())
324    }
325
326    /// Processes an [Swap](enum.Instruction.html).
327    pub fn process_swap(
328        program_id: &Pubkey,
329        amount_in: u64,
330        minimum_amount_out: u64,
331        accounts: &[AccountInfo],
332    ) -> ProgramResult {
333        let account_info_iter = &mut accounts.iter();
334        let swap_info = next_account_info(account_info_iter)?;
335        let authority_info = next_account_info(account_info_iter)?;
336        let user_transfer_authority_info = next_account_info(account_info_iter)?;
337        let source_info = next_account_info(account_info_iter)?;
338        let swap_source_info = next_account_info(account_info_iter)?;
339        let swap_destination_info = next_account_info(account_info_iter)?;
340        let destination_info = next_account_info(account_info_iter)?;
341        let pool_mint_info = next_account_info(account_info_iter)?;
342        let pool_fee_account_info = next_account_info(account_info_iter)?;
343        let token_program_info = next_account_info(account_info_iter)?;
344
345        if swap_info.owner != program_id {
346            return Err(ProgramError::IncorrectProgramId);
347        }
348        let token_swap = SwapVersion::unpack(&swap_info.data.borrow())?;
349
350        if *authority_info.key
351            != Self::authority_id(program_id, swap_info.key, token_swap.bump_seed())?
352        {
353            return Err(SwapError::InvalidProgramAddress.into());
354        }
355        if !(*swap_source_info.key == *token_swap.token_a_account()
356            || *swap_source_info.key == *token_swap.token_b_account())
357        {
358            return Err(SwapError::IncorrectSwapAccount.into());
359        }
360        if !(*swap_destination_info.key == *token_swap.token_a_account()
361            || *swap_destination_info.key == *token_swap.token_b_account())
362        {
363            return Err(SwapError::IncorrectSwapAccount.into());
364        }
365        if *swap_source_info.key == *swap_destination_info.key {
366            return Err(SwapError::InvalidInput.into());
367        }
368        if swap_source_info.key == source_info.key {
369            return Err(SwapError::InvalidInput.into());
370        }
371        if swap_destination_info.key == destination_info.key {
372            return Err(SwapError::InvalidInput.into());
373        }
374        if *pool_mint_info.key != *token_swap.pool_mint() {
375            return Err(SwapError::IncorrectPoolMint.into());
376        }
377        if *pool_fee_account_info.key != *token_swap.pool_fee_account() {
378            return Err(SwapError::IncorrectFeeAccount.into());
379        }
380        if *token_program_info.key != *token_swap.token_program_id() {
381            return Err(SwapError::IncorrectTokenProgramId.into());
382        }
383
384        let source_account =
385            Self::unpack_token_account(swap_source_info, token_swap.token_program_id())?;
386        let dest_account =
387            Self::unpack_token_account(swap_destination_info, token_swap.token_program_id())?;
388        let pool_mint = Self::unpack_mint(pool_mint_info, token_swap.token_program_id())?;
389
390        let trade_direction = if *swap_source_info.key == *token_swap.token_a_account() {
391            TradeDirection::AtoB
392        } else {
393            TradeDirection::BtoA
394        };
395        let result = token_swap
396            .swap_curve()
397            .swap(
398                to_u128(amount_in)?,
399                to_u128(source_account.amount)?,
400                to_u128(dest_account.amount)?,
401                trade_direction,
402                token_swap.fees(),
403            )
404            .ok_or(SwapError::ZeroTradingTokens)?;
405        if result.destination_amount_swapped < to_u128(minimum_amount_out)? {
406            return Err(SwapError::ExceededSlippage.into());
407        }
408
409        let (swap_token_a_amount, swap_token_b_amount) = match trade_direction {
410            TradeDirection::AtoB => (
411                result.new_swap_source_amount,
412                result.new_swap_destination_amount,
413            ),
414            TradeDirection::BtoA => (
415                result.new_swap_destination_amount,
416                result.new_swap_source_amount,
417            ),
418        };
419
420        Self::token_transfer(
421            swap_info.key,
422            token_program_info.clone(),
423            source_info.clone(),
424            swap_source_info.clone(),
425            user_transfer_authority_info.clone(),
426            token_swap.bump_seed(),
427            to_u64(result.source_amount_swapped)?,
428        )?;
429
430        let mut pool_token_amount = token_swap
431            .swap_curve()
432            .withdraw_single_token_type_exact_out(
433                result.owner_fee,
434                swap_token_a_amount,
435                swap_token_b_amount,
436                to_u128(pool_mint.supply)?,
437                trade_direction,
438                token_swap.fees(),
439            )
440            .ok_or(SwapError::FeeCalculationFailure)?;
441
442        if pool_token_amount > 0 {
443            // Allow error to fall through
444            if let Ok(host_fee_account_info) = next_account_info(account_info_iter) {
445                let host_fee_account = Self::unpack_token_account(
446                    host_fee_account_info,
447                    token_swap.token_program_id(),
448                )?;
449                if *pool_mint_info.key != host_fee_account.mint {
450                    return Err(SwapError::IncorrectPoolMint.into());
451                }
452                let host_fee = token_swap
453                    .fees()
454                    .host_fee(pool_token_amount)
455                    .ok_or(SwapError::FeeCalculationFailure)?;
456                if host_fee > 0 {
457                    pool_token_amount = pool_token_amount
458                        .checked_sub(host_fee)
459                        .ok_or(SwapError::FeeCalculationFailure)?;
460                    Self::token_mint_to(
461                        swap_info.key,
462                        token_program_info.clone(),
463                        pool_mint_info.clone(),
464                        host_fee_account_info.clone(),
465                        authority_info.clone(),
466                        token_swap.bump_seed(),
467                        to_u64(host_fee)?,
468                    )?;
469                }
470            }
471            Self::token_mint_to(
472                swap_info.key,
473                token_program_info.clone(),
474                pool_mint_info.clone(),
475                pool_fee_account_info.clone(),
476                authority_info.clone(),
477                token_swap.bump_seed(),
478                to_u64(pool_token_amount)?,
479            )?;
480        }
481
482        Self::token_transfer(
483            swap_info.key,
484            token_program_info.clone(),
485            swap_destination_info.clone(),
486            destination_info.clone(),
487            authority_info.clone(),
488            token_swap.bump_seed(),
489            to_u64(result.destination_amount_swapped)?,
490        )?;
491
492        Ok(())
493    }
494
495    /// Processes an [DepositAllTokenTypes](enum.Instruction.html).
496    pub fn process_deposit_all_token_types(
497        program_id: &Pubkey,
498        pool_token_amount: u64,
499        maximum_token_a_amount: u64,
500        maximum_token_b_amount: u64,
501        accounts: &[AccountInfo],
502    ) -> ProgramResult {
503        let account_info_iter = &mut accounts.iter();
504        let swap_info = next_account_info(account_info_iter)?;
505        let authority_info = next_account_info(account_info_iter)?;
506        let user_transfer_authority_info = next_account_info(account_info_iter)?;
507        let source_a_info = next_account_info(account_info_iter)?;
508        let source_b_info = next_account_info(account_info_iter)?;
509        let token_a_info = next_account_info(account_info_iter)?;
510        let token_b_info = next_account_info(account_info_iter)?;
511        let pool_mint_info = next_account_info(account_info_iter)?;
512        let dest_info = next_account_info(account_info_iter)?;
513        let token_program_info = next_account_info(account_info_iter)?;
514
515        let token_swap = SwapVersion::unpack(&swap_info.data.borrow())?;
516        let calculator = &token_swap.swap_curve().calculator;
517        if !calculator.allows_deposits() {
518            return Err(SwapError::UnsupportedCurveOperation.into());
519        }
520        Self::check_accounts(
521            token_swap.as_ref(),
522            program_id,
523            swap_info,
524            authority_info,
525            token_a_info,
526            token_b_info,
527            pool_mint_info,
528            token_program_info,
529            Some(source_a_info),
530            Some(source_b_info),
531            None,
532        )?;
533
534        let token_a = Self::unpack_token_account(token_a_info, token_swap.token_program_id())?;
535        let token_b = Self::unpack_token_account(token_b_info, token_swap.token_program_id())?;
536        let pool_mint = Self::unpack_mint(pool_mint_info, token_swap.token_program_id())?;
537        let current_pool_mint_supply = to_u128(pool_mint.supply)?;
538        let (pool_token_amount, pool_mint_supply) = if current_pool_mint_supply > 0 {
539            (to_u128(pool_token_amount)?, current_pool_mint_supply)
540        } else {
541            (calculator.new_pool_supply(), calculator.new_pool_supply())
542        };
543
544        let results = calculator
545            .pool_tokens_to_trading_tokens(
546                pool_token_amount,
547                pool_mint_supply,
548                to_u128(token_a.amount)?,
549                to_u128(token_b.amount)?,
550                RoundDirection::Ceiling,
551            )
552            .ok_or(SwapError::ZeroTradingTokens)?;
553        let token_a_amount = to_u64(results.token_a_amount)?;
554        if token_a_amount > maximum_token_a_amount {
555            return Err(SwapError::ExceededSlippage.into());
556        }
557        if token_a_amount == 0 {
558            return Err(SwapError::ZeroTradingTokens.into());
559        }
560        let token_b_amount = to_u64(results.token_b_amount)?;
561        if token_b_amount > maximum_token_b_amount {
562            return Err(SwapError::ExceededSlippage.into());
563        }
564        if token_b_amount == 0 {
565            return Err(SwapError::ZeroTradingTokens.into());
566        }
567
568        let pool_token_amount = to_u64(pool_token_amount)?;
569
570        Self::token_transfer(
571            swap_info.key,
572            token_program_info.clone(),
573            source_a_info.clone(),
574            token_a_info.clone(),
575            user_transfer_authority_info.clone(),
576            token_swap.bump_seed(),
577            token_a_amount,
578        )?;
579        Self::token_transfer(
580            swap_info.key,
581            token_program_info.clone(),
582            source_b_info.clone(),
583            token_b_info.clone(),
584            user_transfer_authority_info.clone(),
585            token_swap.bump_seed(),
586            token_b_amount,
587        )?;
588        Self::token_mint_to(
589            swap_info.key,
590            token_program_info.clone(),
591            pool_mint_info.clone(),
592            dest_info.clone(),
593            authority_info.clone(),
594            token_swap.bump_seed(),
595            pool_token_amount,
596        )?;
597
598        Ok(())
599    }
600
601    /// Processes an [WithdrawAllTokenTypes](enum.Instruction.html).
602    pub fn process_withdraw_all_token_types(
603        program_id: &Pubkey,
604        pool_token_amount: u64,
605        minimum_token_a_amount: u64,
606        minimum_token_b_amount: u64,
607        accounts: &[AccountInfo],
608    ) -> ProgramResult {
609        let account_info_iter = &mut accounts.iter();
610        let swap_info = next_account_info(account_info_iter)?;
611        let authority_info = next_account_info(account_info_iter)?;
612        let user_transfer_authority_info = next_account_info(account_info_iter)?;
613        let pool_mint_info = next_account_info(account_info_iter)?;
614        let source_info = next_account_info(account_info_iter)?;
615        let token_a_info = next_account_info(account_info_iter)?;
616        let token_b_info = next_account_info(account_info_iter)?;
617        let dest_token_a_info = next_account_info(account_info_iter)?;
618        let dest_token_b_info = next_account_info(account_info_iter)?;
619        let pool_fee_account_info = next_account_info(account_info_iter)?;
620        let token_program_info = next_account_info(account_info_iter)?;
621
622        let token_swap = SwapVersion::unpack(&swap_info.data.borrow())?;
623        Self::check_accounts(
624            token_swap.as_ref(),
625            program_id,
626            swap_info,
627            authority_info,
628            token_a_info,
629            token_b_info,
630            pool_mint_info,
631            token_program_info,
632            Some(dest_token_a_info),
633            Some(dest_token_b_info),
634            Some(pool_fee_account_info),
635        )?;
636
637        let token_a = Self::unpack_token_account(token_a_info, token_swap.token_program_id())?;
638        let token_b = Self::unpack_token_account(token_b_info, token_swap.token_program_id())?;
639        let pool_mint = Self::unpack_mint(pool_mint_info, token_swap.token_program_id())?;
640
641        let calculator = &token_swap.swap_curve().calculator;
642
643        let withdraw_fee: u128 = if *pool_fee_account_info.key == *source_info.key {
644            // withdrawing from the fee account, don't assess withdraw fee
645            0
646        } else {
647            token_swap
648                .fees()
649                .owner_withdraw_fee(to_u128(pool_token_amount)?)
650                .ok_or(SwapError::FeeCalculationFailure)?
651        };
652        let pool_token_amount = to_u128(pool_token_amount)?
653            .checked_sub(withdraw_fee)
654            .ok_or(SwapError::CalculationFailure)?;
655
656        let results = calculator
657            .pool_tokens_to_trading_tokens(
658                pool_token_amount,
659                to_u128(pool_mint.supply)?,
660                to_u128(token_a.amount)?,
661                to_u128(token_b.amount)?,
662                RoundDirection::Floor,
663            )
664            .ok_or(SwapError::ZeroTradingTokens)?;
665        let token_a_amount = to_u64(results.token_a_amount)?;
666        let token_a_amount = std::cmp::min(token_a.amount, token_a_amount);
667        if token_a_amount < minimum_token_a_amount {
668            return Err(SwapError::ExceededSlippage.into());
669        }
670        if token_a_amount == 0 && token_a.amount != 0 {
671            return Err(SwapError::ZeroTradingTokens.into());
672        }
673        let token_b_amount = to_u64(results.token_b_amount)?;
674        let token_b_amount = std::cmp::min(token_b.amount, token_b_amount);
675        if token_b_amount < minimum_token_b_amount {
676            return Err(SwapError::ExceededSlippage.into());
677        }
678        if token_b_amount == 0 && token_b.amount != 0 {
679            return Err(SwapError::ZeroTradingTokens.into());
680        }
681
682        if withdraw_fee > 0 {
683            Self::token_transfer(
684                swap_info.key,
685                token_program_info.clone(),
686                source_info.clone(),
687                pool_fee_account_info.clone(),
688                user_transfer_authority_info.clone(),
689                token_swap.bump_seed(),
690                to_u64(withdraw_fee)?,
691            )?;
692        }
693        Self::token_burn(
694            swap_info.key,
695            token_program_info.clone(),
696            source_info.clone(),
697            pool_mint_info.clone(),
698            user_transfer_authority_info.clone(),
699            token_swap.bump_seed(),
700            to_u64(pool_token_amount)?,
701        )?;
702
703        if token_a_amount > 0 {
704            Self::token_transfer(
705                swap_info.key,
706                token_program_info.clone(),
707                token_a_info.clone(),
708                dest_token_a_info.clone(),
709                authority_info.clone(),
710                token_swap.bump_seed(),
711                token_a_amount,
712            )?;
713        }
714        if token_b_amount > 0 {
715            Self::token_transfer(
716                swap_info.key,
717                token_program_info.clone(),
718                token_b_info.clone(),
719                dest_token_b_info.clone(),
720                authority_info.clone(),
721                token_swap.bump_seed(),
722                token_b_amount,
723            )?;
724        }
725        Ok(())
726    }
727
728    /// Processes DepositSingleTokenTypeExactAmountIn
729    pub fn process_deposit_single_token_type_exact_amount_in(
730        program_id: &Pubkey,
731        source_token_amount: u64,
732        minimum_pool_token_amount: u64,
733        accounts: &[AccountInfo],
734    ) -> ProgramResult {
735        let account_info_iter = &mut accounts.iter();
736        let swap_info = next_account_info(account_info_iter)?;
737        let authority_info = next_account_info(account_info_iter)?;
738        let user_transfer_authority_info = next_account_info(account_info_iter)?;
739        let source_info = next_account_info(account_info_iter)?;
740        let swap_token_a_info = next_account_info(account_info_iter)?;
741        let swap_token_b_info = next_account_info(account_info_iter)?;
742        let pool_mint_info = next_account_info(account_info_iter)?;
743        let destination_info = next_account_info(account_info_iter)?;
744        let token_program_info = next_account_info(account_info_iter)?;
745
746        let token_swap = SwapVersion::unpack(&swap_info.data.borrow())?;
747        let source_account =
748            Self::unpack_token_account(source_info, token_swap.token_program_id())?;
749        let swap_token_a =
750            Self::unpack_token_account(swap_token_a_info, token_swap.token_program_id())?;
751        let swap_token_b =
752            Self::unpack_token_account(swap_token_b_info, token_swap.token_program_id())?;
753
754        let trade_direction = if source_account.mint == swap_token_a.mint {
755            TradeDirection::AtoB
756        } else if source_account.mint == swap_token_b.mint {
757            TradeDirection::BtoA
758        } else {
759            return Err(SwapError::IncorrectSwapAccount.into());
760        };
761
762        let (source_a_info, source_b_info) = match trade_direction {
763            TradeDirection::AtoB => (Some(source_info), None),
764            TradeDirection::BtoA => (None, Some(source_info)),
765        };
766
767        Self::check_accounts(
768            token_swap.as_ref(),
769            program_id,
770            swap_info,
771            authority_info,
772            swap_token_a_info,
773            swap_token_b_info,
774            pool_mint_info,
775            token_program_info,
776            source_a_info,
777            source_b_info,
778            None,
779        )?;
780
781        let pool_mint = Self::unpack_mint(pool_mint_info, token_swap.token_program_id())?;
782        let pool_mint_supply = to_u128(pool_mint.supply)?;
783        let pool_token_amount = if pool_mint_supply > 0 {
784            token_swap
785                .swap_curve()
786                .deposit_single_token_type(
787                    to_u128(source_token_amount)?,
788                    to_u128(swap_token_a.amount)?,
789                    to_u128(swap_token_b.amount)?,
790                    pool_mint_supply,
791                    trade_direction,
792                    token_swap.fees(),
793                )
794                .ok_or(SwapError::ZeroTradingTokens)?
795        } else {
796            token_swap.swap_curve().calculator.new_pool_supply()
797        };
798
799        let pool_token_amount = to_u64(pool_token_amount)?;
800        if pool_token_amount < minimum_pool_token_amount {
801            return Err(SwapError::ExceededSlippage.into());
802        }
803        if pool_token_amount == 0 {
804            return Err(SwapError::ZeroTradingTokens.into());
805        }
806
807        match trade_direction {
808            TradeDirection::AtoB => {
809                Self::token_transfer(
810                    swap_info.key,
811                    token_program_info.clone(),
812                    source_info.clone(),
813                    swap_token_a_info.clone(),
814                    user_transfer_authority_info.clone(),
815                    token_swap.bump_seed(),
816                    source_token_amount,
817                )?;
818            }
819            TradeDirection::BtoA => {
820                Self::token_transfer(
821                    swap_info.key,
822                    token_program_info.clone(),
823                    source_info.clone(),
824                    swap_token_b_info.clone(),
825                    user_transfer_authority_info.clone(),
826                    token_swap.bump_seed(),
827                    source_token_amount,
828                )?;
829            }
830        }
831        Self::token_mint_to(
832            swap_info.key,
833            token_program_info.clone(),
834            pool_mint_info.clone(),
835            destination_info.clone(),
836            authority_info.clone(),
837            token_swap.bump_seed(),
838            pool_token_amount,
839        )?;
840
841        Ok(())
842    }
843
844    /// Processes a [WithdrawSingleTokenTypeExactAmountOut](enum.Instruction.html).
845    pub fn process_withdraw_single_token_type_exact_amount_out(
846        program_id: &Pubkey,
847        destination_token_amount: u64,
848        maximum_pool_token_amount: u64,
849        accounts: &[AccountInfo],
850    ) -> ProgramResult {
851        let account_info_iter = &mut accounts.iter();
852        let swap_info = next_account_info(account_info_iter)?;
853        let authority_info = next_account_info(account_info_iter)?;
854        let user_transfer_authority_info = next_account_info(account_info_iter)?;
855        let pool_mint_info = next_account_info(account_info_iter)?;
856        let source_info = next_account_info(account_info_iter)?;
857        let swap_token_a_info = next_account_info(account_info_iter)?;
858        let swap_token_b_info = next_account_info(account_info_iter)?;
859        let destination_info = next_account_info(account_info_iter)?;
860        let pool_fee_account_info = next_account_info(account_info_iter)?;
861        let token_program_info = next_account_info(account_info_iter)?;
862
863        let token_swap = SwapVersion::unpack(&swap_info.data.borrow())?;
864        let destination_account =
865            Self::unpack_token_account(destination_info, token_swap.token_program_id())?;
866        let swap_token_a =
867            Self::unpack_token_account(swap_token_a_info, token_swap.token_program_id())?;
868        let swap_token_b =
869            Self::unpack_token_account(swap_token_b_info, token_swap.token_program_id())?;
870
871        let trade_direction = if destination_account.mint == swap_token_a.mint {
872            TradeDirection::AtoB
873        } else if destination_account.mint == swap_token_b.mint {
874            TradeDirection::BtoA
875        } else {
876            return Err(SwapError::IncorrectSwapAccount.into());
877        };
878
879        let (destination_a_info, destination_b_info) = match trade_direction {
880            TradeDirection::AtoB => (Some(destination_info), None),
881            TradeDirection::BtoA => (None, Some(destination_info)),
882        };
883        Self::check_accounts(
884            token_swap.as_ref(),
885            program_id,
886            swap_info,
887            authority_info,
888            swap_token_a_info,
889            swap_token_b_info,
890            pool_mint_info,
891            token_program_info,
892            destination_a_info,
893            destination_b_info,
894            Some(pool_fee_account_info),
895        )?;
896
897        let pool_mint = Self::unpack_mint(pool_mint_info, token_swap.token_program_id())?;
898        let pool_mint_supply = to_u128(pool_mint.supply)?;
899        let swap_token_a_amount = to_u128(swap_token_a.amount)?;
900        let swap_token_b_amount = to_u128(swap_token_b.amount)?;
901
902        let burn_pool_token_amount = token_swap
903            .swap_curve()
904            .withdraw_single_token_type_exact_out(
905                to_u128(destination_token_amount)?,
906                swap_token_a_amount,
907                swap_token_b_amount,
908                pool_mint_supply,
909                trade_direction,
910                token_swap.fees(),
911            )
912            .ok_or(SwapError::ZeroTradingTokens)?;
913
914        let withdraw_fee: u128 = if *pool_fee_account_info.key == *source_info.key {
915            // withdrawing from the fee account, don't assess withdraw fee
916            0
917        } else {
918            token_swap
919                .fees()
920                .owner_withdraw_fee(burn_pool_token_amount)
921                .ok_or(SwapError::FeeCalculationFailure)?
922        };
923        let pool_token_amount = burn_pool_token_amount
924            .checked_add(withdraw_fee)
925            .ok_or(SwapError::CalculationFailure)?;
926
927        if to_u64(pool_token_amount)? > maximum_pool_token_amount {
928            return Err(SwapError::ExceededSlippage.into());
929        }
930        if pool_token_amount == 0 {
931            return Err(SwapError::ZeroTradingTokens.into());
932        }
933
934        if withdraw_fee > 0 {
935            Self::token_transfer(
936                swap_info.key,
937                token_program_info.clone(),
938                source_info.clone(),
939                pool_fee_account_info.clone(),
940                user_transfer_authority_info.clone(),
941                token_swap.bump_seed(),
942                to_u64(withdraw_fee)?,
943            )?;
944        }
945        Self::token_burn(
946            swap_info.key,
947            token_program_info.clone(),
948            source_info.clone(),
949            pool_mint_info.clone(),
950            user_transfer_authority_info.clone(),
951            token_swap.bump_seed(),
952            to_u64(burn_pool_token_amount)?,
953        )?;
954
955        match trade_direction {
956            TradeDirection::AtoB => {
957                Self::token_transfer(
958                    swap_info.key,
959                    token_program_info.clone(),
960                    swap_token_a_info.clone(),
961                    destination_info.clone(),
962                    authority_info.clone(),
963                    token_swap.bump_seed(),
964                    destination_token_amount,
965                )?;
966            }
967            TradeDirection::BtoA => {
968                Self::token_transfer(
969                    swap_info.key,
970                    token_program_info.clone(),
971                    swap_token_b_info.clone(),
972                    destination_info.clone(),
973                    authority_info.clone(),
974                    token_swap.bump_seed(),
975                    destination_token_amount,
976                )?;
977            }
978        }
979
980        Ok(())
981    }
982
983    /// Processes an [Instruction](enum.Instruction.html).
984    pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
985        Self::process_with_constraints(program_id, accounts, input, &SWAP_CONSTRAINTS)
986    }
987
988    /// Processes an instruction given extra constraint
989    pub fn process_with_constraints(
990        program_id: &Pubkey,
991        accounts: &[AccountInfo],
992        input: &[u8],
993        swap_constraints: &Option<SwapConstraints>,
994    ) -> ProgramResult {
995        let instruction = SwapInstruction::unpack(input)?;
996        match instruction {
997            SwapInstruction::Initialize(Initialize { fees, swap_curve }) => {
998                msg!("Instruction: Init");
999                Self::process_initialize(program_id, fees, swap_curve, accounts, swap_constraints)
1000            }
1001            SwapInstruction::Swap(Swap {
1002                amount_in,
1003                minimum_amount_out,
1004            }) => {
1005                msg!("Instruction: Swap");
1006                Self::process_swap(program_id, amount_in, minimum_amount_out, accounts)
1007            }
1008            SwapInstruction::DepositAllTokenTypes(DepositAllTokenTypes {
1009                pool_token_amount,
1010                maximum_token_a_amount,
1011                maximum_token_b_amount,
1012            }) => {
1013                msg!("Instruction: DepositAllTokenTypes");
1014                Self::process_deposit_all_token_types(
1015                    program_id,
1016                    pool_token_amount,
1017                    maximum_token_a_amount,
1018                    maximum_token_b_amount,
1019                    accounts,
1020                )
1021            }
1022            SwapInstruction::WithdrawAllTokenTypes(WithdrawAllTokenTypes {
1023                pool_token_amount,
1024                minimum_token_a_amount,
1025                minimum_token_b_amount,
1026            }) => {
1027                msg!("Instruction: WithdrawAllTokenTypes");
1028                Self::process_withdraw_all_token_types(
1029                    program_id,
1030                    pool_token_amount,
1031                    minimum_token_a_amount,
1032                    minimum_token_b_amount,
1033                    accounts,
1034                )
1035            }
1036            SwapInstruction::DepositSingleTokenTypeExactAmountIn(
1037                DepositSingleTokenTypeExactAmountIn {
1038                    source_token_amount,
1039                    minimum_pool_token_amount,
1040                },
1041            ) => {
1042                msg!("Instruction: DepositSingleTokenTypeExactAmountIn");
1043                Self::process_deposit_single_token_type_exact_amount_in(
1044                    program_id,
1045                    source_token_amount,
1046                    minimum_pool_token_amount,
1047                    accounts,
1048                )
1049            }
1050            SwapInstruction::WithdrawSingleTokenTypeExactAmountOut(
1051                WithdrawSingleTokenTypeExactAmountOut {
1052                    destination_token_amount,
1053                    maximum_pool_token_amount,
1054                },
1055            ) => {
1056                msg!("Instruction: WithdrawSingleTokenTypeExactAmountOut");
1057                Self::process_withdraw_single_token_type_exact_amount_out(
1058                    program_id,
1059                    destination_token_amount,
1060                    maximum_pool_token_amount,
1061                    accounts,
1062                )
1063            }
1064        }
1065    }
1066}
1067
1068impl PrintProgramError for SwapError {
1069    fn print<E>(&self)
1070    where
1071        E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
1072    {
1073        match self {
1074            SwapError::AlreadyInUse => msg!("Error: Swap account already in use"),
1075            SwapError::InvalidProgramAddress => {
1076                msg!("Error: Invalid program address generated from bump seed and key")
1077            }
1078            SwapError::InvalidOwner => {
1079                msg!("Error: The input account owner is not the program address")
1080            }
1081            SwapError::InvalidOutputOwner => {
1082                msg!("Error: Output pool account owner cannot be the program address")
1083            }
1084            SwapError::ExpectedMint => msg!("Error: Deserialized account is not an GPL Token mint"),
1085            SwapError::ExpectedAccount => {
1086                msg!("Error: Deserialized account is not an GPL Token account")
1087            }
1088            SwapError::EmptySupply => msg!("Error: Input token account empty"),
1089            SwapError::InvalidSupply => msg!("Error: Pool token mint has a non-zero supply"),
1090            SwapError::RepeatedMint => msg!("Error: Swap input token accounts have the same mint"),
1091            SwapError::InvalidDelegate => msg!("Error: Token account has a delegate"),
1092            SwapError::InvalidInput => msg!("Error: InvalidInput"),
1093            SwapError::IncorrectSwapAccount => {
1094                msg!("Error: Address of the provided swap token account is incorrect")
1095            }
1096            SwapError::IncorrectPoolMint => {
1097                msg!("Error: Address of the provided pool token mint is incorrect")
1098            }
1099            SwapError::InvalidOutput => msg!("Error: InvalidOutput"),
1100            SwapError::CalculationFailure => msg!("Error: CalculationFailure"),
1101            SwapError::InvalidInstruction => msg!("Error: InvalidInstruction"),
1102            SwapError::ExceededSlippage => {
1103                msg!("Error: Swap instruction exceeds desired slippage limit")
1104            }
1105            SwapError::InvalidCloseAuthority => msg!("Error: Token account has a close authority"),
1106            SwapError::InvalidFreezeAuthority => {
1107                msg!("Error: Pool token mint has a freeze authority")
1108            }
1109            SwapError::IncorrectFeeAccount => msg!("Error: Pool fee token account incorrect"),
1110            SwapError::ZeroTradingTokens => {
1111                msg!("Error: Given pool token amount results in zero trading tokens")
1112            }
1113            SwapError::FeeCalculationFailure => msg!(
1114                "Error: The fee calculation failed due to overflow, underflow, or unexpected 0"
1115            ),
1116            SwapError::ConversionFailure => msg!("Error: Conversion to or from u64 failed."),
1117            SwapError::InvalidFee => {
1118                msg!("Error: The provided fee does not match the program owner's constraints")
1119            }
1120            SwapError::IncorrectTokenProgramId => {
1121                msg!("Error: The provided token program does not match the token program expected by the swap")
1122            }
1123            SwapError::UnsupportedCurveType => {
1124                msg!("Error: The provided curve type is not supported by the program owner")
1125            }
1126            SwapError::InvalidCurve => {
1127                msg!("Error: The provided curve parameters are invalid")
1128            }
1129            SwapError::UnsupportedCurveOperation => {
1130                msg!("Error: The operation cannot be performed on the given curve")
1131            }
1132        }
1133    }
1134}
1135
1136fn to_u128(val: u64) -> Result<u128, SwapError> {
1137    val.try_into().map_err(|_| SwapError::ConversionFailure)
1138}
1139
1140fn to_u64(val: u128) -> Result<u64, SwapError> {
1141    val.try_into().map_err(|_| SwapError::ConversionFailure)
1142}
1143
1144#[cfg(test)]
1145mod tests {
1146    use super::*;
1147    use crate::{
1148        curve::calculator::{CurveCalculator, INITIAL_SWAP_POOL_AMOUNT},
1149        curve::{
1150            base::CurveType, constant_price::ConstantPriceCurve,
1151            constant_product::ConstantProductCurve, offset::OffsetCurve,
1152        },
1153        instruction::{
1154            deposit_all_token_types, deposit_single_token_type_exact_amount_in, initialize, swap,
1155            withdraw_all_token_types, withdraw_single_token_type_exact_amount_out,
1156        },
1157    };
1158    use gemachain_program::{instruction::Instruction, program_stubs, rent::Rent};
1159    use gemachain_sdk::account::{create_account_for_test, create_is_signer_account_infos, Account};
1160    use gpl_token::{
1161        error::TokenError,
1162        instruction::{
1163            approve, initialize_account, initialize_mint, mint_to, revoke, set_authority,
1164            AuthorityType,
1165        },
1166    };
1167
1168    // Test program id for the swap program.
1169    const SWAP_PROGRAM_ID: Pubkey = Pubkey::new_from_array([2u8; 32]);
1170
1171    struct TestSyscallStubs {}
1172    impl program_stubs::SyscallStubs for TestSyscallStubs {
1173        fn gema_invoke_signed(
1174            &self,
1175            instruction: &Instruction,
1176            account_infos: &[AccountInfo],
1177            signers_seeds: &[&[&[u8]]],
1178        ) -> ProgramResult {
1179            msg!("TestSyscallStubs::gema_invoke_signed()");
1180
1181            let mut new_account_infos = vec![];
1182
1183            // mimic check for token program in accounts
1184            if !account_infos.iter().any(|x| *x.key == gpl_token::id()) {
1185                return Err(ProgramError::InvalidAccountData);
1186            }
1187
1188            for meta in instruction.accounts.iter() {
1189                for account_info in account_infos.iter() {
1190                    if meta.pubkey == *account_info.key {
1191                        let mut new_account_info = account_info.clone();
1192                        for seeds in signers_seeds.iter() {
1193                            let signer =
1194                                Pubkey::create_program_address(seeds, &SWAP_PROGRAM_ID).unwrap();
1195                            if *account_info.key == signer {
1196                                new_account_info.is_signer = true;
1197                            }
1198                        }
1199                        new_account_infos.push(new_account_info);
1200                    }
1201                }
1202            }
1203
1204            gpl_token::processor::Processor::process(
1205                &instruction.program_id,
1206                &new_account_infos,
1207                &instruction.data,
1208            )
1209        }
1210    }
1211
1212    fn test_syscall_stubs() {
1213        use std::sync::Once;
1214        static ONCE: Once = Once::new();
1215
1216        ONCE.call_once(|| {
1217            program_stubs::set_syscall_stubs(Box::new(TestSyscallStubs {}));
1218        });
1219    }
1220
1221    struct SwapAccountInfo {
1222        bump_seed: u8,
1223        authority_key: Pubkey,
1224        fees: Fees,
1225        swap_curve: SwapCurve,
1226        swap_key: Pubkey,
1227        swap_account: Account,
1228        pool_mint_key: Pubkey,
1229        pool_mint_account: Account,
1230        pool_fee_key: Pubkey,
1231        pool_fee_account: Account,
1232        pool_token_key: Pubkey,
1233        pool_token_account: Account,
1234        token_a_key: Pubkey,
1235        token_a_account: Account,
1236        token_a_mint_key: Pubkey,
1237        token_a_mint_account: Account,
1238        token_b_key: Pubkey,
1239        token_b_account: Account,
1240        token_b_mint_key: Pubkey,
1241        token_b_mint_account: Account,
1242    }
1243
1244    impl SwapAccountInfo {
1245        pub fn new(
1246            user_key: &Pubkey,
1247            fees: Fees,
1248            swap_curve: SwapCurve,
1249            token_a_amount: u64,
1250            token_b_amount: u64,
1251        ) -> Self {
1252            let swap_key = Pubkey::new_unique();
1253            let swap_account = Account::new(0, SwapVersion::LATEST_LEN, &SWAP_PROGRAM_ID);
1254            let (authority_key, bump_seed) =
1255                Pubkey::find_program_address(&[&swap_key.to_bytes()[..]], &SWAP_PROGRAM_ID);
1256
1257            let (pool_mint_key, mut pool_mint_account) =
1258                create_mint(&gpl_token::id(), &authority_key, None);
1259            let (pool_token_key, pool_token_account) = mint_token(
1260                &gpl_token::id(),
1261                &pool_mint_key,
1262                &mut pool_mint_account,
1263                &authority_key,
1264                user_key,
1265                0,
1266            );
1267            let (pool_fee_key, pool_fee_account) = mint_token(
1268                &gpl_token::id(),
1269                &pool_mint_key,
1270                &mut pool_mint_account,
1271                &authority_key,
1272                user_key,
1273                0,
1274            );
1275            let (token_a_mint_key, mut token_a_mint_account) =
1276                create_mint(&gpl_token::id(), user_key, None);
1277            let (token_a_key, token_a_account) = mint_token(
1278                &gpl_token::id(),
1279                &token_a_mint_key,
1280                &mut token_a_mint_account,
1281                user_key,
1282                &authority_key,
1283                token_a_amount,
1284            );
1285            let (token_b_mint_key, mut token_b_mint_account) =
1286                create_mint(&gpl_token::id(), user_key, None);
1287            let (token_b_key, token_b_account) = mint_token(
1288                &gpl_token::id(),
1289                &token_b_mint_key,
1290                &mut token_b_mint_account,
1291                user_key,
1292                &authority_key,
1293                token_b_amount,
1294            );
1295
1296            SwapAccountInfo {
1297                bump_seed,
1298                authority_key,
1299                fees,
1300                swap_curve,
1301                swap_key,
1302                swap_account,
1303                pool_mint_key,
1304                pool_mint_account,
1305                pool_fee_key,
1306                pool_fee_account,
1307                pool_token_key,
1308                pool_token_account,
1309                token_a_key,
1310                token_a_account,
1311                token_a_mint_key,
1312                token_a_mint_account,
1313                token_b_key,
1314                token_b_account,
1315                token_b_mint_key,
1316                token_b_mint_account,
1317            }
1318        }
1319
1320        pub fn initialize_swap(&mut self) -> ProgramResult {
1321            do_process_instruction(
1322                initialize(
1323                    &SWAP_PROGRAM_ID,
1324                    &gpl_token::id(),
1325                    &self.swap_key,
1326                    &self.authority_key,
1327                    &self.token_a_key,
1328                    &self.token_b_key,
1329                    &self.pool_mint_key,
1330                    &self.pool_fee_key,
1331                    &self.pool_token_key,
1332                    self.fees.clone(),
1333                    self.swap_curve.clone(),
1334                )
1335                .unwrap(),
1336                vec![
1337                    &mut self.swap_account,
1338                    &mut Account::default(),
1339                    &mut self.token_a_account,
1340                    &mut self.token_b_account,
1341                    &mut self.pool_mint_account,
1342                    &mut self.pool_fee_account,
1343                    &mut self.pool_token_account,
1344                    &mut Account::default(),
1345                ],
1346            )
1347        }
1348
1349        pub fn setup_token_accounts(
1350            &mut self,
1351            mint_owner: &Pubkey,
1352            account_owner: &Pubkey,
1353            a_amount: u64,
1354            b_amount: u64,
1355            pool_amount: u64,
1356        ) -> (Pubkey, Account, Pubkey, Account, Pubkey, Account) {
1357            let (token_a_key, token_a_account) = mint_token(
1358                &gpl_token::id(),
1359                &self.token_a_mint_key,
1360                &mut self.token_a_mint_account,
1361                mint_owner,
1362                account_owner,
1363                a_amount,
1364            );
1365            let (token_b_key, token_b_account) = mint_token(
1366                &gpl_token::id(),
1367                &self.token_b_mint_key,
1368                &mut self.token_b_mint_account,
1369                mint_owner,
1370                account_owner,
1371                b_amount,
1372            );
1373            let (pool_key, pool_account) = mint_token(
1374                &gpl_token::id(),
1375                &self.pool_mint_key,
1376                &mut self.pool_mint_account,
1377                &self.authority_key,
1378                account_owner,
1379                pool_amount,
1380            );
1381            (
1382                token_a_key,
1383                token_a_account,
1384                token_b_key,
1385                token_b_account,
1386                pool_key,
1387                pool_account,
1388            )
1389        }
1390
1391        fn get_token_account(&self, account_key: &Pubkey) -> &Account {
1392            if *account_key == self.token_a_key {
1393                return &self.token_a_account;
1394            } else if *account_key == self.token_b_key {
1395                return &self.token_b_account;
1396            }
1397            panic!("Could not find matching swap token account");
1398        }
1399
1400        fn set_token_account(&mut self, account_key: &Pubkey, account: Account) {
1401            if *account_key == self.token_a_key {
1402                self.token_a_account = account;
1403                return;
1404            } else if *account_key == self.token_b_key {
1405                self.token_b_account = account;
1406                return;
1407            }
1408            panic!("Could not find matching swap token account");
1409        }
1410
1411        #[allow(clippy::too_many_arguments)]
1412        pub fn swap(
1413            &mut self,
1414            user_key: &Pubkey,
1415            user_source_key: &Pubkey,
1416            mut user_source_account: &mut Account,
1417            swap_source_key: &Pubkey,
1418            swap_destination_key: &Pubkey,
1419            user_destination_key: &Pubkey,
1420            mut user_destination_account: &mut Account,
1421            amount_in: u64,
1422            minimum_amount_out: u64,
1423        ) -> ProgramResult {
1424            let user_transfer_key = Pubkey::new_unique();
1425            // approve moving from user source account
1426            do_process_instruction(
1427                approve(
1428                    &gpl_token::id(),
1429                    user_source_key,
1430                    &user_transfer_key,
1431                    user_key,
1432                    &[],
1433                    amount_in,
1434                )
1435                .unwrap(),
1436                vec![
1437                    &mut user_source_account,
1438                    &mut Account::default(),
1439                    &mut Account::default(),
1440                ],
1441            )
1442            .unwrap();
1443
1444            let mut swap_source_account = self.get_token_account(swap_source_key).clone();
1445            let mut swap_destination_account = self.get_token_account(swap_destination_key).clone();
1446
1447            // perform the swap
1448            do_process_instruction(
1449                swap(
1450                    &SWAP_PROGRAM_ID,
1451                    &gpl_token::id(),
1452                    &self.swap_key,
1453                    &self.authority_key,
1454                    &user_transfer_key,
1455                    user_source_key,
1456                    swap_source_key,
1457                    swap_destination_key,
1458                    user_destination_key,
1459                    &self.pool_mint_key,
1460                    &self.pool_fee_key,
1461                    None,
1462                    Swap {
1463                        amount_in,
1464                        minimum_amount_out,
1465                    },
1466                )
1467                .unwrap(),
1468                vec![
1469                    &mut self.swap_account,
1470                    &mut Account::default(),
1471                    &mut Account::default(),
1472                    &mut user_source_account,
1473                    &mut swap_source_account,
1474                    &mut swap_destination_account,
1475                    &mut user_destination_account,
1476                    &mut self.pool_mint_account,
1477                    &mut self.pool_fee_account,
1478                    &mut Account::default(),
1479                ],
1480            )?;
1481
1482            self.set_token_account(swap_source_key, swap_source_account);
1483            self.set_token_account(swap_destination_key, swap_destination_account);
1484
1485            Ok(())
1486        }
1487
1488        #[allow(clippy::too_many_arguments)]
1489        pub fn deposit_all_token_types(
1490            &mut self,
1491            depositor_key: &Pubkey,
1492            depositor_token_a_key: &Pubkey,
1493            mut depositor_token_a_account: &mut Account,
1494            depositor_token_b_key: &Pubkey,
1495            mut depositor_token_b_account: &mut Account,
1496            depositor_pool_key: &Pubkey,
1497            mut depositor_pool_account: &mut Account,
1498            pool_token_amount: u64,
1499            maximum_token_a_amount: u64,
1500            maximum_token_b_amount: u64,
1501        ) -> ProgramResult {
1502            let user_transfer_authority = Pubkey::new_unique();
1503            do_process_instruction(
1504                approve(
1505                    &gpl_token::id(),
1506                    depositor_token_a_key,
1507                    &user_transfer_authority,
1508                    depositor_key,
1509                    &[],
1510                    maximum_token_a_amount,
1511                )
1512                .unwrap(),
1513                vec![
1514                    &mut depositor_token_a_account,
1515                    &mut Account::default(),
1516                    &mut Account::default(),
1517                ],
1518            )
1519            .unwrap();
1520
1521            do_process_instruction(
1522                approve(
1523                    &gpl_token::id(),
1524                    depositor_token_b_key,
1525                    &user_transfer_authority,
1526                    depositor_key,
1527                    &[],
1528                    maximum_token_b_amount,
1529                )
1530                .unwrap(),
1531                vec![
1532                    &mut depositor_token_b_account,
1533                    &mut Account::default(),
1534                    &mut Account::default(),
1535                ],
1536            )
1537            .unwrap();
1538
1539            do_process_instruction(
1540                deposit_all_token_types(
1541                    &SWAP_PROGRAM_ID,
1542                    &gpl_token::id(),
1543                    &self.swap_key,
1544                    &self.authority_key,
1545                    &user_transfer_authority,
1546                    depositor_token_a_key,
1547                    depositor_token_b_key,
1548                    &self.token_a_key,
1549                    &self.token_b_key,
1550                    &self.pool_mint_key,
1551                    depositor_pool_key,
1552                    DepositAllTokenTypes {
1553                        pool_token_amount,
1554                        maximum_token_a_amount,
1555                        maximum_token_b_amount,
1556                    },
1557                )
1558                .unwrap(),
1559                vec![
1560                    &mut self.swap_account,
1561                    &mut Account::default(),
1562                    &mut Account::default(),
1563                    &mut depositor_token_a_account,
1564                    &mut depositor_token_b_account,
1565                    &mut self.token_a_account,
1566                    &mut self.token_b_account,
1567                    &mut self.pool_mint_account,
1568                    &mut depositor_pool_account,
1569                    &mut Account::default(),
1570                ],
1571            )
1572        }
1573
1574        #[allow(clippy::too_many_arguments)]
1575        pub fn withdraw_all_token_types(
1576            &mut self,
1577            user_key: &Pubkey,
1578            pool_key: &Pubkey,
1579            mut pool_account: &mut Account,
1580            token_a_key: &Pubkey,
1581            mut token_a_account: &mut Account,
1582            token_b_key: &Pubkey,
1583            mut token_b_account: &mut Account,
1584            pool_token_amount: u64,
1585            minimum_token_a_amount: u64,
1586            minimum_token_b_amount: u64,
1587        ) -> ProgramResult {
1588            let user_transfer_authority_key = Pubkey::new_unique();
1589            // approve user transfer authority to take out pool tokens
1590            do_process_instruction(
1591                approve(
1592                    &gpl_token::id(),
1593                    pool_key,
1594                    &user_transfer_authority_key,
1595                    user_key,
1596                    &[],
1597                    pool_token_amount,
1598                )
1599                .unwrap(),
1600                vec![
1601                    &mut pool_account,
1602                    &mut Account::default(),
1603                    &mut Account::default(),
1604                ],
1605            )
1606            .unwrap();
1607
1608            // withdraw token a and b correctly
1609            do_process_instruction(
1610                withdraw_all_token_types(
1611                    &SWAP_PROGRAM_ID,
1612                    &gpl_token::id(),
1613                    &self.swap_key,
1614                    &self.authority_key,
1615                    &user_transfer_authority_key,
1616                    &self.pool_mint_key,
1617                    &self.pool_fee_key,
1618                    pool_key,
1619                    &self.token_a_key,
1620                    &self.token_b_key,
1621                    token_a_key,
1622                    token_b_key,
1623                    WithdrawAllTokenTypes {
1624                        pool_token_amount,
1625                        minimum_token_a_amount,
1626                        minimum_token_b_amount,
1627                    },
1628                )
1629                .unwrap(),
1630                vec![
1631                    &mut self.swap_account,
1632                    &mut Account::default(),
1633                    &mut Account::default(),
1634                    &mut self.pool_mint_account,
1635                    &mut pool_account,
1636                    &mut self.token_a_account,
1637                    &mut self.token_b_account,
1638                    &mut token_a_account,
1639                    &mut token_b_account,
1640                    &mut self.pool_fee_account,
1641                    &mut Account::default(),
1642                ],
1643            )
1644        }
1645
1646        #[allow(clippy::too_many_arguments)]
1647        pub fn deposit_single_token_type_exact_amount_in(
1648            &mut self,
1649            depositor_key: &Pubkey,
1650            deposit_account_key: &Pubkey,
1651            mut deposit_token_account: &mut Account,
1652            deposit_pool_key: &Pubkey,
1653            mut deposit_pool_account: &mut Account,
1654            source_token_amount: u64,
1655            minimum_pool_token_amount: u64,
1656        ) -> ProgramResult {
1657            let user_transfer_authority_key = Pubkey::new_unique();
1658            do_process_instruction(
1659                approve(
1660                    &gpl_token::id(),
1661                    deposit_account_key,
1662                    &user_transfer_authority_key,
1663                    depositor_key,
1664                    &[],
1665                    source_token_amount,
1666                )
1667                .unwrap(),
1668                vec![
1669                    &mut deposit_token_account,
1670                    &mut Account::default(),
1671                    &mut Account::default(),
1672                ],
1673            )
1674            .unwrap();
1675
1676            do_process_instruction(
1677                deposit_single_token_type_exact_amount_in(
1678                    &SWAP_PROGRAM_ID,
1679                    &gpl_token::id(),
1680                    &self.swap_key,
1681                    &self.authority_key,
1682                    &user_transfer_authority_key,
1683                    deposit_account_key,
1684                    &self.token_a_key,
1685                    &self.token_b_key,
1686                    &self.pool_mint_key,
1687                    deposit_pool_key,
1688                    DepositSingleTokenTypeExactAmountIn {
1689                        source_token_amount,
1690                        minimum_pool_token_amount,
1691                    },
1692                )
1693                .unwrap(),
1694                vec![
1695                    &mut self.swap_account,
1696                    &mut Account::default(),
1697                    &mut Account::default(),
1698                    &mut deposit_token_account,
1699                    &mut self.token_a_account,
1700                    &mut self.token_b_account,
1701                    &mut self.pool_mint_account,
1702                    &mut deposit_pool_account,
1703                    &mut Account::default(),
1704                ],
1705            )
1706        }
1707
1708        #[allow(clippy::too_many_arguments)]
1709        pub fn withdraw_single_token_type_exact_amount_out(
1710            &mut self,
1711            user_key: &Pubkey,
1712            pool_key: &Pubkey,
1713            mut pool_account: &mut Account,
1714            destination_key: &Pubkey,
1715            mut destination_account: &mut Account,
1716            destination_token_amount: u64,
1717            maximum_pool_token_amount: u64,
1718        ) -> ProgramResult {
1719            let user_transfer_authority_key = Pubkey::new_unique();
1720            // approve user transfer authority to take out pool tokens
1721            do_process_instruction(
1722                approve(
1723                    &gpl_token::id(),
1724                    pool_key,
1725                    &user_transfer_authority_key,
1726                    user_key,
1727                    &[],
1728                    maximum_pool_token_amount,
1729                )
1730                .unwrap(),
1731                vec![
1732                    &mut pool_account,
1733                    &mut Account::default(),
1734                    &mut Account::default(),
1735                ],
1736            )
1737            .unwrap();
1738
1739            do_process_instruction(
1740                withdraw_single_token_type_exact_amount_out(
1741                    &SWAP_PROGRAM_ID,
1742                    &gpl_token::id(),
1743                    &self.swap_key,
1744                    &self.authority_key,
1745                    &user_transfer_authority_key,
1746                    &self.pool_mint_key,
1747                    &self.pool_fee_key,
1748                    pool_key,
1749                    &self.token_a_key,
1750                    &self.token_b_key,
1751                    destination_key,
1752                    WithdrawSingleTokenTypeExactAmountOut {
1753                        destination_token_amount,
1754                        maximum_pool_token_amount,
1755                    },
1756                )
1757                .unwrap(),
1758                vec![
1759                    &mut self.swap_account,
1760                    &mut Account::default(),
1761                    &mut Account::default(),
1762                    &mut self.pool_mint_account,
1763                    &mut pool_account,
1764                    &mut self.token_a_account,
1765                    &mut self.token_b_account,
1766                    &mut destination_account,
1767                    &mut self.pool_fee_account,
1768                    &mut Account::default(),
1769                ],
1770            )
1771        }
1772    }
1773
1774    fn mint_minimum_balance() -> u64 {
1775        Rent::default().minimum_balance(gpl_token::state::Mint::get_packed_len())
1776    }
1777
1778    fn account_minimum_balance() -> u64 {
1779        Rent::default().minimum_balance(gpl_token::state::Account::get_packed_len())
1780    }
1781
1782    fn do_process_instruction_with_fee_constraints(
1783        instruction: Instruction,
1784        accounts: Vec<&mut Account>,
1785        swap_constraints: &Option<SwapConstraints>,
1786    ) -> ProgramResult {
1787        test_syscall_stubs();
1788
1789        // approximate the logic in the actual runtime which runs the instruction
1790        // and only updates accounts if the instruction is successful
1791        let mut account_clones = accounts.iter().map(|x| (*x).clone()).collect::<Vec<_>>();
1792        let mut meta = instruction
1793            .accounts
1794            .iter()
1795            .zip(account_clones.iter_mut())
1796            .map(|(account_meta, account)| (&account_meta.pubkey, account_meta.is_signer, account))
1797            .collect::<Vec<_>>();
1798        let mut account_infos = create_is_signer_account_infos(&mut meta);
1799        let res = if instruction.program_id == SWAP_PROGRAM_ID {
1800            Processor::process_with_constraints(
1801                &instruction.program_id,
1802                &account_infos,
1803                &instruction.data,
1804                swap_constraints,
1805            )
1806        } else {
1807            gpl_token::processor::Processor::process(
1808                &instruction.program_id,
1809                &account_infos,
1810                &instruction.data,
1811            )
1812        };
1813
1814        if res.is_ok() {
1815            let mut account_metas = instruction
1816                .accounts
1817                .iter()
1818                .zip(accounts)
1819                .map(|(account_meta, account)| (&account_meta.pubkey, account))
1820                .collect::<Vec<_>>();
1821            for account_info in account_infos.iter_mut() {
1822                for account_meta in account_metas.iter_mut() {
1823                    if account_info.key == account_meta.0 {
1824                        let account = &mut account_meta.1;
1825                        account.owner = *account_info.owner;
1826                        account.carats = **account_info.carats.borrow();
1827                        account.data = account_info.data.borrow().to_vec();
1828                    }
1829                }
1830            }
1831        }
1832        res
1833    }
1834
1835    fn do_process_instruction(
1836        instruction: Instruction,
1837        accounts: Vec<&mut Account>,
1838    ) -> ProgramResult {
1839        do_process_instruction_with_fee_constraints(instruction, accounts, &SWAP_CONSTRAINTS)
1840    }
1841
1842    fn mint_token(
1843        program_id: &Pubkey,
1844        mint_key: &Pubkey,
1845        mut mint_account: &mut Account,
1846        mint_authority_key: &Pubkey,
1847        account_owner_key: &Pubkey,
1848        amount: u64,
1849    ) -> (Pubkey, Account) {
1850        let account_key = Pubkey::new_unique();
1851        let mut account_account = Account::new(
1852            account_minimum_balance(),
1853            gpl_token::state::Account::get_packed_len(),
1854            program_id,
1855        );
1856        let mut mint_authority_account = Account::default();
1857        let mut rent_sysvar_account = create_account_for_test(&Rent::free());
1858
1859        do_process_instruction(
1860            initialize_account(program_id, &account_key, mint_key, account_owner_key).unwrap(),
1861            vec![
1862                &mut account_account,
1863                &mut mint_account,
1864                &mut mint_authority_account,
1865                &mut rent_sysvar_account,
1866            ],
1867        )
1868        .unwrap();
1869
1870        if amount > 0 {
1871            do_process_instruction(
1872                mint_to(
1873                    program_id,
1874                    mint_key,
1875                    &account_key,
1876                    mint_authority_key,
1877                    &[],
1878                    amount,
1879                )
1880                .unwrap(),
1881                vec![
1882                    &mut mint_account,
1883                    &mut account_account,
1884                    &mut mint_authority_account,
1885                ],
1886            )
1887            .unwrap();
1888        }
1889
1890        (account_key, account_account)
1891    }
1892
1893    fn create_mint(
1894        program_id: &Pubkey,
1895        authority_key: &Pubkey,
1896        freeze_authority: Option<&Pubkey>,
1897    ) -> (Pubkey, Account) {
1898        let mint_key = Pubkey::new_unique();
1899        let mut mint_account = Account::new(
1900            mint_minimum_balance(),
1901            gpl_token::state::Mint::get_packed_len(),
1902            program_id,
1903        );
1904        let mut rent_sysvar_account = create_account_for_test(&Rent::free());
1905
1906        do_process_instruction(
1907            initialize_mint(program_id, &mint_key, authority_key, freeze_authority, 2).unwrap(),
1908            vec![&mut mint_account, &mut rent_sysvar_account],
1909        )
1910        .unwrap();
1911
1912        (mint_key, mint_account)
1913    }
1914
1915    #[test]
1916    fn test_token_program_id_error() {
1917        test_syscall_stubs();
1918        let swap_key = Pubkey::new_unique();
1919        let mut mint = (Pubkey::new_unique(), Account::default());
1920        let mut destination = (Pubkey::new_unique(), Account::default());
1921        let token_program = (gpl_token::id(), Account::default());
1922        let (authority_key, bump_seed) =
1923            Pubkey::find_program_address(&[&swap_key.to_bytes()[..]], &SWAP_PROGRAM_ID);
1924        let mut authority = (authority_key, Account::default());
1925        let swap_bytes = swap_key.to_bytes();
1926        let authority_signature_seeds = [&swap_bytes[..32], &[bump_seed]];
1927        let signers = &[&authority_signature_seeds[..]];
1928        let ix = mint_to(
1929            &token_program.0,
1930            &mint.0,
1931            &destination.0,
1932            &authority.0,
1933            &[],
1934            10,
1935        )
1936        .unwrap();
1937        let mint = (&mut mint).into();
1938        let destination = (&mut destination).into();
1939        let authority = (&mut authority).into();
1940
1941        let err = invoke_signed(&ix, &[mint, destination, authority], signers).unwrap_err();
1942        assert_eq!(err, ProgramError::InvalidAccountData);
1943    }
1944
1945    #[test]
1946    fn test_initialize() {
1947        let user_key = Pubkey::new_unique();
1948        let trade_fee_numerator = 1;
1949        let trade_fee_denominator = 2;
1950        let owner_trade_fee_numerator = 1;
1951        let owner_trade_fee_denominator = 10;
1952        let owner_withdraw_fee_numerator = 1;
1953        let owner_withdraw_fee_denominator = 5;
1954        let host_fee_numerator = 20;
1955        let host_fee_denominator = 100;
1956        let fees = Fees {
1957            trade_fee_numerator,
1958            trade_fee_denominator,
1959            owner_trade_fee_numerator,
1960            owner_trade_fee_denominator,
1961            owner_withdraw_fee_numerator,
1962            owner_withdraw_fee_denominator,
1963            host_fee_numerator,
1964            host_fee_denominator,
1965        };
1966
1967        let token_a_amount = 1000;
1968        let token_b_amount = 2000;
1969        let pool_token_amount = 10;
1970        let curve_type = CurveType::ConstantProduct;
1971        let swap_curve = SwapCurve {
1972            curve_type,
1973            calculator: Box::new(ConstantProductCurve {}),
1974        };
1975
1976        let mut accounts =
1977            SwapAccountInfo::new(&user_key, fees, swap_curve, token_a_amount, token_b_amount);
1978
1979        // uninitialized token a account
1980        {
1981            let old_account = accounts.token_a_account;
1982            accounts.token_a_account = Account::new(0, 0, &gpl_token::id());
1983            assert_eq!(
1984                Err(SwapError::ExpectedAccount.into()),
1985                accounts.initialize_swap()
1986            );
1987            accounts.token_a_account = old_account;
1988        }
1989
1990        // uninitialized token b account
1991        {
1992            let old_account = accounts.token_b_account;
1993            accounts.token_b_account = Account::new(0, 0, &gpl_token::id());
1994            assert_eq!(
1995                Err(SwapError::ExpectedAccount.into()),
1996                accounts.initialize_swap()
1997            );
1998            accounts.token_b_account = old_account;
1999        }
2000
2001        // uninitialized pool mint
2002        {
2003            let old_account = accounts.pool_mint_account;
2004            accounts.pool_mint_account = Account::new(0, 0, &gpl_token::id());
2005            assert_eq!(
2006                Err(SwapError::ExpectedMint.into()),
2007                accounts.initialize_swap()
2008            );
2009            accounts.pool_mint_account = old_account;
2010        }
2011
2012        // token A account owner is not swap authority
2013        {
2014            let (_token_a_key, token_a_account) = mint_token(
2015                &gpl_token::id(),
2016                &accounts.token_a_mint_key,
2017                &mut accounts.token_a_mint_account,
2018                &user_key,
2019                &user_key,
2020                0,
2021            );
2022            let old_account = accounts.token_a_account;
2023            accounts.token_a_account = token_a_account;
2024            assert_eq!(
2025                Err(SwapError::InvalidOwner.into()),
2026                accounts.initialize_swap()
2027            );
2028            accounts.token_a_account = old_account;
2029        }
2030
2031        // token B account owner is not swap authority
2032        {
2033            let (_token_b_key, token_b_account) = mint_token(
2034                &gpl_token::id(),
2035                &accounts.token_b_mint_key,
2036                &mut accounts.token_b_mint_account,
2037                &user_key,
2038                &user_key,
2039                0,
2040            );
2041            let old_account = accounts.token_b_account;
2042            accounts.token_b_account = token_b_account;
2043            assert_eq!(
2044                Err(SwapError::InvalidOwner.into()),
2045                accounts.initialize_swap()
2046            );
2047            accounts.token_b_account = old_account;
2048        }
2049
2050        // pool token account owner is swap authority
2051        {
2052            let (_pool_token_key, pool_token_account) = mint_token(
2053                &gpl_token::id(),
2054                &accounts.pool_mint_key,
2055                &mut accounts.pool_mint_account,
2056                &accounts.authority_key,
2057                &accounts.authority_key,
2058                0,
2059            );
2060            let old_account = accounts.pool_token_account;
2061            accounts.pool_token_account = pool_token_account;
2062            assert_eq!(
2063                Err(SwapError::InvalidOutputOwner.into()),
2064                accounts.initialize_swap()
2065            );
2066            accounts.pool_token_account = old_account;
2067        }
2068
2069        // pool fee account owner is swap authority
2070        {
2071            let (_pool_fee_key, pool_fee_account) = mint_token(
2072                &gpl_token::id(),
2073                &accounts.pool_mint_key,
2074                &mut accounts.pool_mint_account,
2075                &accounts.authority_key,
2076                &accounts.authority_key,
2077                0,
2078            );
2079            let old_account = accounts.pool_fee_account;
2080            accounts.pool_fee_account = pool_fee_account;
2081            assert_eq!(
2082                Err(SwapError::InvalidOutputOwner.into()),
2083                accounts.initialize_swap()
2084            );
2085            accounts.pool_fee_account = old_account;
2086        }
2087
2088        // pool mint authority is not swap authority
2089        {
2090            let (_pool_mint_key, pool_mint_account) =
2091                create_mint(&gpl_token::id(), &user_key, None);
2092            let old_mint = accounts.pool_mint_account;
2093            accounts.pool_mint_account = pool_mint_account;
2094            assert_eq!(
2095                Err(SwapError::InvalidOwner.into()),
2096                accounts.initialize_swap()
2097            );
2098            accounts.pool_mint_account = old_mint;
2099        }
2100
2101        // pool mint token has freeze authority
2102        {
2103            let (_pool_mint_key, pool_mint_account) =
2104                create_mint(&gpl_token::id(), &accounts.authority_key, Some(&user_key));
2105            let old_mint = accounts.pool_mint_account;
2106            accounts.pool_mint_account = pool_mint_account;
2107            assert_eq!(
2108                Err(SwapError::InvalidFreezeAuthority.into()),
2109                accounts.initialize_swap()
2110            );
2111            accounts.pool_mint_account = old_mint;
2112        }
2113
2114        // token A account owned by wrong program
2115        {
2116            let (_token_a_key, mut token_a_account) = mint_token(
2117                &gpl_token::id(),
2118                &accounts.token_a_mint_key,
2119                &mut accounts.token_a_mint_account,
2120                &user_key,
2121                &accounts.authority_key,
2122                token_a_amount,
2123            );
2124            token_a_account.owner = SWAP_PROGRAM_ID;
2125            let old_account = accounts.token_a_account;
2126            accounts.token_a_account = token_a_account;
2127            assert_eq!(
2128                Err(SwapError::IncorrectTokenProgramId.into()),
2129                accounts.initialize_swap()
2130            );
2131            accounts.token_a_account = old_account;
2132        }
2133
2134        // token B account owned by wrong program
2135        {
2136            let (_token_b_key, mut token_b_account) = mint_token(
2137                &gpl_token::id(),
2138                &accounts.token_b_mint_key,
2139                &mut accounts.token_b_mint_account,
2140                &user_key,
2141                &accounts.authority_key,
2142                token_b_amount,
2143            );
2144            token_b_account.owner = SWAP_PROGRAM_ID;
2145            let old_account = accounts.token_b_account;
2146            accounts.token_b_account = token_b_account;
2147            assert_eq!(
2148                Err(SwapError::IncorrectTokenProgramId.into()),
2149                accounts.initialize_swap()
2150            );
2151            accounts.token_b_account = old_account;
2152        }
2153
2154        // empty token A account
2155        {
2156            let (_token_a_key, token_a_account) = mint_token(
2157                &gpl_token::id(),
2158                &accounts.token_a_mint_key,
2159                &mut accounts.token_a_mint_account,
2160                &user_key,
2161                &accounts.authority_key,
2162                0,
2163            );
2164            let old_account = accounts.token_a_account;
2165            accounts.token_a_account = token_a_account;
2166            assert_eq!(
2167                Err(SwapError::EmptySupply.into()),
2168                accounts.initialize_swap()
2169            );
2170            accounts.token_a_account = old_account;
2171        }
2172
2173        // empty token B account
2174        {
2175            let (_token_b_key, token_b_account) = mint_token(
2176                &gpl_token::id(),
2177                &accounts.token_b_mint_key,
2178                &mut accounts.token_b_mint_account,
2179                &user_key,
2180                &accounts.authority_key,
2181                0,
2182            );
2183            let old_account = accounts.token_b_account;
2184            accounts.token_b_account = token_b_account;
2185            assert_eq!(
2186                Err(SwapError::EmptySupply.into()),
2187                accounts.initialize_swap()
2188            );
2189            accounts.token_b_account = old_account;
2190        }
2191
2192        // invalid pool tokens
2193        {
2194            let old_mint = accounts.pool_mint_account;
2195            let old_pool_account = accounts.pool_token_account;
2196
2197            let (_pool_mint_key, pool_mint_account) =
2198                create_mint(&gpl_token::id(), &accounts.authority_key, None);
2199            accounts.pool_mint_account = pool_mint_account;
2200
2201            let (_empty_pool_token_key, empty_pool_token_account) = mint_token(
2202                &gpl_token::id(),
2203                &accounts.pool_mint_key,
2204                &mut accounts.pool_mint_account,
2205                &accounts.authority_key,
2206                &user_key,
2207                0,
2208            );
2209
2210            let (_pool_token_key, pool_token_account) = mint_token(
2211                &gpl_token::id(),
2212                &accounts.pool_mint_key,
2213                &mut accounts.pool_mint_account,
2214                &accounts.authority_key,
2215                &user_key,
2216                pool_token_amount,
2217            );
2218
2219            // non-empty pool token account
2220            accounts.pool_token_account = pool_token_account;
2221            assert_eq!(
2222                Err(SwapError::InvalidSupply.into()),
2223                accounts.initialize_swap()
2224            );
2225
2226            // pool tokens already in circulation
2227            accounts.pool_token_account = empty_pool_token_account;
2228            assert_eq!(
2229                Err(SwapError::InvalidSupply.into()),
2230                accounts.initialize_swap()
2231            );
2232
2233            accounts.pool_mint_account = old_mint;
2234            accounts.pool_token_account = old_pool_account;
2235        }
2236
2237        // pool fee account has wrong mint
2238        {
2239            let (_pool_fee_key, pool_fee_account) = mint_token(
2240                &gpl_token::id(),
2241                &accounts.token_a_mint_key,
2242                &mut accounts.token_a_mint_account,
2243                &user_key,
2244                &user_key,
2245                0,
2246            );
2247            let old_account = accounts.pool_fee_account;
2248            accounts.pool_fee_account = pool_fee_account;
2249            assert_eq!(
2250                Err(SwapError::IncorrectPoolMint.into()),
2251                accounts.initialize_swap()
2252            );
2253            accounts.pool_fee_account = old_account;
2254        }
2255
2256        // token A account is delegated
2257        {
2258            do_process_instruction(
2259                approve(
2260                    &gpl_token::id(),
2261                    &accounts.token_a_key,
2262                    &user_key,
2263                    &accounts.authority_key,
2264                    &[],
2265                    1,
2266                )
2267                .unwrap(),
2268                vec![
2269                    &mut accounts.token_a_account,
2270                    &mut Account::default(),
2271                    &mut Account::default(),
2272                ],
2273            )
2274            .unwrap();
2275            assert_eq!(
2276                Err(SwapError::InvalidDelegate.into()),
2277                accounts.initialize_swap()
2278            );
2279
2280            do_process_instruction(
2281                revoke(
2282                    &gpl_token::id(),
2283                    &accounts.token_a_key,
2284                    &accounts.authority_key,
2285                    &[],
2286                )
2287                .unwrap(),
2288                vec![&mut accounts.token_a_account, &mut Account::default()],
2289            )
2290            .unwrap();
2291        }
2292
2293        // token B account is delegated
2294        {
2295            do_process_instruction(
2296                approve(
2297                    &gpl_token::id(),
2298                    &accounts.token_b_key,
2299                    &user_key,
2300                    &accounts.authority_key,
2301                    &[],
2302                    1,
2303                )
2304                .unwrap(),
2305                vec![
2306                    &mut accounts.token_b_account,
2307                    &mut Account::default(),
2308                    &mut Account::default(),
2309                ],
2310            )
2311            .unwrap();
2312            assert_eq!(
2313                Err(SwapError::InvalidDelegate.into()),
2314                accounts.initialize_swap()
2315            );
2316
2317            do_process_instruction(
2318                revoke(
2319                    &gpl_token::id(),
2320                    &accounts.token_b_key,
2321                    &accounts.authority_key,
2322                    &[],
2323                )
2324                .unwrap(),
2325                vec![&mut accounts.token_b_account, &mut Account::default()],
2326            )
2327            .unwrap();
2328        }
2329
2330        // token A account has close authority
2331        {
2332            do_process_instruction(
2333                set_authority(
2334                    &gpl_token::id(),
2335                    &accounts.token_a_key,
2336                    Some(&user_key),
2337                    AuthorityType::CloseAccount,
2338                    &accounts.authority_key,
2339                    &[],
2340                )
2341                .unwrap(),
2342                vec![&mut accounts.token_a_account, &mut Account::default()],
2343            )
2344            .unwrap();
2345            assert_eq!(
2346                Err(SwapError::InvalidCloseAuthority.into()),
2347                accounts.initialize_swap()
2348            );
2349
2350            do_process_instruction(
2351                set_authority(
2352                    &gpl_token::id(),
2353                    &accounts.token_a_key,
2354                    None,
2355                    AuthorityType::CloseAccount,
2356                    &user_key,
2357                    &[],
2358                )
2359                .unwrap(),
2360                vec![&mut accounts.token_a_account, &mut Account::default()],
2361            )
2362            .unwrap();
2363        }
2364
2365        // token B account has close authority
2366        {
2367            do_process_instruction(
2368                set_authority(
2369                    &gpl_token::id(),
2370                    &accounts.token_b_key,
2371                    Some(&user_key),
2372                    AuthorityType::CloseAccount,
2373                    &accounts.authority_key,
2374                    &[],
2375                )
2376                .unwrap(),
2377                vec![&mut accounts.token_b_account, &mut Account::default()],
2378            )
2379            .unwrap();
2380            assert_eq!(
2381                Err(SwapError::InvalidCloseAuthority.into()),
2382                accounts.initialize_swap()
2383            );
2384
2385            do_process_instruction(
2386                set_authority(
2387                    &gpl_token::id(),
2388                    &accounts.token_b_key,
2389                    None,
2390                    AuthorityType::CloseAccount,
2391                    &user_key,
2392                    &[],
2393                )
2394                .unwrap(),
2395                vec![&mut accounts.token_b_account, &mut Account::default()],
2396            )
2397            .unwrap();
2398        }
2399
2400        // wrong token program id
2401        {
2402            let wrong_program_id = Pubkey::new_unique();
2403            assert_eq!(
2404                Err(SwapError::IncorrectTokenProgramId.into()),
2405                do_process_instruction(
2406                    initialize(
2407                        &SWAP_PROGRAM_ID,
2408                        &wrong_program_id,
2409                        &accounts.swap_key,
2410                        &accounts.authority_key,
2411                        &accounts.token_a_key,
2412                        &accounts.token_b_key,
2413                        &accounts.pool_mint_key,
2414                        &accounts.pool_fee_key,
2415                        &accounts.pool_token_key,
2416                        accounts.fees.clone(),
2417                        accounts.swap_curve.clone(),
2418                    )
2419                    .unwrap(),
2420                    vec![
2421                        &mut accounts.swap_account,
2422                        &mut Account::default(),
2423                        &mut accounts.token_a_account,
2424                        &mut accounts.token_b_account,
2425                        &mut accounts.pool_mint_account,
2426                        &mut accounts.pool_fee_account,
2427                        &mut accounts.pool_token_account,
2428                        &mut Account::default(),
2429                    ],
2430                )
2431            );
2432        }
2433
2434        // create swap with same token A and B
2435        {
2436            let (_token_a_repeat_key, token_a_repeat_account) = mint_token(
2437                &gpl_token::id(),
2438                &accounts.token_a_mint_key,
2439                &mut accounts.token_a_mint_account,
2440                &user_key,
2441                &accounts.authority_key,
2442                10,
2443            );
2444            let old_account = accounts.token_b_account;
2445            accounts.token_b_account = token_a_repeat_account;
2446            assert_eq!(
2447                Err(SwapError::RepeatedMint.into()),
2448                accounts.initialize_swap()
2449            );
2450            accounts.token_b_account = old_account;
2451        }
2452
2453        // create valid swap
2454        accounts.initialize_swap().unwrap();
2455
2456        // create invalid flat swap
2457        {
2458            let token_b_price = 0;
2459            let fees = Fees {
2460                trade_fee_numerator,
2461                trade_fee_denominator,
2462                owner_trade_fee_numerator,
2463                owner_trade_fee_denominator,
2464                owner_withdraw_fee_numerator,
2465                owner_withdraw_fee_denominator,
2466                host_fee_numerator,
2467                host_fee_denominator,
2468            };
2469            let swap_curve = SwapCurve {
2470                curve_type: CurveType::ConstantPrice,
2471                calculator: Box::new(ConstantPriceCurve { token_b_price }),
2472            };
2473            let mut accounts =
2474                SwapAccountInfo::new(&user_key, fees, swap_curve, token_a_amount, token_b_amount);
2475            assert_eq!(
2476                Err(SwapError::InvalidCurve.into()),
2477                accounts.initialize_swap()
2478            );
2479        }
2480
2481        // create valid flat swap
2482        {
2483            let fees = Fees {
2484                trade_fee_numerator,
2485                trade_fee_denominator,
2486                owner_trade_fee_numerator,
2487                owner_trade_fee_denominator,
2488                owner_withdraw_fee_numerator,
2489                owner_withdraw_fee_denominator,
2490                host_fee_numerator,
2491                host_fee_denominator,
2492            };
2493            let token_b_price = 10_000;
2494            let swap_curve = SwapCurve {
2495                curve_type: CurveType::ConstantPrice,
2496                calculator: Box::new(ConstantPriceCurve { token_b_price }),
2497            };
2498            let mut accounts =
2499                SwapAccountInfo::new(&user_key, fees, swap_curve, token_a_amount, token_b_amount);
2500            accounts.initialize_swap().unwrap();
2501        }
2502
2503        // create invalid offset swap
2504        {
2505            let token_b_offset = 0;
2506            let fees = Fees {
2507                trade_fee_numerator,
2508                trade_fee_denominator,
2509                owner_trade_fee_numerator,
2510                owner_trade_fee_denominator,
2511                owner_withdraw_fee_numerator,
2512                owner_withdraw_fee_denominator,
2513                host_fee_numerator,
2514                host_fee_denominator,
2515            };
2516            let swap_curve = SwapCurve {
2517                curve_type: CurveType::Offset,
2518                calculator: Box::new(OffsetCurve { token_b_offset }),
2519            };
2520            let mut accounts =
2521                SwapAccountInfo::new(&user_key, fees, swap_curve, token_a_amount, token_b_amount);
2522            assert_eq!(
2523                Err(SwapError::InvalidCurve.into()),
2524                accounts.initialize_swap()
2525            );
2526        }
2527
2528        // create valid offset swap
2529        {
2530            let token_b_offset = 10;
2531            let fees = Fees {
2532                trade_fee_numerator,
2533                trade_fee_denominator,
2534                owner_trade_fee_numerator,
2535                owner_trade_fee_denominator,
2536                owner_withdraw_fee_numerator,
2537                owner_withdraw_fee_denominator,
2538                host_fee_numerator,
2539                host_fee_denominator,
2540            };
2541            let swap_curve = SwapCurve {
2542                curve_type: CurveType::Offset,
2543                calculator: Box::new(OffsetCurve { token_b_offset }),
2544            };
2545            let mut accounts =
2546                SwapAccountInfo::new(&user_key, fees, swap_curve, token_a_amount, token_b_amount);
2547            accounts.initialize_swap().unwrap();
2548        }
2549
2550        // wrong owner key in constraint
2551        {
2552            let new_key = Pubkey::new_unique();
2553            let trade_fee_numerator = 25;
2554            let trade_fee_denominator = 10000;
2555            let owner_trade_fee_numerator = 5;
2556            let owner_trade_fee_denominator = 10000;
2557            let host_fee_numerator = 20;
2558            let host_fee_denominator = 100;
2559            let fees = Fees {
2560                trade_fee_numerator,
2561                trade_fee_denominator,
2562                owner_trade_fee_numerator,
2563                owner_trade_fee_denominator,
2564                owner_withdraw_fee_numerator,
2565                owner_withdraw_fee_denominator,
2566                host_fee_numerator,
2567                host_fee_denominator,
2568            };
2569            let curve = ConstantProductCurve {};
2570            let swap_curve = SwapCurve {
2571                curve_type: CurveType::ConstantProduct,
2572                calculator: Box::new(curve),
2573            };
2574            let owner_key = &new_key.to_string();
2575            let valid_curve_types = &[CurveType::ConstantProduct];
2576            let constraints = Some(SwapConstraints {
2577                owner_key,
2578                valid_curve_types,
2579                fees: &fees,
2580            });
2581            let mut accounts = SwapAccountInfo::new(
2582                &user_key,
2583                fees.clone(),
2584                swap_curve,
2585                token_a_amount,
2586                token_b_amount,
2587            );
2588            assert_eq!(
2589                Err(SwapError::InvalidOwner.into()),
2590                do_process_instruction_with_fee_constraints(
2591                    initialize(
2592                        &SWAP_PROGRAM_ID,
2593                        &gpl_token::id(),
2594                        &accounts.swap_key,
2595                        &accounts.authority_key,
2596                        &accounts.token_a_key,
2597                        &accounts.token_b_key,
2598                        &accounts.pool_mint_key,
2599                        &accounts.pool_fee_key,
2600                        &accounts.pool_token_key,
2601                        accounts.fees.clone(),
2602                        accounts.swap_curve.clone(),
2603                    )
2604                    .unwrap(),
2605                    vec![
2606                        &mut accounts.swap_account,
2607                        &mut Account::default(),
2608                        &mut accounts.token_a_account,
2609                        &mut accounts.token_b_account,
2610                        &mut accounts.pool_mint_account,
2611                        &mut accounts.pool_fee_account,
2612                        &mut accounts.pool_token_account,
2613                        &mut Account::default(),
2614                    ],
2615                    &constraints,
2616                )
2617            );
2618        }
2619
2620        // wrong fee in constraint
2621        {
2622            let trade_fee_numerator = 25;
2623            let trade_fee_denominator = 10000;
2624            let owner_trade_fee_numerator = 5;
2625            let owner_trade_fee_denominator = 10000;
2626            let host_fee_numerator = 20;
2627            let host_fee_denominator = 100;
2628            let fees = Fees {
2629                trade_fee_numerator,
2630                trade_fee_denominator,
2631                owner_trade_fee_numerator,
2632                owner_trade_fee_denominator,
2633                owner_withdraw_fee_numerator,
2634                owner_withdraw_fee_denominator,
2635                host_fee_numerator,
2636                host_fee_denominator,
2637            };
2638            let curve = ConstantProductCurve {};
2639            let swap_curve = SwapCurve {
2640                curve_type: CurveType::ConstantProduct,
2641                calculator: Box::new(curve),
2642            };
2643            let owner_key = &user_key.to_string();
2644            let valid_curve_types = &[CurveType::ConstantProduct];
2645            let constraints = Some(SwapConstraints {
2646                owner_key,
2647                valid_curve_types,
2648                fees: &fees,
2649            });
2650            let mut bad_fees = fees.clone();
2651            bad_fees.trade_fee_numerator = trade_fee_numerator - 1;
2652            let mut accounts = SwapAccountInfo::new(
2653                &user_key,
2654                bad_fees,
2655                swap_curve,
2656                token_a_amount,
2657                token_b_amount,
2658            );
2659            assert_eq!(
2660                Err(SwapError::InvalidFee.into()),
2661                do_process_instruction_with_fee_constraints(
2662                    initialize(
2663                        &SWAP_PROGRAM_ID,
2664                        &gpl_token::id(),
2665                        &accounts.swap_key,
2666                        &accounts.authority_key,
2667                        &accounts.token_a_key,
2668                        &accounts.token_b_key,
2669                        &accounts.pool_mint_key,
2670                        &accounts.pool_fee_key,
2671                        &accounts.pool_token_key,
2672                        accounts.fees.clone(),
2673                        accounts.swap_curve.clone(),
2674                    )
2675                    .unwrap(),
2676                    vec![
2677                        &mut accounts.swap_account,
2678                        &mut Account::default(),
2679                        &mut accounts.token_a_account,
2680                        &mut accounts.token_b_account,
2681                        &mut accounts.pool_mint_account,
2682                        &mut accounts.pool_fee_account,
2683                        &mut accounts.pool_token_account,
2684                        &mut Account::default(),
2685                    ],
2686                    &constraints,
2687                )
2688            );
2689        }
2690
2691        // create valid swap with constraints
2692        {
2693            let trade_fee_numerator = 25;
2694            let trade_fee_denominator = 10000;
2695            let owner_trade_fee_numerator = 5;
2696            let owner_trade_fee_denominator = 10000;
2697            let host_fee_numerator = 20;
2698            let host_fee_denominator = 100;
2699            let fees = Fees {
2700                trade_fee_numerator,
2701                trade_fee_denominator,
2702                owner_trade_fee_numerator,
2703                owner_trade_fee_denominator,
2704                owner_withdraw_fee_numerator,
2705                owner_withdraw_fee_denominator,
2706                host_fee_numerator,
2707                host_fee_denominator,
2708            };
2709            let curve = ConstantProductCurve {};
2710            let swap_curve = SwapCurve {
2711                curve_type: CurveType::ConstantProduct,
2712                calculator: Box::new(curve),
2713            };
2714            let owner_key = &user_key.to_string();
2715            let valid_curve_types = &[CurveType::ConstantProduct];
2716            let constraints = Some(SwapConstraints {
2717                owner_key,
2718                valid_curve_types,
2719                fees: &fees,
2720            });
2721            let mut accounts = SwapAccountInfo::new(
2722                &user_key,
2723                fees.clone(),
2724                swap_curve,
2725                token_a_amount,
2726                token_b_amount,
2727            );
2728            do_process_instruction_with_fee_constraints(
2729                initialize(
2730                    &SWAP_PROGRAM_ID,
2731                    &gpl_token::id(),
2732                    &accounts.swap_key,
2733                    &accounts.authority_key,
2734                    &accounts.token_a_key,
2735                    &accounts.token_b_key,
2736                    &accounts.pool_mint_key,
2737                    &accounts.pool_fee_key,
2738                    &accounts.pool_token_key,
2739                    accounts.fees,
2740                    accounts.swap_curve.clone(),
2741                )
2742                .unwrap(),
2743                vec![
2744                    &mut accounts.swap_account,
2745                    &mut Account::default(),
2746                    &mut accounts.token_a_account,
2747                    &mut accounts.token_b_account,
2748                    &mut accounts.pool_mint_account,
2749                    &mut accounts.pool_fee_account,
2750                    &mut accounts.pool_token_account,
2751                    &mut Account::default(),
2752                ],
2753                &constraints,
2754            )
2755            .unwrap();
2756        }
2757
2758        // create again
2759        {
2760            assert_eq!(
2761                Err(SwapError::AlreadyInUse.into()),
2762                accounts.initialize_swap()
2763            );
2764        }
2765        let swap_state = SwapVersion::unpack(&accounts.swap_account.data).unwrap();
2766        assert!(swap_state.is_initialized());
2767        assert_eq!(swap_state.bump_seed(), accounts.bump_seed);
2768        assert_eq!(
2769            swap_state.swap_curve().curve_type,
2770            accounts.swap_curve.curve_type
2771        );
2772        assert_eq!(*swap_state.token_a_account(), accounts.token_a_key);
2773        assert_eq!(*swap_state.token_b_account(), accounts.token_b_key);
2774        assert_eq!(*swap_state.pool_mint(), accounts.pool_mint_key);
2775        assert_eq!(*swap_state.token_a_mint(), accounts.token_a_mint_key);
2776        assert_eq!(*swap_state.token_b_mint(), accounts.token_b_mint_key);
2777        assert_eq!(*swap_state.pool_fee_account(), accounts.pool_fee_key);
2778        let token_a = gpl_token::state::Account::unpack(&accounts.token_a_account.data).unwrap();
2779        assert_eq!(token_a.amount, token_a_amount);
2780        let token_b = gpl_token::state::Account::unpack(&accounts.token_b_account.data).unwrap();
2781        assert_eq!(token_b.amount, token_b_amount);
2782        let pool_account =
2783            gpl_token::state::Account::unpack(&accounts.pool_token_account.data).unwrap();
2784        let pool_mint = gpl_token::state::Mint::unpack(&accounts.pool_mint_account.data).unwrap();
2785        assert_eq!(pool_mint.supply, pool_account.amount);
2786    }
2787
2788    #[test]
2789    fn test_deposit() {
2790        let user_key = Pubkey::new_unique();
2791        let depositor_key = Pubkey::new_unique();
2792        let trade_fee_numerator = 1;
2793        let trade_fee_denominator = 2;
2794        let owner_trade_fee_numerator = 1;
2795        let owner_trade_fee_denominator = 10;
2796        let owner_withdraw_fee_numerator = 1;
2797        let owner_withdraw_fee_denominator = 5;
2798        let host_fee_numerator = 20;
2799        let host_fee_denominator = 100;
2800
2801        let fees = Fees {
2802            trade_fee_numerator,
2803            trade_fee_denominator,
2804            owner_trade_fee_numerator,
2805            owner_trade_fee_denominator,
2806            owner_withdraw_fee_numerator,
2807            owner_withdraw_fee_denominator,
2808            host_fee_numerator,
2809            host_fee_denominator,
2810        };
2811
2812        let token_a_amount = 1000;
2813        let token_b_amount = 9000;
2814        let curve_type = CurveType::ConstantProduct;
2815        let swap_curve = SwapCurve {
2816            curve_type,
2817            calculator: Box::new(ConstantProductCurve {}),
2818        };
2819
2820        let mut accounts =
2821            SwapAccountInfo::new(&user_key, fees, swap_curve, token_a_amount, token_b_amount);
2822
2823        // depositing 10% of the current pool amount in token A and B means
2824        // that our pool tokens will be worth 1 / 10 of the current pool amount
2825        let pool_amount = INITIAL_SWAP_POOL_AMOUNT / 10;
2826        let deposit_a = token_a_amount / 10;
2827        let deposit_b = token_b_amount / 10;
2828
2829        // swap not initialized
2830        {
2831            let (
2832                token_a_key,
2833                mut token_a_account,
2834                token_b_key,
2835                mut token_b_account,
2836                pool_key,
2837                mut pool_account,
2838            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
2839            assert_eq!(
2840                Err(ProgramError::UninitializedAccount),
2841                accounts.deposit_all_token_types(
2842                    &depositor_key,
2843                    &token_a_key,
2844                    &mut token_a_account,
2845                    &token_b_key,
2846                    &mut token_b_account,
2847                    &pool_key,
2848                    &mut pool_account,
2849                    pool_amount.try_into().unwrap(),
2850                    deposit_a,
2851                    deposit_b,
2852                )
2853            );
2854        }
2855
2856        accounts.initialize_swap().unwrap();
2857
2858        // wrong owner for swap account
2859        {
2860            let (
2861                token_a_key,
2862                mut token_a_account,
2863                token_b_key,
2864                mut token_b_account,
2865                pool_key,
2866                mut pool_account,
2867            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
2868            let old_swap_account = accounts.swap_account;
2869            let mut wrong_swap_account = old_swap_account.clone();
2870            wrong_swap_account.owner = gpl_token::id();
2871            accounts.swap_account = wrong_swap_account;
2872            assert_eq!(
2873                Err(ProgramError::IncorrectProgramId),
2874                accounts.deposit_all_token_types(
2875                    &depositor_key,
2876                    &token_a_key,
2877                    &mut token_a_account,
2878                    &token_b_key,
2879                    &mut token_b_account,
2880                    &pool_key,
2881                    &mut pool_account,
2882                    pool_amount.try_into().unwrap(),
2883                    deposit_a,
2884                    deposit_b,
2885                )
2886            );
2887            accounts.swap_account = old_swap_account;
2888        }
2889
2890        // wrong bump seed for authority_key
2891        {
2892            let (
2893                token_a_key,
2894                mut token_a_account,
2895                token_b_key,
2896                mut token_b_account,
2897                pool_key,
2898                mut pool_account,
2899            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
2900            let old_authority = accounts.authority_key;
2901            let (bad_authority_key, _bump_seed) = Pubkey::find_program_address(
2902                &[&accounts.swap_key.to_bytes()[..]],
2903                &gpl_token::id(),
2904            );
2905            accounts.authority_key = bad_authority_key;
2906            assert_eq!(
2907                Err(SwapError::InvalidProgramAddress.into()),
2908                accounts.deposit_all_token_types(
2909                    &depositor_key,
2910                    &token_a_key,
2911                    &mut token_a_account,
2912                    &token_b_key,
2913                    &mut token_b_account,
2914                    &pool_key,
2915                    &mut pool_account,
2916                    pool_amount.try_into().unwrap(),
2917                    deposit_a,
2918                    deposit_b,
2919                )
2920            );
2921            accounts.authority_key = old_authority;
2922        }
2923
2924        // not enough token A
2925        {
2926            let (
2927                token_a_key,
2928                mut token_a_account,
2929                token_b_key,
2930                mut token_b_account,
2931                pool_key,
2932                mut pool_account,
2933            ) = accounts.setup_token_accounts(
2934                &user_key,
2935                &depositor_key,
2936                deposit_a / 2,
2937                deposit_b,
2938                0,
2939            );
2940            assert_eq!(
2941                Err(TokenError::InsufficientFunds.into()),
2942                accounts.deposit_all_token_types(
2943                    &depositor_key,
2944                    &token_a_key,
2945                    &mut token_a_account,
2946                    &token_b_key,
2947                    &mut token_b_account,
2948                    &pool_key,
2949                    &mut pool_account,
2950                    pool_amount.try_into().unwrap(),
2951                    deposit_a,
2952                    deposit_b,
2953                )
2954            );
2955        }
2956
2957        // not enough token B
2958        {
2959            let (
2960                token_a_key,
2961                mut token_a_account,
2962                token_b_key,
2963                mut token_b_account,
2964                pool_key,
2965                mut pool_account,
2966            ) = accounts.setup_token_accounts(
2967                &user_key,
2968                &depositor_key,
2969                deposit_a,
2970                deposit_b / 2,
2971                0,
2972            );
2973            assert_eq!(
2974                Err(TokenError::InsufficientFunds.into()),
2975                accounts.deposit_all_token_types(
2976                    &depositor_key,
2977                    &token_a_key,
2978                    &mut token_a_account,
2979                    &token_b_key,
2980                    &mut token_b_account,
2981                    &pool_key,
2982                    &mut pool_account,
2983                    pool_amount.try_into().unwrap(),
2984                    deposit_a,
2985                    deposit_b,
2986                )
2987            );
2988        }
2989
2990        // wrong swap token accounts
2991        {
2992            let (
2993                token_a_key,
2994                mut token_a_account,
2995                token_b_key,
2996                mut token_b_account,
2997                pool_key,
2998                mut pool_account,
2999            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
3000            assert_eq!(
3001                Err(TokenError::MintMismatch.into()),
3002                accounts.deposit_all_token_types(
3003                    &depositor_key,
3004                    &token_b_key,
3005                    &mut token_b_account,
3006                    &token_a_key,
3007                    &mut token_a_account,
3008                    &pool_key,
3009                    &mut pool_account,
3010                    pool_amount.try_into().unwrap(),
3011                    deposit_a,
3012                    deposit_b,
3013                )
3014            );
3015        }
3016
3017        // wrong pool token account
3018        {
3019            let (
3020                token_a_key,
3021                mut token_a_account,
3022                token_b_key,
3023                mut token_b_account,
3024                _pool_key,
3025                mut _pool_account,
3026            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
3027            let (
3028                wrong_token_key,
3029                mut wrong_token_account,
3030                _token_b_key,
3031                mut _token_b_account,
3032                _pool_key,
3033                mut _pool_account,
3034            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
3035            assert_eq!(
3036                Err(TokenError::MintMismatch.into()),
3037                accounts.deposit_all_token_types(
3038                    &depositor_key,
3039                    &token_a_key,
3040                    &mut token_a_account,
3041                    &token_b_key,
3042                    &mut token_b_account,
3043                    &wrong_token_key,
3044                    &mut wrong_token_account,
3045                    pool_amount.try_into().unwrap(),
3046                    deposit_a,
3047                    deposit_b,
3048                )
3049            );
3050        }
3051
3052        // no approval
3053        {
3054            let (
3055                token_a_key,
3056                mut token_a_account,
3057                token_b_key,
3058                mut token_b_account,
3059                pool_key,
3060                mut pool_account,
3061            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
3062            let user_transfer_authority_key = Pubkey::new_unique();
3063            assert_eq!(
3064                Err(TokenError::OwnerMismatch.into()),
3065                do_process_instruction(
3066                    deposit_all_token_types(
3067                        &SWAP_PROGRAM_ID,
3068                        &gpl_token::id(),
3069                        &accounts.swap_key,
3070                        &accounts.authority_key,
3071                        &user_transfer_authority_key,
3072                        &token_a_key,
3073                        &token_b_key,
3074                        &accounts.token_a_key,
3075                        &accounts.token_b_key,
3076                        &accounts.pool_mint_key,
3077                        &pool_key,
3078                        DepositAllTokenTypes {
3079                            pool_token_amount: pool_amount.try_into().unwrap(),
3080                            maximum_token_a_amount: deposit_a,
3081                            maximum_token_b_amount: deposit_b,
3082                        },
3083                    )
3084                    .unwrap(),
3085                    vec![
3086                        &mut accounts.swap_account,
3087                        &mut Account::default(),
3088                        &mut Account::default(),
3089                        &mut token_a_account,
3090                        &mut token_b_account,
3091                        &mut accounts.token_a_account,
3092                        &mut accounts.token_b_account,
3093                        &mut accounts.pool_mint_account,
3094                        &mut pool_account,
3095                        &mut Account::default(),
3096                    ],
3097                )
3098            );
3099        }
3100
3101        // wrong token program id
3102        {
3103            let (
3104                token_a_key,
3105                mut token_a_account,
3106                token_b_key,
3107                mut token_b_account,
3108                pool_key,
3109                mut pool_account,
3110            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
3111            let wrong_key = Pubkey::new_unique();
3112            assert_eq!(
3113                Err(SwapError::IncorrectTokenProgramId.into()),
3114                do_process_instruction(
3115                    deposit_all_token_types(
3116                        &SWAP_PROGRAM_ID,
3117                        &wrong_key,
3118                        &accounts.swap_key,
3119                        &accounts.authority_key,
3120                        &accounts.authority_key,
3121                        &token_a_key,
3122                        &token_b_key,
3123                        &accounts.token_a_key,
3124                        &accounts.token_b_key,
3125                        &accounts.pool_mint_key,
3126                        &pool_key,
3127                        DepositAllTokenTypes {
3128                            pool_token_amount: pool_amount.try_into().unwrap(),
3129                            maximum_token_a_amount: deposit_a,
3130                            maximum_token_b_amount: deposit_b,
3131                        },
3132                    )
3133                    .unwrap(),
3134                    vec![
3135                        &mut accounts.swap_account,
3136                        &mut Account::default(),
3137                        &mut Account::default(),
3138                        &mut token_a_account,
3139                        &mut token_b_account,
3140                        &mut accounts.token_a_account,
3141                        &mut accounts.token_b_account,
3142                        &mut accounts.pool_mint_account,
3143                        &mut pool_account,
3144                        &mut Account::default(),
3145                    ],
3146                )
3147            );
3148        }
3149
3150        // wrong swap token accounts
3151        {
3152            let (
3153                token_a_key,
3154                mut token_a_account,
3155                token_b_key,
3156                mut token_b_account,
3157                pool_key,
3158                mut pool_account,
3159            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
3160
3161            let old_a_key = accounts.token_a_key;
3162            let old_a_account = accounts.token_a_account;
3163
3164            accounts.token_a_key = token_a_key;
3165            accounts.token_a_account = token_a_account.clone();
3166
3167            // wrong swap token a account
3168            assert_eq!(
3169                Err(SwapError::IncorrectSwapAccount.into()),
3170                accounts.deposit_all_token_types(
3171                    &depositor_key,
3172                    &token_a_key,
3173                    &mut token_a_account,
3174                    &token_b_key,
3175                    &mut token_b_account,
3176                    &pool_key,
3177                    &mut pool_account,
3178                    pool_amount.try_into().unwrap(),
3179                    deposit_a,
3180                    deposit_b,
3181                )
3182            );
3183
3184            accounts.token_a_key = old_a_key;
3185            accounts.token_a_account = old_a_account;
3186
3187            let old_b_key = accounts.token_b_key;
3188            let old_b_account = accounts.token_b_account;
3189
3190            accounts.token_b_key = token_b_key;
3191            accounts.token_b_account = token_b_account.clone();
3192
3193            // wrong swap token b account
3194            assert_eq!(
3195                Err(SwapError::IncorrectSwapAccount.into()),
3196                accounts.deposit_all_token_types(
3197                    &depositor_key,
3198                    &token_a_key,
3199                    &mut token_a_account,
3200                    &token_b_key,
3201                    &mut token_b_account,
3202                    &pool_key,
3203                    &mut pool_account,
3204                    pool_amount.try_into().unwrap(),
3205                    deposit_a,
3206                    deposit_b,
3207                )
3208            );
3209
3210            accounts.token_b_key = old_b_key;
3211            accounts.token_b_account = old_b_account;
3212        }
3213
3214        // wrong mint
3215        {
3216            let (
3217                token_a_key,
3218                mut token_a_account,
3219                token_b_key,
3220                mut token_b_account,
3221                pool_key,
3222                mut pool_account,
3223            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
3224            let (pool_mint_key, pool_mint_account) =
3225                create_mint(&gpl_token::id(), &accounts.authority_key, None);
3226            let old_pool_key = accounts.pool_mint_key;
3227            let old_pool_account = accounts.pool_mint_account;
3228            accounts.pool_mint_key = pool_mint_key;
3229            accounts.pool_mint_account = pool_mint_account;
3230
3231            assert_eq!(
3232                Err(SwapError::IncorrectPoolMint.into()),
3233                accounts.deposit_all_token_types(
3234                    &depositor_key,
3235                    &token_a_key,
3236                    &mut token_a_account,
3237                    &token_b_key,
3238                    &mut token_b_account,
3239                    &pool_key,
3240                    &mut pool_account,
3241                    pool_amount.try_into().unwrap(),
3242                    deposit_a,
3243                    deposit_b,
3244                )
3245            );
3246
3247            accounts.pool_mint_key = old_pool_key;
3248            accounts.pool_mint_account = old_pool_account;
3249        }
3250
3251        // deposit 1 pool token fails beacuse it equates to 0 swap tokens
3252        {
3253            let (
3254                token_a_key,
3255                mut token_a_account,
3256                token_b_key,
3257                mut token_b_account,
3258                pool_key,
3259                mut pool_account,
3260            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
3261            assert_eq!(
3262                Err(SwapError::ZeroTradingTokens.into()),
3263                accounts.deposit_all_token_types(
3264                    &depositor_key,
3265                    &token_a_key,
3266                    &mut token_a_account,
3267                    &token_b_key,
3268                    &mut token_b_account,
3269                    &pool_key,
3270                    &mut pool_account,
3271                    1,
3272                    deposit_a,
3273                    deposit_b,
3274                )
3275            );
3276        }
3277
3278        // slippage exceeded
3279        {
3280            let (
3281                token_a_key,
3282                mut token_a_account,
3283                token_b_key,
3284                mut token_b_account,
3285                pool_key,
3286                mut pool_account,
3287            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
3288            // maximum A amount in too low
3289            assert_eq!(
3290                Err(SwapError::ExceededSlippage.into()),
3291                accounts.deposit_all_token_types(
3292                    &depositor_key,
3293                    &token_a_key,
3294                    &mut token_a_account,
3295                    &token_b_key,
3296                    &mut token_b_account,
3297                    &pool_key,
3298                    &mut pool_account,
3299                    pool_amount.try_into().unwrap(),
3300                    deposit_a / 10,
3301                    deposit_b,
3302                )
3303            );
3304            // maximum B amount in too low
3305            assert_eq!(
3306                Err(SwapError::ExceededSlippage.into()),
3307                accounts.deposit_all_token_types(
3308                    &depositor_key,
3309                    &token_a_key,
3310                    &mut token_a_account,
3311                    &token_b_key,
3312                    &mut token_b_account,
3313                    &pool_key,
3314                    &mut pool_account,
3315                    pool_amount.try_into().unwrap(),
3316                    deposit_a,
3317                    deposit_b / 10,
3318                )
3319            );
3320        }
3321
3322        // invalid input: can't use swap pool tokens as source
3323        {
3324            let (
3325                _token_a_key,
3326                _token_a_account,
3327                _token_b_key,
3328                _token_b_account,
3329                pool_key,
3330                mut pool_account,
3331            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
3332            let swap_token_a_key = accounts.token_a_key;
3333            let mut swap_token_a_account = accounts.get_token_account(&swap_token_a_key).clone();
3334            let swap_token_b_key = accounts.token_b_key;
3335            let mut swap_token_b_account = accounts.get_token_account(&swap_token_b_key).clone();
3336            let authority_key = accounts.authority_key;
3337            assert_eq!(
3338                Err(SwapError::InvalidInput.into()),
3339                accounts.deposit_all_token_types(
3340                    &authority_key,
3341                    &swap_token_a_key,
3342                    &mut swap_token_a_account,
3343                    &swap_token_b_key,
3344                    &mut swap_token_b_account,
3345                    &pool_key,
3346                    &mut pool_account,
3347                    pool_amount.try_into().unwrap(),
3348                    deposit_a,
3349                    deposit_b,
3350                )
3351            );
3352        }
3353
3354        // correctly deposit
3355        {
3356            let (
3357                token_a_key,
3358                mut token_a_account,
3359                token_b_key,
3360                mut token_b_account,
3361                pool_key,
3362                mut pool_account,
3363            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
3364            accounts
3365                .deposit_all_token_types(
3366                    &depositor_key,
3367                    &token_a_key,
3368                    &mut token_a_account,
3369                    &token_b_key,
3370                    &mut token_b_account,
3371                    &pool_key,
3372                    &mut pool_account,
3373                    pool_amount.try_into().unwrap(),
3374                    deposit_a,
3375                    deposit_b,
3376                )
3377                .unwrap();
3378
3379            let swap_token_a =
3380                gpl_token::state::Account::unpack(&accounts.token_a_account.data).unwrap();
3381            assert_eq!(swap_token_a.amount, deposit_a + token_a_amount);
3382            let swap_token_b =
3383                gpl_token::state::Account::unpack(&accounts.token_b_account.data).unwrap();
3384            assert_eq!(swap_token_b.amount, deposit_b + token_b_amount);
3385            let token_a = gpl_token::state::Account::unpack(&token_a_account.data).unwrap();
3386            assert_eq!(token_a.amount, 0);
3387            let token_b = gpl_token::state::Account::unpack(&token_b_account.data).unwrap();
3388            assert_eq!(token_b.amount, 0);
3389            let pool_account = gpl_token::state::Account::unpack(&pool_account.data).unwrap();
3390            let swap_pool_account =
3391                gpl_token::state::Account::unpack(&accounts.pool_token_account.data).unwrap();
3392            let pool_mint =
3393                gpl_token::state::Mint::unpack(&accounts.pool_mint_account.data).unwrap();
3394            assert_eq!(
3395                pool_mint.supply,
3396                pool_account.amount + swap_pool_account.amount
3397            );
3398        }
3399    }
3400
3401    #[test]
3402    fn test_withdraw() {
3403        let user_key = Pubkey::new_unique();
3404        let trade_fee_numerator = 1;
3405        let trade_fee_denominator = 2;
3406        let owner_trade_fee_numerator = 1;
3407        let owner_trade_fee_denominator = 10;
3408        let owner_withdraw_fee_numerator = 1;
3409        let owner_withdraw_fee_denominator = 5;
3410        let host_fee_numerator = 7;
3411        let host_fee_denominator = 100;
3412
3413        let fees = Fees {
3414            trade_fee_numerator,
3415            trade_fee_denominator,
3416            owner_trade_fee_numerator,
3417            owner_trade_fee_denominator,
3418            owner_withdraw_fee_numerator,
3419            owner_withdraw_fee_denominator,
3420            host_fee_numerator,
3421            host_fee_denominator,
3422        };
3423
3424        let token_a_amount = 1000;
3425        let token_b_amount = 2000;
3426        let curve_type = CurveType::ConstantProduct;
3427        let swap_curve = SwapCurve {
3428            curve_type,
3429            calculator: Box::new(ConstantProductCurve {}),
3430        };
3431
3432        let withdrawer_key = Pubkey::new_unique();
3433        let initial_a = token_a_amount / 10;
3434        let initial_b = token_b_amount / 10;
3435        let initial_pool = swap_curve.calculator.new_pool_supply() / 10;
3436        let withdraw_amount = initial_pool / 4;
3437        let minimum_token_a_amount = initial_a / 40;
3438        let minimum_token_b_amount = initial_b / 40;
3439
3440        let mut accounts =
3441            SwapAccountInfo::new(&user_key, fees, swap_curve, token_a_amount, token_b_amount);
3442
3443        // swap not initialized
3444        {
3445            let (
3446                token_a_key,
3447                mut token_a_account,
3448                token_b_key,
3449                mut token_b_account,
3450                pool_key,
3451                mut pool_account,
3452            ) = accounts.setup_token_accounts(&user_key, &withdrawer_key, initial_a, initial_b, 0);
3453            assert_eq!(
3454                Err(ProgramError::UninitializedAccount),
3455                accounts.withdraw_all_token_types(
3456                    &withdrawer_key,
3457                    &pool_key,
3458                    &mut pool_account,
3459                    &token_a_key,
3460                    &mut token_a_account,
3461                    &token_b_key,
3462                    &mut token_b_account,
3463                    withdraw_amount.try_into().unwrap(),
3464                    minimum_token_a_amount,
3465                    minimum_token_b_amount,
3466                )
3467            );
3468        }
3469
3470        accounts.initialize_swap().unwrap();
3471
3472        // wrong owner for swap account
3473        {
3474            let (
3475                token_a_key,
3476                mut token_a_account,
3477                token_b_key,
3478                mut token_b_account,
3479                pool_key,
3480                mut pool_account,
3481            ) = accounts.setup_token_accounts(&user_key, &withdrawer_key, initial_a, initial_b, 0);
3482            let old_swap_account = accounts.swap_account;
3483            let mut wrong_swap_account = old_swap_account.clone();
3484            wrong_swap_account.owner = gpl_token::id();
3485            accounts.swap_account = wrong_swap_account;
3486            assert_eq!(
3487                Err(ProgramError::IncorrectProgramId),
3488                accounts.withdraw_all_token_types(
3489                    &withdrawer_key,
3490                    &pool_key,
3491                    &mut pool_account,
3492                    &token_a_key,
3493                    &mut token_a_account,
3494                    &token_b_key,
3495                    &mut token_b_account,
3496                    withdraw_amount.try_into().unwrap(),
3497                    minimum_token_a_amount,
3498                    minimum_token_b_amount,
3499                )
3500            );
3501            accounts.swap_account = old_swap_account;
3502        }
3503
3504        // wrong bump seed for authority_key
3505        {
3506            let (
3507                token_a_key,
3508                mut token_a_account,
3509                token_b_key,
3510                mut token_b_account,
3511                pool_key,
3512                mut pool_account,
3513            ) = accounts.setup_token_accounts(&user_key, &withdrawer_key, initial_a, initial_b, 0);
3514            let old_authority = accounts.authority_key;
3515            let (bad_authority_key, _bump_seed) = Pubkey::find_program_address(
3516                &[&accounts.swap_key.to_bytes()[..]],
3517                &gpl_token::id(),
3518            );
3519            accounts.authority_key = bad_authority_key;
3520            assert_eq!(
3521                Err(SwapError::InvalidProgramAddress.into()),
3522                accounts.withdraw_all_token_types(
3523                    &withdrawer_key,
3524                    &pool_key,
3525                    &mut pool_account,
3526                    &token_a_key,
3527                    &mut token_a_account,
3528                    &token_b_key,
3529                    &mut token_b_account,
3530                    withdraw_amount.try_into().unwrap(),
3531                    minimum_token_a_amount,
3532                    minimum_token_b_amount,
3533                )
3534            );
3535            accounts.authority_key = old_authority;
3536        }
3537
3538        // not enough pool tokens
3539        {
3540            let (
3541                token_a_key,
3542                mut token_a_account,
3543                token_b_key,
3544                mut token_b_account,
3545                pool_key,
3546                mut pool_account,
3547            ) = accounts.setup_token_accounts(
3548                &user_key,
3549                &withdrawer_key,
3550                initial_a,
3551                initial_b,
3552                to_u64(withdraw_amount).unwrap() / 2u64,
3553            );
3554            assert_eq!(
3555                Err(TokenError::InsufficientFunds.into()),
3556                accounts.withdraw_all_token_types(
3557                    &withdrawer_key,
3558                    &pool_key,
3559                    &mut pool_account,
3560                    &token_a_key,
3561                    &mut token_a_account,
3562                    &token_b_key,
3563                    &mut token_b_account,
3564                    withdraw_amount.try_into().unwrap(),
3565                    minimum_token_a_amount / 2,
3566                    minimum_token_b_amount / 2,
3567                )
3568            );
3569        }
3570
3571        // wrong token a / b accounts
3572        {
3573            let (
3574                token_a_key,
3575                mut token_a_account,
3576                token_b_key,
3577                mut token_b_account,
3578                pool_key,
3579                mut pool_account,
3580            ) = accounts.setup_token_accounts(
3581                &user_key,
3582                &withdrawer_key,
3583                initial_a,
3584                initial_b,
3585                withdraw_amount.try_into().unwrap(),
3586            );
3587            assert_eq!(
3588                Err(TokenError::MintMismatch.into()),
3589                accounts.withdraw_all_token_types(
3590                    &withdrawer_key,
3591                    &pool_key,
3592                    &mut pool_account,
3593                    &token_b_key,
3594                    &mut token_b_account,
3595                    &token_a_key,
3596                    &mut token_a_account,
3597                    withdraw_amount.try_into().unwrap(),
3598                    minimum_token_a_amount,
3599                    minimum_token_b_amount,
3600                )
3601            );
3602        }
3603
3604        // wrong pool token account
3605        {
3606            let (
3607                token_a_key,
3608                mut token_a_account,
3609                token_b_key,
3610                mut token_b_account,
3611                _pool_key,
3612                _pool_account,
3613            ) = accounts.setup_token_accounts(
3614                &user_key,
3615                &withdrawer_key,
3616                initial_a,
3617                initial_b,
3618                withdraw_amount.try_into().unwrap(),
3619            );
3620            let (
3621                wrong_token_a_key,
3622                mut wrong_token_a_account,
3623                _token_b_key,
3624                _token_b_account,
3625                _pool_key,
3626                _pool_account,
3627            ) = accounts.setup_token_accounts(
3628                &user_key,
3629                &withdrawer_key,
3630                withdraw_amount.try_into().unwrap(),
3631                initial_b,
3632                withdraw_amount.try_into().unwrap(),
3633            );
3634            assert_eq!(
3635                Err(TokenError::MintMismatch.into()),
3636                accounts.withdraw_all_token_types(
3637                    &withdrawer_key,
3638                    &wrong_token_a_key,
3639                    &mut wrong_token_a_account,
3640                    &token_a_key,
3641                    &mut token_a_account,
3642                    &token_b_key,
3643                    &mut token_b_account,
3644                    withdraw_amount.try_into().unwrap(),
3645                    minimum_token_a_amount,
3646                    minimum_token_b_amount,
3647                )
3648            );
3649        }
3650
3651        // wrong pool fee account
3652        {
3653            let (
3654                token_a_key,
3655                mut token_a_account,
3656                token_b_key,
3657                mut token_b_account,
3658                wrong_pool_key,
3659                wrong_pool_account,
3660            ) = accounts.setup_token_accounts(
3661                &user_key,
3662                &withdrawer_key,
3663                initial_a,
3664                initial_b,
3665                withdraw_amount.try_into().unwrap(),
3666            );
3667            let (
3668                _token_a_key,
3669                _token_a_account,
3670                _token_b_key,
3671                _token_b_account,
3672                pool_key,
3673                mut pool_account,
3674            ) = accounts.setup_token_accounts(
3675                &user_key,
3676                &withdrawer_key,
3677                initial_a,
3678                initial_b,
3679                withdraw_amount.try_into().unwrap(),
3680            );
3681            let old_pool_fee_account = accounts.pool_fee_account;
3682            let old_pool_fee_key = accounts.pool_fee_key;
3683            accounts.pool_fee_account = wrong_pool_account;
3684            accounts.pool_fee_key = wrong_pool_key;
3685            assert_eq!(
3686                Err(SwapError::IncorrectFeeAccount.into()),
3687                accounts.withdraw_all_token_types(
3688                    &withdrawer_key,
3689                    &pool_key,
3690                    &mut pool_account,
3691                    &token_a_key,
3692                    &mut token_a_account,
3693                    &token_b_key,
3694                    &mut token_b_account,
3695                    withdraw_amount.try_into().unwrap(),
3696                    minimum_token_a_amount,
3697                    minimum_token_b_amount,
3698                ),
3699            );
3700            accounts.pool_fee_account = old_pool_fee_account;
3701            accounts.pool_fee_key = old_pool_fee_key;
3702        }
3703
3704        // no approval
3705        {
3706            let (
3707                token_a_key,
3708                mut token_a_account,
3709                token_b_key,
3710                mut token_b_account,
3711                pool_key,
3712                mut pool_account,
3713            ) = accounts.setup_token_accounts(
3714                &user_key,
3715                &withdrawer_key,
3716                0,
3717                0,
3718                withdraw_amount.try_into().unwrap(),
3719            );
3720            let user_transfer_authority_key = Pubkey::new_unique();
3721            assert_eq!(
3722                Err(TokenError::OwnerMismatch.into()),
3723                do_process_instruction(
3724                    withdraw_all_token_types(
3725                        &SWAP_PROGRAM_ID,
3726                        &gpl_token::id(),
3727                        &accounts.swap_key,
3728                        &accounts.authority_key,
3729                        &user_transfer_authority_key,
3730                        &accounts.pool_mint_key,
3731                        &accounts.pool_fee_key,
3732                        &pool_key,
3733                        &accounts.token_a_key,
3734                        &accounts.token_b_key,
3735                        &token_a_key,
3736                        &token_b_key,
3737                        WithdrawAllTokenTypes {
3738                            pool_token_amount: withdraw_amount.try_into().unwrap(),
3739                            minimum_token_a_amount,
3740                            minimum_token_b_amount,
3741                        }
3742                    )
3743                    .unwrap(),
3744                    vec![
3745                        &mut accounts.swap_account,
3746                        &mut Account::default(),
3747                        &mut Account::default(),
3748                        &mut accounts.pool_mint_account,
3749                        &mut pool_account,
3750                        &mut accounts.token_a_account,
3751                        &mut accounts.token_b_account,
3752                        &mut token_a_account,
3753                        &mut token_b_account,
3754                        &mut accounts.pool_fee_account,
3755                        &mut Account::default(),
3756                    ],
3757                )
3758            );
3759        }
3760
3761        // wrong token program id
3762        {
3763            let (
3764                token_a_key,
3765                mut token_a_account,
3766                token_b_key,
3767                mut token_b_account,
3768                pool_key,
3769                mut pool_account,
3770            ) = accounts.setup_token_accounts(
3771                &user_key,
3772                &withdrawer_key,
3773                initial_a,
3774                initial_b,
3775                withdraw_amount.try_into().unwrap(),
3776            );
3777            let wrong_key = Pubkey::new_unique();
3778            assert_eq!(
3779                Err(SwapError::IncorrectTokenProgramId.into()),
3780                do_process_instruction(
3781                    withdraw_all_token_types(
3782                        &SWAP_PROGRAM_ID,
3783                        &wrong_key,
3784                        &accounts.swap_key,
3785                        &accounts.authority_key,
3786                        &accounts.authority_key,
3787                        &accounts.pool_mint_key,
3788                        &accounts.pool_fee_key,
3789                        &pool_key,
3790                        &accounts.token_a_key,
3791                        &accounts.token_b_key,
3792                        &token_a_key,
3793                        &token_b_key,
3794                        WithdrawAllTokenTypes {
3795                            pool_token_amount: withdraw_amount.try_into().unwrap(),
3796                            minimum_token_a_amount,
3797                            minimum_token_b_amount,
3798                        },
3799                    )
3800                    .unwrap(),
3801                    vec![
3802                        &mut accounts.swap_account,
3803                        &mut Account::default(),
3804                        &mut Account::default(),
3805                        &mut accounts.pool_mint_account,
3806                        &mut pool_account,
3807                        &mut accounts.token_a_account,
3808                        &mut accounts.token_b_account,
3809                        &mut token_a_account,
3810                        &mut token_b_account,
3811                        &mut accounts.pool_fee_account,
3812                        &mut Account::default(),
3813                    ],
3814                )
3815            );
3816        }
3817
3818        // wrong swap token accounts
3819        {
3820            let (
3821                token_a_key,
3822                mut token_a_account,
3823                token_b_key,
3824                mut token_b_account,
3825                pool_key,
3826                mut pool_account,
3827            ) = accounts.setup_token_accounts(
3828                &user_key,
3829                &withdrawer_key,
3830                initial_a,
3831                initial_b,
3832                initial_pool.try_into().unwrap(),
3833            );
3834
3835            let old_a_key = accounts.token_a_key;
3836            let old_a_account = accounts.token_a_account;
3837
3838            accounts.token_a_key = token_a_key;
3839            accounts.token_a_account = token_a_account.clone();
3840
3841            // wrong swap token a account
3842            assert_eq!(
3843                Err(SwapError::IncorrectSwapAccount.into()),
3844                accounts.withdraw_all_token_types(
3845                    &withdrawer_key,
3846                    &pool_key,
3847                    &mut pool_account,
3848                    &token_a_key,
3849                    &mut token_a_account,
3850                    &token_b_key,
3851                    &mut token_b_account,
3852                    withdraw_amount.try_into().unwrap(),
3853                    minimum_token_a_amount,
3854                    minimum_token_b_amount,
3855                )
3856            );
3857
3858            accounts.token_a_key = old_a_key;
3859            accounts.token_a_account = old_a_account;
3860
3861            let old_b_key = accounts.token_b_key;
3862            let old_b_account = accounts.token_b_account;
3863
3864            accounts.token_b_key = token_b_key;
3865            accounts.token_b_account = token_b_account.clone();
3866
3867            // wrong swap token b account
3868            assert_eq!(
3869                Err(SwapError::IncorrectSwapAccount.into()),
3870                accounts.withdraw_all_token_types(
3871                    &withdrawer_key,
3872                    &pool_key,
3873                    &mut pool_account,
3874                    &token_a_key,
3875                    &mut token_a_account,
3876                    &token_b_key,
3877                    &mut token_b_account,
3878                    withdraw_amount.try_into().unwrap(),
3879                    minimum_token_a_amount,
3880                    minimum_token_b_amount,
3881                )
3882            );
3883
3884            accounts.token_b_key = old_b_key;
3885            accounts.token_b_account = old_b_account;
3886        }
3887
3888        // wrong mint
3889        {
3890            let (
3891                token_a_key,
3892                mut token_a_account,
3893                token_b_key,
3894                mut token_b_account,
3895                pool_key,
3896                mut pool_account,
3897            ) = accounts.setup_token_accounts(
3898                &user_key,
3899                &withdrawer_key,
3900                initial_a,
3901                initial_b,
3902                initial_pool.try_into().unwrap(),
3903            );
3904            let (pool_mint_key, pool_mint_account) =
3905                create_mint(&gpl_token::id(), &accounts.authority_key, None);
3906            let old_pool_key = accounts.pool_mint_key;
3907            let old_pool_account = accounts.pool_mint_account;
3908            accounts.pool_mint_key = pool_mint_key;
3909            accounts.pool_mint_account = pool_mint_account;
3910
3911            assert_eq!(
3912                Err(SwapError::IncorrectPoolMint.into()),
3913                accounts.withdraw_all_token_types(
3914                    &withdrawer_key,
3915                    &pool_key,
3916                    &mut pool_account,
3917                    &token_a_key,
3918                    &mut token_a_account,
3919                    &token_b_key,
3920                    &mut token_b_account,
3921                    withdraw_amount.try_into().unwrap(),
3922                    minimum_token_a_amount,
3923                    minimum_token_b_amount,
3924                )
3925            );
3926
3927            accounts.pool_mint_key = old_pool_key;
3928            accounts.pool_mint_account = old_pool_account;
3929        }
3930
3931        // withdrawing 1 pool token fails because it equates to 0 output tokens
3932        {
3933            let (
3934                token_a_key,
3935                mut token_a_account,
3936                token_b_key,
3937                mut token_b_account,
3938                pool_key,
3939                mut pool_account,
3940            ) = accounts.setup_token_accounts(
3941                &user_key,
3942                &withdrawer_key,
3943                initial_a,
3944                initial_b,
3945                initial_pool.try_into().unwrap(),
3946            );
3947            assert_eq!(
3948                Err(SwapError::ZeroTradingTokens.into()),
3949                accounts.withdraw_all_token_types(
3950                    &withdrawer_key,
3951                    &pool_key,
3952                    &mut pool_account,
3953                    &token_a_key,
3954                    &mut token_a_account,
3955                    &token_b_key,
3956                    &mut token_b_account,
3957                    1,
3958                    0,
3959                    0,
3960                )
3961            );
3962        }
3963
3964        // slippage exceeded
3965        {
3966            let (
3967                token_a_key,
3968                mut token_a_account,
3969                token_b_key,
3970                mut token_b_account,
3971                pool_key,
3972                mut pool_account,
3973            ) = accounts.setup_token_accounts(
3974                &user_key,
3975                &withdrawer_key,
3976                initial_a,
3977                initial_b,
3978                initial_pool.try_into().unwrap(),
3979            );
3980            // minimum A amount out too high
3981            assert_eq!(
3982                Err(SwapError::ExceededSlippage.into()),
3983                accounts.withdraw_all_token_types(
3984                    &withdrawer_key,
3985                    &pool_key,
3986                    &mut pool_account,
3987                    &token_a_key,
3988                    &mut token_a_account,
3989                    &token_b_key,
3990                    &mut token_b_account,
3991                    withdraw_amount.try_into().unwrap(),
3992                    minimum_token_a_amount * 10,
3993                    minimum_token_b_amount,
3994                )
3995            );
3996            // minimum B amount out too high
3997            assert_eq!(
3998                Err(SwapError::ExceededSlippage.into()),
3999                accounts.withdraw_all_token_types(
4000                    &withdrawer_key,
4001                    &pool_key,
4002                    &mut pool_account,
4003                    &token_a_key,
4004                    &mut token_a_account,
4005                    &token_b_key,
4006                    &mut token_b_account,
4007                    withdraw_amount.try_into().unwrap(),
4008                    minimum_token_a_amount,
4009                    minimum_token_b_amount * 10,
4010                )
4011            );
4012        }
4013
4014        // invalid input: can't use swap pool tokens as destination
4015        {
4016            let (
4017                token_a_key,
4018                mut token_a_account,
4019                token_b_key,
4020                mut token_b_account,
4021                pool_key,
4022                mut pool_account,
4023            ) = accounts.setup_token_accounts(
4024                &user_key,
4025                &withdrawer_key,
4026                initial_a,
4027                initial_b,
4028                initial_pool.try_into().unwrap(),
4029            );
4030            let swap_token_a_key = accounts.token_a_key;
4031            let mut swap_token_a_account = accounts.get_token_account(&swap_token_a_key).clone();
4032            assert_eq!(
4033                Err(SwapError::InvalidInput.into()),
4034                accounts.withdraw_all_token_types(
4035                    &withdrawer_key,
4036                    &pool_key,
4037                    &mut pool_account,
4038                    &swap_token_a_key,
4039                    &mut swap_token_a_account,
4040                    &token_b_key,
4041                    &mut token_b_account,
4042                    withdraw_amount.try_into().unwrap(),
4043                    minimum_token_a_amount,
4044                    minimum_token_b_amount,
4045                )
4046            );
4047            let swap_token_b_key = accounts.token_b_key;
4048            let mut swap_token_b_account = accounts.get_token_account(&swap_token_b_key).clone();
4049            assert_eq!(
4050                Err(SwapError::InvalidInput.into()),
4051                accounts.withdraw_all_token_types(
4052                    &withdrawer_key,
4053                    &pool_key,
4054                    &mut pool_account,
4055                    &token_a_key,
4056                    &mut token_a_account,
4057                    &swap_token_b_key,
4058                    &mut swap_token_b_account,
4059                    withdraw_amount.try_into().unwrap(),
4060                    minimum_token_a_amount,
4061                    minimum_token_b_amount,
4062                )
4063            );
4064        }
4065
4066        // correct withdrawal
4067        {
4068            let (
4069                token_a_key,
4070                mut token_a_account,
4071                token_b_key,
4072                mut token_b_account,
4073                pool_key,
4074                mut pool_account,
4075            ) = accounts.setup_token_accounts(
4076                &user_key,
4077                &withdrawer_key,
4078                initial_a,
4079                initial_b,
4080                initial_pool.try_into().unwrap(),
4081            );
4082
4083            accounts
4084                .withdraw_all_token_types(
4085                    &withdrawer_key,
4086                    &pool_key,
4087                    &mut pool_account,
4088                    &token_a_key,
4089                    &mut token_a_account,
4090                    &token_b_key,
4091                    &mut token_b_account,
4092                    withdraw_amount.try_into().unwrap(),
4093                    minimum_token_a_amount,
4094                    minimum_token_b_amount,
4095                )
4096                .unwrap();
4097
4098            let swap_token_a =
4099                gpl_token::state::Account::unpack(&accounts.token_a_account.data).unwrap();
4100            let swap_token_b =
4101                gpl_token::state::Account::unpack(&accounts.token_b_account.data).unwrap();
4102            let pool_mint =
4103                gpl_token::state::Mint::unpack(&accounts.pool_mint_account.data).unwrap();
4104            let withdraw_fee = accounts.fees.owner_withdraw_fee(withdraw_amount).unwrap();
4105            let results = accounts
4106                .swap_curve
4107                .calculator
4108                .pool_tokens_to_trading_tokens(
4109                    withdraw_amount - withdraw_fee,
4110                    pool_mint.supply.try_into().unwrap(),
4111                    swap_token_a.amount.try_into().unwrap(),
4112                    swap_token_b.amount.try_into().unwrap(),
4113                    RoundDirection::Floor,
4114                )
4115                .unwrap();
4116            assert_eq!(
4117                swap_token_a.amount,
4118                token_a_amount - to_u64(results.token_a_amount).unwrap()
4119            );
4120            assert_eq!(
4121                swap_token_b.amount,
4122                token_b_amount - to_u64(results.token_b_amount).unwrap()
4123            );
4124            let token_a = gpl_token::state::Account::unpack(&token_a_account.data).unwrap();
4125            assert_eq!(
4126                token_a.amount,
4127                initial_a + to_u64(results.token_a_amount).unwrap()
4128            );
4129            let token_b = gpl_token::state::Account::unpack(&token_b_account.data).unwrap();
4130            assert_eq!(
4131                token_b.amount,
4132                initial_b + to_u64(results.token_b_amount).unwrap()
4133            );
4134            let pool_account = gpl_token::state::Account::unpack(&pool_account.data).unwrap();
4135            assert_eq!(
4136                pool_account.amount,
4137                to_u64(initial_pool - withdraw_amount).unwrap()
4138            );
4139            let fee_account =
4140                gpl_token::state::Account::unpack(&accounts.pool_fee_account.data).unwrap();
4141            assert_eq!(
4142                fee_account.amount,
4143                TryInto::<u64>::try_into(withdraw_fee).unwrap()
4144            );
4145        }
4146
4147        // correct withdrawal from fee account
4148        {
4149            let (
4150                token_a_key,
4151                mut token_a_account,
4152                token_b_key,
4153                mut token_b_account,
4154                _pool_key,
4155                mut _pool_account,
4156            ) = accounts.setup_token_accounts(&user_key, &withdrawer_key, 0, 0, 0);
4157
4158            let pool_fee_key = accounts.pool_fee_key;
4159            let mut pool_fee_account = accounts.pool_fee_account.clone();
4160            let fee_account = gpl_token::state::Account::unpack(&pool_fee_account.data).unwrap();
4161            let pool_fee_amount = fee_account.amount;
4162
4163            accounts
4164                .withdraw_all_token_types(
4165                    &user_key,
4166                    &pool_fee_key,
4167                    &mut pool_fee_account,
4168                    &token_a_key,
4169                    &mut token_a_account,
4170                    &token_b_key,
4171                    &mut token_b_account,
4172                    pool_fee_amount,
4173                    0,
4174                    0,
4175                )
4176                .unwrap();
4177
4178            let swap_token_a =
4179                gpl_token::state::Account::unpack(&accounts.token_a_account.data).unwrap();
4180            let swap_token_b =
4181                gpl_token::state::Account::unpack(&accounts.token_b_account.data).unwrap();
4182            let pool_mint =
4183                gpl_token::state::Mint::unpack(&accounts.pool_mint_account.data).unwrap();
4184            let results = accounts
4185                .swap_curve
4186                .calculator
4187                .pool_tokens_to_trading_tokens(
4188                    pool_fee_amount.try_into().unwrap(),
4189                    pool_mint.supply.try_into().unwrap(),
4190                    swap_token_a.amount.try_into().unwrap(),
4191                    swap_token_b.amount.try_into().unwrap(),
4192                    RoundDirection::Floor,
4193                )
4194                .unwrap();
4195            let token_a = gpl_token::state::Account::unpack(&token_a_account.data).unwrap();
4196            assert_eq!(
4197                token_a.amount,
4198                TryInto::<u64>::try_into(results.token_a_amount).unwrap()
4199            );
4200            let token_b = gpl_token::state::Account::unpack(&token_b_account.data).unwrap();
4201            assert_eq!(
4202                token_b.amount,
4203                TryInto::<u64>::try_into(results.token_b_amount).unwrap()
4204            );
4205        }
4206    }
4207
4208    #[test]
4209    fn test_deposit_one_exact_in() {
4210        let user_key = Pubkey::new_unique();
4211        let depositor_key = Pubkey::new_unique();
4212        let trade_fee_numerator = 1;
4213        let trade_fee_denominator = 2;
4214        let owner_trade_fee_numerator = 1;
4215        let owner_trade_fee_denominator = 10;
4216        let owner_withdraw_fee_numerator = 1;
4217        let owner_withdraw_fee_denominator = 5;
4218        let host_fee_numerator = 20;
4219        let host_fee_denominator = 100;
4220
4221        let fees = Fees {
4222            trade_fee_numerator,
4223            trade_fee_denominator,
4224            owner_trade_fee_numerator,
4225            owner_trade_fee_denominator,
4226            owner_withdraw_fee_numerator,
4227            owner_withdraw_fee_denominator,
4228            host_fee_numerator,
4229            host_fee_denominator,
4230        };
4231
4232        let token_a_amount = 1000;
4233        let token_b_amount = 9000;
4234        let curve_type = CurveType::ConstantProduct;
4235        let swap_curve = SwapCurve {
4236            curve_type,
4237            calculator: Box::new(ConstantProductCurve {}),
4238        };
4239
4240        let mut accounts =
4241            SwapAccountInfo::new(&user_key, fees, swap_curve, token_a_amount, token_b_amount);
4242
4243        let deposit_a = token_a_amount / 10;
4244        let deposit_b = token_b_amount / 10;
4245        let pool_amount = to_u64(INITIAL_SWAP_POOL_AMOUNT / 100).unwrap();
4246
4247        // swap not initialized
4248        {
4249            let (
4250                token_a_key,
4251                mut token_a_account,
4252                _token_b_key,
4253                _token_b_account,
4254                pool_key,
4255                mut pool_account,
4256            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
4257            assert_eq!(
4258                Err(ProgramError::UninitializedAccount),
4259                accounts.deposit_single_token_type_exact_amount_in(
4260                    &depositor_key,
4261                    &token_a_key,
4262                    &mut token_a_account,
4263                    &pool_key,
4264                    &mut pool_account,
4265                    deposit_a,
4266                    pool_amount,
4267                )
4268            );
4269        }
4270
4271        accounts.initialize_swap().unwrap();
4272
4273        // wrong owner for swap account
4274        {
4275            let (
4276                token_a_key,
4277                mut token_a_account,
4278                _token_b_key,
4279                _token_b_account,
4280                pool_key,
4281                mut pool_account,
4282            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
4283            let old_swap_account = accounts.swap_account;
4284            let mut wrong_swap_account = old_swap_account.clone();
4285            wrong_swap_account.owner = gpl_token::id();
4286            accounts.swap_account = wrong_swap_account;
4287            assert_eq!(
4288                Err(ProgramError::IncorrectProgramId),
4289                accounts.deposit_single_token_type_exact_amount_in(
4290                    &depositor_key,
4291                    &token_a_key,
4292                    &mut token_a_account,
4293                    &pool_key,
4294                    &mut pool_account,
4295                    deposit_a,
4296                    pool_amount,
4297                )
4298            );
4299            accounts.swap_account = old_swap_account;
4300        }
4301
4302        // wrong bump seed for authority_key
4303        {
4304            let (
4305                token_a_key,
4306                mut token_a_account,
4307                _token_b_key,
4308                _token_b_account,
4309                pool_key,
4310                mut pool_account,
4311            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
4312            let old_authority = accounts.authority_key;
4313            let (bad_authority_key, _bump_seed) = Pubkey::find_program_address(
4314                &[&accounts.swap_key.to_bytes()[..]],
4315                &gpl_token::id(),
4316            );
4317            accounts.authority_key = bad_authority_key;
4318            assert_eq!(
4319                Err(SwapError::InvalidProgramAddress.into()),
4320                accounts.deposit_single_token_type_exact_amount_in(
4321                    &depositor_key,
4322                    &token_a_key,
4323                    &mut token_a_account,
4324                    &pool_key,
4325                    &mut pool_account,
4326                    deposit_a,
4327                    pool_amount,
4328                )
4329            );
4330            accounts.authority_key = old_authority;
4331        }
4332
4333        // not enough token A / B
4334        {
4335            let (
4336                token_a_key,
4337                mut token_a_account,
4338                token_b_key,
4339                mut token_b_account,
4340                pool_key,
4341                mut pool_account,
4342            ) = accounts.setup_token_accounts(
4343                &user_key,
4344                &depositor_key,
4345                deposit_a / 2,
4346                deposit_b / 2,
4347                0,
4348            );
4349            assert_eq!(
4350                Err(TokenError::InsufficientFunds.into()),
4351                accounts.deposit_single_token_type_exact_amount_in(
4352                    &depositor_key,
4353                    &token_a_key,
4354                    &mut token_a_account,
4355                    &pool_key,
4356                    &mut pool_account,
4357                    deposit_a,
4358                    0,
4359                )
4360            );
4361            assert_eq!(
4362                Err(TokenError::InsufficientFunds.into()),
4363                accounts.deposit_single_token_type_exact_amount_in(
4364                    &depositor_key,
4365                    &token_b_key,
4366                    &mut token_b_account,
4367                    &pool_key,
4368                    &mut pool_account,
4369                    deposit_b,
4370                    0,
4371                )
4372            );
4373        }
4374
4375        // wrong pool token account
4376        {
4377            let (
4378                token_a_key,
4379                mut token_a_account,
4380                token_b_key,
4381                mut token_b_account,
4382                _pool_key,
4383                mut _pool_account,
4384            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
4385            assert_eq!(
4386                Err(TokenError::MintMismatch.into()),
4387                accounts.deposit_single_token_type_exact_amount_in(
4388                    &depositor_key,
4389                    &token_a_key,
4390                    &mut token_a_account,
4391                    &token_b_key,
4392                    &mut token_b_account,
4393                    deposit_a,
4394                    pool_amount,
4395                )
4396            );
4397        }
4398
4399        // no approval
4400        {
4401            let (
4402                token_a_key,
4403                mut token_a_account,
4404                _token_b_key,
4405                _token_b_account,
4406                pool_key,
4407                mut pool_account,
4408            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
4409            let user_transfer_authority_key = Pubkey::new_unique();
4410            assert_eq!(
4411                Err(TokenError::OwnerMismatch.into()),
4412                do_process_instruction(
4413                    deposit_single_token_type_exact_amount_in(
4414                        &SWAP_PROGRAM_ID,
4415                        &gpl_token::id(),
4416                        &accounts.swap_key,
4417                        &accounts.authority_key,
4418                        &user_transfer_authority_key,
4419                        &token_a_key,
4420                        &accounts.token_a_key,
4421                        &accounts.token_b_key,
4422                        &accounts.pool_mint_key,
4423                        &pool_key,
4424                        DepositSingleTokenTypeExactAmountIn {
4425                            source_token_amount: deposit_a,
4426                            minimum_pool_token_amount: pool_amount,
4427                        },
4428                    )
4429                    .unwrap(),
4430                    vec![
4431                        &mut accounts.swap_account,
4432                        &mut Account::default(),
4433                        &mut Account::default(),
4434                        &mut token_a_account,
4435                        &mut accounts.token_a_account,
4436                        &mut accounts.token_b_account,
4437                        &mut accounts.pool_mint_account,
4438                        &mut pool_account,
4439                        &mut Account::default(),
4440                    ],
4441                )
4442            );
4443        }
4444
4445        // wrong token program id
4446        {
4447            let (
4448                token_a_key,
4449                mut token_a_account,
4450                _token_b_key,
4451                _token_b_account,
4452                pool_key,
4453                mut pool_account,
4454            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
4455            let wrong_key = Pubkey::new_unique();
4456            assert_eq!(
4457                Err(SwapError::IncorrectTokenProgramId.into()),
4458                do_process_instruction(
4459                    deposit_single_token_type_exact_amount_in(
4460                        &SWAP_PROGRAM_ID,
4461                        &wrong_key,
4462                        &accounts.swap_key,
4463                        &accounts.authority_key,
4464                        &accounts.authority_key,
4465                        &token_a_key,
4466                        &accounts.token_a_key,
4467                        &accounts.token_b_key,
4468                        &accounts.pool_mint_key,
4469                        &pool_key,
4470                        DepositSingleTokenTypeExactAmountIn {
4471                            source_token_amount: deposit_a,
4472                            minimum_pool_token_amount: pool_amount,
4473                        },
4474                    )
4475                    .unwrap(),
4476                    vec![
4477                        &mut accounts.swap_account,
4478                        &mut Account::default(),
4479                        &mut Account::default(),
4480                        &mut token_a_account,
4481                        &mut accounts.token_a_account,
4482                        &mut accounts.token_b_account,
4483                        &mut accounts.pool_mint_account,
4484                        &mut pool_account,
4485                        &mut Account::default(),
4486                    ],
4487                )
4488            );
4489        }
4490
4491        // wrong swap token accounts
4492        {
4493            let (
4494                token_a_key,
4495                mut token_a_account,
4496                token_b_key,
4497                token_b_account,
4498                pool_key,
4499                mut pool_account,
4500            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
4501
4502            let old_a_key = accounts.token_a_key;
4503            let old_a_account = accounts.token_a_account;
4504
4505            accounts.token_a_key = token_a_key;
4506            accounts.token_a_account = token_a_account.clone();
4507
4508            // wrong swap token a account
4509            assert_eq!(
4510                Err(SwapError::IncorrectSwapAccount.into()),
4511                accounts.deposit_single_token_type_exact_amount_in(
4512                    &depositor_key,
4513                    &token_a_key,
4514                    &mut token_a_account,
4515                    &pool_key,
4516                    &mut pool_account,
4517                    deposit_a,
4518                    pool_amount,
4519                )
4520            );
4521
4522            accounts.token_a_key = old_a_key;
4523            accounts.token_a_account = old_a_account;
4524
4525            let old_b_key = accounts.token_b_key;
4526            let old_b_account = accounts.token_b_account;
4527
4528            accounts.token_b_key = token_b_key;
4529            accounts.token_b_account = token_b_account;
4530
4531            // wrong swap token b account
4532            assert_eq!(
4533                Err(SwapError::IncorrectSwapAccount.into()),
4534                accounts.deposit_single_token_type_exact_amount_in(
4535                    &depositor_key,
4536                    &token_a_key,
4537                    &mut token_a_account,
4538                    &pool_key,
4539                    &mut pool_account,
4540                    deposit_a,
4541                    pool_amount,
4542                )
4543            );
4544
4545            accounts.token_b_key = old_b_key;
4546            accounts.token_b_account = old_b_account;
4547        }
4548
4549        // wrong mint
4550        {
4551            let (
4552                token_a_key,
4553                mut token_a_account,
4554                _token_b_key,
4555                _token_b_account,
4556                pool_key,
4557                mut pool_account,
4558            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
4559            let (pool_mint_key, pool_mint_account) =
4560                create_mint(&gpl_token::id(), &accounts.authority_key, None);
4561            let old_pool_key = accounts.pool_mint_key;
4562            let old_pool_account = accounts.pool_mint_account;
4563            accounts.pool_mint_key = pool_mint_key;
4564            accounts.pool_mint_account = pool_mint_account;
4565
4566            assert_eq!(
4567                Err(SwapError::IncorrectPoolMint.into()),
4568                accounts.deposit_single_token_type_exact_amount_in(
4569                    &depositor_key,
4570                    &token_a_key,
4571                    &mut token_a_account,
4572                    &pool_key,
4573                    &mut pool_account,
4574                    deposit_a,
4575                    pool_amount,
4576                )
4577            );
4578
4579            accounts.pool_mint_key = old_pool_key;
4580            accounts.pool_mint_account = old_pool_account;
4581        }
4582
4583        // slippage exceeded
4584        {
4585            let (
4586                token_a_key,
4587                mut token_a_account,
4588                token_b_key,
4589                mut token_b_account,
4590                pool_key,
4591                mut pool_account,
4592            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
4593            // minimum pool amount too high
4594            assert_eq!(
4595                Err(SwapError::ExceededSlippage.into()),
4596                accounts.deposit_single_token_type_exact_amount_in(
4597                    &depositor_key,
4598                    &token_a_key,
4599                    &mut token_a_account,
4600                    &pool_key,
4601                    &mut pool_account,
4602                    deposit_a / 10,
4603                    pool_amount,
4604                )
4605            );
4606            // minimum pool amount too high
4607            assert_eq!(
4608                Err(SwapError::ExceededSlippage.into()),
4609                accounts.deposit_single_token_type_exact_amount_in(
4610                    &depositor_key,
4611                    &token_b_key,
4612                    &mut token_b_account,
4613                    &pool_key,
4614                    &mut pool_account,
4615                    deposit_b / 10,
4616                    pool_amount,
4617                )
4618            );
4619        }
4620
4621        // invalid input: can't use swap pool tokens as source
4622        {
4623            let (
4624                _token_a_key,
4625                _token_a_account,
4626                _token_b_key,
4627                _token_b_account,
4628                pool_key,
4629                mut pool_account,
4630            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
4631            let swap_token_a_key = accounts.token_a_key;
4632            let mut swap_token_a_account = accounts.get_token_account(&swap_token_a_key).clone();
4633            let swap_token_b_key = accounts.token_b_key;
4634            let mut swap_token_b_account = accounts.get_token_account(&swap_token_b_key).clone();
4635            let authority_key = accounts.authority_key;
4636            assert_eq!(
4637                Err(SwapError::InvalidInput.into()),
4638                accounts.deposit_single_token_type_exact_amount_in(
4639                    &authority_key,
4640                    &swap_token_a_key,
4641                    &mut swap_token_a_account,
4642                    &pool_key,
4643                    &mut pool_account,
4644                    deposit_a,
4645                    pool_amount,
4646                )
4647            );
4648            assert_eq!(
4649                Err(SwapError::InvalidInput.into()),
4650                accounts.deposit_single_token_type_exact_amount_in(
4651                    &authority_key,
4652                    &swap_token_b_key,
4653                    &mut swap_token_b_account,
4654                    &pool_key,
4655                    &mut pool_account,
4656                    deposit_b,
4657                    pool_amount,
4658                )
4659            );
4660        }
4661
4662        // correctly deposit
4663        {
4664            let (
4665                token_a_key,
4666                mut token_a_account,
4667                token_b_key,
4668                mut token_b_account,
4669                pool_key,
4670                mut pool_account,
4671            ) = accounts.setup_token_accounts(&user_key, &depositor_key, deposit_a, deposit_b, 0);
4672            accounts
4673                .deposit_single_token_type_exact_amount_in(
4674                    &depositor_key,
4675                    &token_a_key,
4676                    &mut token_a_account,
4677                    &pool_key,
4678                    &mut pool_account,
4679                    deposit_a,
4680                    pool_amount,
4681                )
4682                .unwrap();
4683
4684            let swap_token_a =
4685                gpl_token::state::Account::unpack(&accounts.token_a_account.data).unwrap();
4686            assert_eq!(swap_token_a.amount, deposit_a + token_a_amount);
4687
4688            let token_a = gpl_token::state::Account::unpack(&token_a_account.data).unwrap();
4689            assert_eq!(token_a.amount, 0);
4690
4691            accounts
4692                .deposit_single_token_type_exact_amount_in(
4693                    &depositor_key,
4694                    &token_b_key,
4695                    &mut token_b_account,
4696                    &pool_key,
4697                    &mut pool_account,
4698                    deposit_b,
4699                    pool_amount,
4700                )
4701                .unwrap();
4702            let swap_token_b =
4703                gpl_token::state::Account::unpack(&accounts.token_b_account.data).unwrap();
4704            assert_eq!(swap_token_b.amount, deposit_b + token_b_amount);
4705
4706            let token_b = gpl_token::state::Account::unpack(&token_b_account.data).unwrap();
4707            assert_eq!(token_b.amount, 0);
4708
4709            let pool_account = gpl_token::state::Account::unpack(&pool_account.data).unwrap();
4710            let swap_pool_account =
4711                gpl_token::state::Account::unpack(&accounts.pool_token_account.data).unwrap();
4712            let pool_mint =
4713                gpl_token::state::Mint::unpack(&accounts.pool_mint_account.data).unwrap();
4714            assert_eq!(
4715                pool_mint.supply,
4716                pool_account.amount + swap_pool_account.amount
4717            );
4718        }
4719    }
4720
4721    #[test]
4722    fn test_withdraw_one_exact_out() {
4723        let user_key = Pubkey::new_unique();
4724        let trade_fee_numerator = 1;
4725        let trade_fee_denominator = 2;
4726        let owner_trade_fee_numerator = 1;
4727        let owner_trade_fee_denominator = 10;
4728        let owner_withdraw_fee_numerator = 1;
4729        let owner_withdraw_fee_denominator = 5;
4730        let host_fee_numerator = 7;
4731        let host_fee_denominator = 100;
4732
4733        let fees = Fees {
4734            trade_fee_numerator,
4735            trade_fee_denominator,
4736            owner_trade_fee_numerator,
4737            owner_trade_fee_denominator,
4738            owner_withdraw_fee_numerator,
4739            owner_withdraw_fee_denominator,
4740            host_fee_numerator,
4741            host_fee_denominator,
4742        };
4743
4744        let token_a_amount = 100_000;
4745        let token_b_amount = 200_000;
4746        let curve_type = CurveType::ConstantProduct;
4747        let swap_curve = SwapCurve {
4748            curve_type,
4749            calculator: Box::new(ConstantProductCurve {}),
4750        };
4751
4752        let withdrawer_key = Pubkey::new_unique();
4753        let initial_a = token_a_amount / 10;
4754        let initial_b = token_b_amount / 10;
4755        let initial_pool = swap_curve.calculator.new_pool_supply() / 10;
4756        let maximum_pool_token_amount = to_u64(initial_pool / 4).unwrap();
4757        let destination_a_amount = initial_a / 40;
4758        let destination_b_amount = initial_b / 40;
4759
4760        let mut accounts =
4761            SwapAccountInfo::new(&user_key, fees, swap_curve, token_a_amount, token_b_amount);
4762
4763        // swap not initialized
4764        {
4765            let (
4766                token_a_key,
4767                mut token_a_account,
4768                _token_b_key,
4769                _token_b_account,
4770                pool_key,
4771                mut pool_account,
4772            ) = accounts.setup_token_accounts(&user_key, &withdrawer_key, initial_a, initial_b, 0);
4773            assert_eq!(
4774                Err(ProgramError::UninitializedAccount),
4775                accounts.withdraw_single_token_type_exact_amount_out(
4776                    &withdrawer_key,
4777                    &pool_key,
4778                    &mut pool_account,
4779                    &token_a_key,
4780                    &mut token_a_account,
4781                    destination_a_amount,
4782                    maximum_pool_token_amount,
4783                )
4784            );
4785        }
4786
4787        accounts.initialize_swap().unwrap();
4788
4789        // wrong owner for swap account
4790        {
4791            let (
4792                token_a_key,
4793                mut token_a_account,
4794                _token_b_key,
4795                _token_b_account,
4796                pool_key,
4797                mut pool_account,
4798            ) = accounts.setup_token_accounts(&user_key, &withdrawer_key, initial_a, initial_b, 0);
4799            let old_swap_account = accounts.swap_account;
4800            let mut wrong_swap_account = old_swap_account.clone();
4801            wrong_swap_account.owner = gpl_token::id();
4802            accounts.swap_account = wrong_swap_account;
4803            assert_eq!(
4804                Err(ProgramError::IncorrectProgramId),
4805                accounts.withdraw_single_token_type_exact_amount_out(
4806                    &withdrawer_key,
4807                    &pool_key,
4808                    &mut pool_account,
4809                    &token_a_key,
4810                    &mut token_a_account,
4811                    destination_a_amount,
4812                    maximum_pool_token_amount,
4813                )
4814            );
4815            accounts.swap_account = old_swap_account;
4816        }
4817
4818        // wrong bump seed for authority_key
4819        {
4820            let (
4821                _token_a_key,
4822                _token_a_account,
4823                token_b_key,
4824                mut token_b_account,
4825                pool_key,
4826                mut pool_account,
4827            ) = accounts.setup_token_accounts(&user_key, &withdrawer_key, initial_a, initial_b, 0);
4828            let old_authority = accounts.authority_key;
4829            let (bad_authority_key, _bump_seed) = Pubkey::find_program_address(
4830                &[&accounts.swap_key.to_bytes()[..]],
4831                &gpl_token::id(),
4832            );
4833            accounts.authority_key = bad_authority_key;
4834            assert_eq!(
4835                Err(SwapError::InvalidProgramAddress.into()),
4836                accounts.withdraw_single_token_type_exact_amount_out(
4837                    &withdrawer_key,
4838                    &pool_key,
4839                    &mut pool_account,
4840                    &token_b_key,
4841                    &mut token_b_account,
4842                    destination_b_amount,
4843                    maximum_pool_token_amount,
4844                )
4845            );
4846            accounts.authority_key = old_authority;
4847        }
4848
4849        // not enough pool tokens
4850        {
4851            let (
4852                _token_a_key,
4853                _token_a_account,
4854                token_b_key,
4855                mut token_b_account,
4856                pool_key,
4857                mut pool_account,
4858            ) = accounts.setup_token_accounts(
4859                &user_key,
4860                &withdrawer_key,
4861                initial_a,
4862                initial_b,
4863                maximum_pool_token_amount / 1000,
4864            );
4865            assert_eq!(
4866                Err(TokenError::InsufficientFunds.into()),
4867                accounts.withdraw_single_token_type_exact_amount_out(
4868                    &withdrawer_key,
4869                    &pool_key,
4870                    &mut pool_account,
4871                    &token_b_key,
4872                    &mut token_b_account,
4873                    destination_b_amount,
4874                    maximum_pool_token_amount,
4875                )
4876            );
4877        }
4878
4879        // wrong pool token account
4880        {
4881            let (
4882                token_a_key,
4883                mut token_a_account,
4884                token_b_key,
4885                mut token_b_account,
4886                _pool_key,
4887                _pool_account,
4888            ) = accounts.setup_token_accounts(
4889                &user_key,
4890                &withdrawer_key,
4891                maximum_pool_token_amount,
4892                initial_b,
4893                maximum_pool_token_amount,
4894            );
4895            assert_eq!(
4896                Err(TokenError::MintMismatch.into()),
4897                accounts.withdraw_single_token_type_exact_amount_out(
4898                    &withdrawer_key,
4899                    &token_a_key,
4900                    &mut token_a_account,
4901                    &token_b_key,
4902                    &mut token_b_account,
4903                    destination_b_amount,
4904                    maximum_pool_token_amount,
4905                )
4906            );
4907        }
4908
4909        // wrong pool fee account
4910        {
4911            let (
4912                token_a_key,
4913                mut token_a_account,
4914                _token_b_key,
4915                _token_b_account,
4916                wrong_pool_key,
4917                wrong_pool_account,
4918            ) = accounts.setup_token_accounts(
4919                &user_key,
4920                &withdrawer_key,
4921                initial_a,
4922                initial_b,
4923                maximum_pool_token_amount,
4924            );
4925            let (
4926                _token_a_key,
4927                _token_a_account,
4928                _token_b_key,
4929                _token_b_account,
4930                pool_key,
4931                mut pool_account,
4932            ) = accounts.setup_token_accounts(
4933                &user_key,
4934                &withdrawer_key,
4935                initial_a,
4936                initial_b,
4937                maximum_pool_token_amount,
4938            );
4939            let old_pool_fee_account = accounts.pool_fee_account;
4940            let old_pool_fee_key = accounts.pool_fee_key;
4941            accounts.pool_fee_account = wrong_pool_account;
4942            accounts.pool_fee_key = wrong_pool_key;
4943            assert_eq!(
4944                Err(SwapError::IncorrectFeeAccount.into()),
4945                accounts.withdraw_single_token_type_exact_amount_out(
4946                    &withdrawer_key,
4947                    &pool_key,
4948                    &mut pool_account,
4949                    &token_a_key,
4950                    &mut token_a_account,
4951                    destination_a_amount,
4952                    maximum_pool_token_amount,
4953                )
4954            );
4955            accounts.pool_fee_account = old_pool_fee_account;
4956            accounts.pool_fee_key = old_pool_fee_key;
4957        }
4958
4959        // no approval
4960        {
4961            let (
4962                token_a_key,
4963                mut token_a_account,
4964                _token_b_key,
4965                _token_b_account,
4966                pool_key,
4967                mut pool_account,
4968            ) = accounts.setup_token_accounts(
4969                &user_key,
4970                &withdrawer_key,
4971                0,
4972                0,
4973                maximum_pool_token_amount,
4974            );
4975            let user_transfer_authority_key = Pubkey::new_unique();
4976            assert_eq!(
4977                Err(TokenError::OwnerMismatch.into()),
4978                do_process_instruction(
4979                    withdraw_single_token_type_exact_amount_out(
4980                        &SWAP_PROGRAM_ID,
4981                        &gpl_token::id(),
4982                        &accounts.swap_key,
4983                        &accounts.authority_key,
4984                        &user_transfer_authority_key,
4985                        &accounts.pool_mint_key,
4986                        &accounts.pool_fee_key,
4987                        &pool_key,
4988                        &accounts.token_a_key,
4989                        &accounts.token_b_key,
4990                        &token_a_key,
4991                        WithdrawSingleTokenTypeExactAmountOut {
4992                            destination_token_amount: destination_a_amount,
4993                            maximum_pool_token_amount,
4994                        }
4995                    )
4996                    .unwrap(),
4997                    vec![
4998                        &mut accounts.swap_account,
4999                        &mut Account::default(),
5000                        &mut Account::default(),
5001                        &mut accounts.pool_mint_account,
5002                        &mut pool_account,
5003                        &mut accounts.token_a_account,
5004                        &mut accounts.token_b_account,
5005                        &mut token_a_account,
5006                        &mut accounts.pool_fee_account,
5007                        &mut Account::default(),
5008                    ],
5009                )
5010            );
5011        }
5012
5013        // wrong token program id
5014        {
5015            let (
5016                token_a_key,
5017                mut token_a_account,
5018                _token_b_key,
5019                _token_b_account,
5020                pool_key,
5021                mut pool_account,
5022            ) = accounts.setup_token_accounts(
5023                &user_key,
5024                &withdrawer_key,
5025                initial_a,
5026                initial_b,
5027                maximum_pool_token_amount,
5028            );
5029            let wrong_key = Pubkey::new_unique();
5030            assert_eq!(
5031                Err(SwapError::IncorrectTokenProgramId.into()),
5032                do_process_instruction(
5033                    withdraw_single_token_type_exact_amount_out(
5034                        &SWAP_PROGRAM_ID,
5035                        &wrong_key,
5036                        &accounts.swap_key,
5037                        &accounts.authority_key,
5038                        &accounts.authority_key,
5039                        &accounts.pool_mint_key,
5040                        &accounts.pool_fee_key,
5041                        &pool_key,
5042                        &accounts.token_a_key,
5043                        &accounts.token_b_key,
5044                        &token_a_key,
5045                        WithdrawSingleTokenTypeExactAmountOut {
5046                            destination_token_amount: destination_a_amount,
5047                            maximum_pool_token_amount,
5048                        }
5049                    )
5050                    .unwrap(),
5051                    vec![
5052                        &mut accounts.swap_account,
5053                        &mut Account::default(),
5054                        &mut Account::default(),
5055                        &mut accounts.pool_mint_account,
5056                        &mut pool_account,
5057                        &mut accounts.token_a_account,
5058                        &mut accounts.token_b_account,
5059                        &mut token_a_account,
5060                        &mut accounts.pool_fee_account,
5061                        &mut Account::default(),
5062                    ],
5063                )
5064            );
5065        }
5066
5067        // wrong swap token accounts
5068        {
5069            let (
5070                token_a_key,
5071                mut token_a_account,
5072                token_b_key,
5073                mut token_b_account,
5074                pool_key,
5075                mut pool_account,
5076            ) = accounts.setup_token_accounts(
5077                &user_key,
5078                &withdrawer_key,
5079                initial_a,
5080                initial_b,
5081                initial_pool.try_into().unwrap(),
5082            );
5083
5084            let old_a_key = accounts.token_a_key;
5085            let old_a_account = accounts.token_a_account;
5086
5087            accounts.token_a_key = token_a_key;
5088            accounts.token_a_account = token_a_account.clone();
5089
5090            // wrong swap token a account
5091            assert_eq!(
5092                Err(SwapError::IncorrectSwapAccount.into()),
5093                accounts.withdraw_single_token_type_exact_amount_out(
5094                    &withdrawer_key,
5095                    &pool_key,
5096                    &mut pool_account,
5097                    &token_a_key,
5098                    &mut token_a_account,
5099                    destination_a_amount,
5100                    maximum_pool_token_amount,
5101                )
5102            );
5103
5104            accounts.token_a_key = old_a_key;
5105            accounts.token_a_account = old_a_account;
5106
5107            let old_b_key = accounts.token_b_key;
5108            let old_b_account = accounts.token_b_account;
5109
5110            accounts.token_b_key = token_b_key;
5111            accounts.token_b_account = token_b_account.clone();
5112
5113            // wrong swap token b account
5114            assert_eq!(
5115                Err(SwapError::IncorrectSwapAccount.into()),
5116                accounts.withdraw_single_token_type_exact_amount_out(
5117                    &withdrawer_key,
5118                    &pool_key,
5119                    &mut pool_account,
5120                    &token_b_key,
5121                    &mut token_b_account,
5122                    destination_b_amount,
5123                    maximum_pool_token_amount,
5124                )
5125            );
5126
5127            accounts.token_b_key = old_b_key;
5128            accounts.token_b_account = old_b_account;
5129        }
5130
5131        // wrong mint
5132        {
5133            let (
5134                token_a_key,
5135                mut token_a_account,
5136                _token_b_key,
5137                _token_b_account,
5138                pool_key,
5139                mut pool_account,
5140            ) = accounts.setup_token_accounts(
5141                &user_key,
5142                &withdrawer_key,
5143                initial_a,
5144                initial_b,
5145                initial_pool.try_into().unwrap(),
5146            );
5147            let (pool_mint_key, pool_mint_account) =
5148                create_mint(&gpl_token::id(), &accounts.authority_key, None);
5149            let old_pool_key = accounts.pool_mint_key;
5150            let old_pool_account = accounts.pool_mint_account;
5151            accounts.pool_mint_key = pool_mint_key;
5152            accounts.pool_mint_account = pool_mint_account;
5153
5154            assert_eq!(
5155                Err(SwapError::IncorrectPoolMint.into()),
5156                accounts.withdraw_single_token_type_exact_amount_out(
5157                    &withdrawer_key,
5158                    &pool_key,
5159                    &mut pool_account,
5160                    &token_a_key,
5161                    &mut token_a_account,
5162                    destination_a_amount,
5163                    maximum_pool_token_amount,
5164                )
5165            );
5166
5167            accounts.pool_mint_key = old_pool_key;
5168            accounts.pool_mint_account = old_pool_account;
5169        }
5170
5171        // slippage exceeded
5172        {
5173            let (
5174                token_a_key,
5175                mut token_a_account,
5176                token_b_key,
5177                mut token_b_account,
5178                pool_key,
5179                mut pool_account,
5180            ) = accounts.setup_token_accounts(
5181                &user_key,
5182                &withdrawer_key,
5183                initial_a,
5184                initial_b,
5185                maximum_pool_token_amount,
5186            );
5187
5188            // maximum pool token amount too low
5189            assert_eq!(
5190                Err(SwapError::ExceededSlippage.into()),
5191                accounts.withdraw_single_token_type_exact_amount_out(
5192                    &withdrawer_key,
5193                    &pool_key,
5194                    &mut pool_account,
5195                    &token_a_key,
5196                    &mut token_a_account,
5197                    destination_a_amount,
5198                    maximum_pool_token_amount / 1000,
5199                )
5200            );
5201            assert_eq!(
5202                Err(SwapError::ExceededSlippage.into()),
5203                accounts.withdraw_single_token_type_exact_amount_out(
5204                    &withdrawer_key,
5205                    &pool_key,
5206                    &mut pool_account,
5207                    &token_b_key,
5208                    &mut token_b_account,
5209                    destination_b_amount,
5210                    maximum_pool_token_amount / 1000,
5211                )
5212            );
5213        }
5214
5215        // invalid input: can't use swap pool tokens as destination
5216        {
5217            let (
5218                _token_a_key,
5219                _token_a_account,
5220                _token_b_key,
5221                _token_b_account,
5222                pool_key,
5223                mut pool_account,
5224            ) = accounts.setup_token_accounts(
5225                &user_key,
5226                &withdrawer_key,
5227                initial_a,
5228                initial_b,
5229                maximum_pool_token_amount,
5230            );
5231            let swap_token_a_key = accounts.token_a_key;
5232            let mut swap_token_a_account = accounts.get_token_account(&swap_token_a_key).clone();
5233            assert_eq!(
5234                Err(SwapError::InvalidInput.into()),
5235                accounts.withdraw_single_token_type_exact_amount_out(
5236                    &withdrawer_key,
5237                    &pool_key,
5238                    &mut pool_account,
5239                    &swap_token_a_key,
5240                    &mut swap_token_a_account,
5241                    destination_a_amount,
5242                    maximum_pool_token_amount,
5243                )
5244            );
5245            let swap_token_b_key = accounts.token_b_key;
5246            let mut swap_token_b_account = accounts.get_token_account(&swap_token_b_key).clone();
5247            assert_eq!(
5248                Err(SwapError::InvalidInput.into()),
5249                accounts.withdraw_single_token_type_exact_amount_out(
5250                    &withdrawer_key,
5251                    &pool_key,
5252                    &mut pool_account,
5253                    &swap_token_b_key,
5254                    &mut swap_token_b_account,
5255                    destination_b_amount,
5256                    maximum_pool_token_amount,
5257                )
5258            );
5259        }
5260
5261        // correct withdrawal
5262        {
5263            let (
5264                token_a_key,
5265                mut token_a_account,
5266                _token_b_key,
5267                _token_b_account,
5268                pool_key,
5269                mut pool_account,
5270            ) = accounts.setup_token_accounts(
5271                &user_key,
5272                &withdrawer_key,
5273                initial_a,
5274                initial_b,
5275                initial_pool.try_into().unwrap(),
5276            );
5277
5278            let swap_token_a =
5279                gpl_token::state::Account::unpack(&accounts.token_a_account.data).unwrap();
5280            let swap_token_b =
5281                gpl_token::state::Account::unpack(&accounts.token_b_account.data).unwrap();
5282            let pool_mint =
5283                gpl_token::state::Mint::unpack(&accounts.pool_mint_account.data).unwrap();
5284
5285            let pool_token_amount = accounts
5286                .swap_curve
5287                .withdraw_single_token_type_exact_out(
5288                    destination_a_amount.try_into().unwrap(),
5289                    swap_token_a.amount.try_into().unwrap(),
5290                    swap_token_b.amount.try_into().unwrap(),
5291                    pool_mint.supply.try_into().unwrap(),
5292                    TradeDirection::AtoB,
5293                    &accounts.fees,
5294                )
5295                .unwrap();
5296            let withdraw_fee = accounts.fees.owner_withdraw_fee(pool_token_amount).unwrap();
5297
5298            accounts
5299                .withdraw_single_token_type_exact_amount_out(
5300                    &withdrawer_key,
5301                    &pool_key,
5302                    &mut pool_account,
5303                    &token_a_key,
5304                    &mut token_a_account,
5305                    destination_a_amount,
5306                    maximum_pool_token_amount,
5307                )
5308                .unwrap();
5309
5310            let swap_token_a =
5311                gpl_token::state::Account::unpack(&accounts.token_a_account.data).unwrap();
5312
5313            assert_eq!(swap_token_a.amount, token_a_amount - destination_a_amount);
5314            let token_a = gpl_token::state::Account::unpack(&token_a_account.data).unwrap();
5315            assert_eq!(token_a.amount, initial_a + destination_a_amount);
5316
5317            let pool_account = gpl_token::state::Account::unpack(&pool_account.data).unwrap();
5318            assert_eq!(
5319                pool_account.amount,
5320                to_u64(initial_pool - pool_token_amount - withdraw_fee).unwrap()
5321            );
5322            let fee_account =
5323                gpl_token::state::Account::unpack(&accounts.pool_fee_account.data).unwrap();
5324            assert_eq!(fee_account.amount, to_u64(withdraw_fee).unwrap());
5325        }
5326
5327        // correct withdrawal from fee account
5328        {
5329            let (
5330                token_a_key,
5331                mut token_a_account,
5332                _token_b_key,
5333                _token_b_account,
5334                _pool_key,
5335                _pool_account,
5336            ) = accounts.setup_token_accounts(&user_key, &withdrawer_key, initial_a, initial_b, 0);
5337
5338            let fee_a_amount = 2;
5339            let pool_fee_key = accounts.pool_fee_key;
5340            let mut pool_fee_account = accounts.pool_fee_account.clone();
5341            let fee_account = gpl_token::state::Account::unpack(&pool_fee_account.data).unwrap();
5342            let pool_fee_amount = fee_account.amount;
5343
5344            let swap_token_a =
5345                gpl_token::state::Account::unpack(&accounts.token_a_account.data).unwrap();
5346
5347            let token_a_amount = swap_token_a.amount;
5348            accounts
5349                .withdraw_single_token_type_exact_amount_out(
5350                    &user_key,
5351                    &pool_fee_key,
5352                    &mut pool_fee_account,
5353                    &token_a_key,
5354                    &mut token_a_account,
5355                    fee_a_amount,
5356                    pool_fee_amount,
5357                )
5358                .unwrap();
5359
5360            let swap_token_a =
5361                gpl_token::state::Account::unpack(&accounts.token_a_account.data).unwrap();
5362
5363            assert_eq!(swap_token_a.amount, token_a_amount - fee_a_amount);
5364            let token_a = gpl_token::state::Account::unpack(&token_a_account.data).unwrap();
5365            assert_eq!(token_a.amount, initial_a + fee_a_amount);
5366        }
5367    }
5368
5369    fn check_valid_swap_curve(
5370        fees: Fees,
5371        curve_type: CurveType,
5372        calculator: Box<dyn CurveCalculator>,
5373        token_a_amount: u64,
5374        token_b_amount: u64,
5375    ) {
5376        let user_key = Pubkey::new_unique();
5377        let swapper_key = Pubkey::new_unique();
5378
5379        let swap_curve = SwapCurve {
5380            curve_type,
5381            calculator,
5382        };
5383
5384        let mut accounts = SwapAccountInfo::new(
5385            &user_key,
5386            fees.clone(),
5387            swap_curve.clone(),
5388            token_a_amount,
5389            token_b_amount,
5390        );
5391        let initial_a = token_a_amount / 5;
5392        let initial_b = token_b_amount / 5;
5393        accounts.initialize_swap().unwrap();
5394
5395        let swap_token_a_key = accounts.token_a_key;
5396        let swap_token_b_key = accounts.token_b_key;
5397
5398        let (
5399            token_a_key,
5400            mut token_a_account,
5401            token_b_key,
5402            mut token_b_account,
5403            _pool_key,
5404            _pool_account,
5405        ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
5406        // swap one way
5407        let a_to_b_amount = initial_a / 10;
5408        let minimum_token_b_amount = 0;
5409        let pool_mint = gpl_token::state::Mint::unpack(&accounts.pool_mint_account.data).unwrap();
5410        let initial_supply = pool_mint.supply;
5411        accounts
5412            .swap(
5413                &swapper_key,
5414                &token_a_key,
5415                &mut token_a_account,
5416                &swap_token_a_key,
5417                &swap_token_b_key,
5418                &token_b_key,
5419                &mut token_b_account,
5420                a_to_b_amount,
5421                minimum_token_b_amount,
5422            )
5423            .unwrap();
5424
5425        let results = swap_curve
5426            .swap(
5427                a_to_b_amount.try_into().unwrap(),
5428                token_a_amount.try_into().unwrap(),
5429                token_b_amount.try_into().unwrap(),
5430                TradeDirection::AtoB,
5431                &fees,
5432            )
5433            .unwrap();
5434
5435        let swap_token_a =
5436            gpl_token::state::Account::unpack(&accounts.token_a_account.data).unwrap();
5437        let token_a_amount = swap_token_a.amount;
5438        assert_eq!(
5439            token_a_amount,
5440            TryInto::<u64>::try_into(results.new_swap_source_amount).unwrap()
5441        );
5442        let token_a = gpl_token::state::Account::unpack(&token_a_account.data).unwrap();
5443        assert_eq!(token_a.amount, initial_a - a_to_b_amount);
5444
5445        let swap_token_b =
5446            gpl_token::state::Account::unpack(&accounts.token_b_account.data).unwrap();
5447        let token_b_amount = swap_token_b.amount;
5448        assert_eq!(
5449            token_b_amount,
5450            TryInto::<u64>::try_into(results.new_swap_destination_amount).unwrap()
5451        );
5452        let token_b = gpl_token::state::Account::unpack(&token_b_account.data).unwrap();
5453        assert_eq!(
5454            token_b.amount,
5455            initial_b + to_u64(results.destination_amount_swapped).unwrap()
5456        );
5457
5458        let first_fee = swap_curve
5459            .withdraw_single_token_type_exact_out(
5460                results.owner_fee,
5461                token_a_amount.try_into().unwrap(),
5462                token_b_amount.try_into().unwrap(),
5463                initial_supply.try_into().unwrap(),
5464                TradeDirection::AtoB,
5465                &fees,
5466            )
5467            .unwrap();
5468        let fee_account =
5469            gpl_token::state::Account::unpack(&accounts.pool_fee_account.data).unwrap();
5470        assert_eq!(
5471            fee_account.amount,
5472            TryInto::<u64>::try_into(first_fee).unwrap()
5473        );
5474
5475        let first_swap_amount = results.destination_amount_swapped;
5476
5477        // swap the other way
5478        let pool_mint = gpl_token::state::Mint::unpack(&accounts.pool_mint_account.data).unwrap();
5479        let initial_supply = pool_mint.supply;
5480
5481        let b_to_a_amount = initial_b / 10;
5482        let minimum_a_amount = 0;
5483        accounts
5484            .swap(
5485                &swapper_key,
5486                &token_b_key,
5487                &mut token_b_account,
5488                &swap_token_b_key,
5489                &swap_token_a_key,
5490                &token_a_key,
5491                &mut token_a_account,
5492                b_to_a_amount,
5493                minimum_a_amount,
5494            )
5495            .unwrap();
5496
5497        let results = swap_curve
5498            .swap(
5499                b_to_a_amount.try_into().unwrap(),
5500                token_b_amount.try_into().unwrap(),
5501                token_a_amount.try_into().unwrap(),
5502                TradeDirection::BtoA,
5503                &fees,
5504            )
5505            .unwrap();
5506
5507        let swap_token_a =
5508            gpl_token::state::Account::unpack(&accounts.token_a_account.data).unwrap();
5509        let token_a_amount = swap_token_a.amount;
5510        assert_eq!(
5511            token_a_amount,
5512            TryInto::<u64>::try_into(results.new_swap_destination_amount).unwrap()
5513        );
5514        let token_a = gpl_token::state::Account::unpack(&token_a_account.data).unwrap();
5515        assert_eq!(
5516            token_a.amount,
5517            initial_a - a_to_b_amount + to_u64(results.destination_amount_swapped).unwrap()
5518        );
5519
5520        let swap_token_b =
5521            gpl_token::state::Account::unpack(&accounts.token_b_account.data).unwrap();
5522        let token_b_amount = swap_token_b.amount;
5523        assert_eq!(
5524            token_b_amount,
5525            TryInto::<u64>::try_into(results.new_swap_source_amount).unwrap()
5526        );
5527        let token_b = gpl_token::state::Account::unpack(&token_b_account.data).unwrap();
5528        assert_eq!(
5529            token_b.amount,
5530            initial_b + to_u64(first_swap_amount).unwrap()
5531                - to_u64(results.source_amount_swapped).unwrap()
5532        );
5533
5534        let second_fee = swap_curve
5535            .withdraw_single_token_type_exact_out(
5536                results.owner_fee,
5537                token_a_amount.try_into().unwrap(),
5538                token_b_amount.try_into().unwrap(),
5539                initial_supply.try_into().unwrap(),
5540                TradeDirection::BtoA,
5541                &fees,
5542            )
5543            .unwrap();
5544        let fee_account =
5545            gpl_token::state::Account::unpack(&accounts.pool_fee_account.data).unwrap();
5546        assert_eq!(fee_account.amount, to_u64(first_fee + second_fee).unwrap());
5547    }
5548
5549    #[test]
5550    fn test_valid_swap_curves_all_fees() {
5551        // All fees
5552        let trade_fee_numerator = 1;
5553        let trade_fee_denominator = 10;
5554        let owner_trade_fee_numerator = 1;
5555        let owner_trade_fee_denominator = 30;
5556        let owner_withdraw_fee_numerator = 1;
5557        let owner_withdraw_fee_denominator = 30;
5558        let host_fee_numerator = 20;
5559        let host_fee_denominator = 100;
5560        let fees = Fees {
5561            trade_fee_numerator,
5562            trade_fee_denominator,
5563            owner_trade_fee_numerator,
5564            owner_trade_fee_denominator,
5565            owner_withdraw_fee_numerator,
5566            owner_withdraw_fee_denominator,
5567            host_fee_numerator,
5568            host_fee_denominator,
5569        };
5570
5571        let token_a_amount = 10_000_000_000;
5572        let token_b_amount = 50_000_000_000;
5573
5574        check_valid_swap_curve(
5575            fees.clone(),
5576            CurveType::ConstantProduct,
5577            Box::new(ConstantProductCurve {}),
5578            token_a_amount,
5579            token_b_amount,
5580        );
5581        let token_b_price = 1;
5582        check_valid_swap_curve(
5583            fees.clone(),
5584            CurveType::ConstantPrice,
5585            Box::new(ConstantPriceCurve { token_b_price }),
5586            token_a_amount,
5587            token_b_amount,
5588        );
5589        let token_b_offset = 10_000_000_000;
5590        check_valid_swap_curve(
5591            fees,
5592            CurveType::Offset,
5593            Box::new(OffsetCurve { token_b_offset }),
5594            token_a_amount,
5595            token_b_amount,
5596        );
5597    }
5598
5599    #[test]
5600    fn test_valid_swap_curves_trade_fee_only() {
5601        let trade_fee_numerator = 1;
5602        let trade_fee_denominator = 10;
5603        let owner_trade_fee_numerator = 0;
5604        let owner_trade_fee_denominator = 0;
5605        let owner_withdraw_fee_numerator = 0;
5606        let owner_withdraw_fee_denominator = 0;
5607        let host_fee_numerator = 0;
5608        let host_fee_denominator = 0;
5609        let fees = Fees {
5610            trade_fee_numerator,
5611            trade_fee_denominator,
5612            owner_trade_fee_numerator,
5613            owner_trade_fee_denominator,
5614            owner_withdraw_fee_numerator,
5615            owner_withdraw_fee_denominator,
5616            host_fee_numerator,
5617            host_fee_denominator,
5618        };
5619
5620        let token_a_amount = 10_000_000_000;
5621        let token_b_amount = 50_000_000_000;
5622
5623        check_valid_swap_curve(
5624            fees.clone(),
5625            CurveType::ConstantProduct,
5626            Box::new(ConstantProductCurve {}),
5627            token_a_amount,
5628            token_b_amount,
5629        );
5630        let token_b_price = 10_000;
5631        check_valid_swap_curve(
5632            fees.clone(),
5633            CurveType::ConstantPrice,
5634            Box::new(ConstantPriceCurve { token_b_price }),
5635            token_a_amount,
5636            token_b_amount / token_b_price,
5637        );
5638        let token_b_offset = 1;
5639        check_valid_swap_curve(
5640            fees,
5641            CurveType::Offset,
5642            Box::new(OffsetCurve { token_b_offset }),
5643            token_a_amount,
5644            token_b_amount,
5645        );
5646    }
5647
5648    #[test]
5649    fn test_valid_swap_with_fee_constraints() {
5650        let owner_key = Pubkey::new_unique();
5651
5652        let trade_fee_numerator = 1;
5653        let trade_fee_denominator = 10;
5654        let owner_trade_fee_numerator = 1;
5655        let owner_trade_fee_denominator = 30;
5656        let owner_withdraw_fee_numerator = 1;
5657        let owner_withdraw_fee_denominator = 30;
5658        let host_fee_numerator = 10;
5659        let host_fee_denominator = 100;
5660
5661        let token_a_amount = 1_000_000;
5662        let token_b_amount = 5_000_000;
5663
5664        let fees = Fees {
5665            trade_fee_numerator,
5666            trade_fee_denominator,
5667            owner_trade_fee_numerator,
5668            owner_trade_fee_denominator,
5669            owner_withdraw_fee_numerator,
5670            owner_withdraw_fee_denominator,
5671            host_fee_numerator,
5672            host_fee_denominator,
5673        };
5674
5675        let curve = ConstantProductCurve {};
5676        let swap_curve = SwapCurve {
5677            curve_type: CurveType::ConstantProduct,
5678            calculator: Box::new(curve),
5679        };
5680
5681        let owner_key_str = &owner_key.to_string();
5682        let valid_curve_types = &[CurveType::ConstantProduct];
5683        let constraints = Some(SwapConstraints {
5684            owner_key: owner_key_str,
5685            valid_curve_types,
5686            fees: &fees,
5687        });
5688        let mut accounts = SwapAccountInfo::new(
5689            &owner_key,
5690            fees.clone(),
5691            swap_curve,
5692            token_a_amount,
5693            token_b_amount,
5694        );
5695
5696        // initialize swap
5697        do_process_instruction_with_fee_constraints(
5698            initialize(
5699                &SWAP_PROGRAM_ID,
5700                &gpl_token::id(),
5701                &accounts.swap_key,
5702                &accounts.authority_key,
5703                &accounts.token_a_key,
5704                &accounts.token_b_key,
5705                &accounts.pool_mint_key,
5706                &accounts.pool_fee_key,
5707                &accounts.pool_token_key,
5708                accounts.fees.clone(),
5709                accounts.swap_curve.clone(),
5710            )
5711            .unwrap(),
5712            vec![
5713                &mut accounts.swap_account,
5714                &mut Account::default(),
5715                &mut accounts.token_a_account,
5716                &mut accounts.token_b_account,
5717                &mut accounts.pool_mint_account,
5718                &mut accounts.pool_fee_account,
5719                &mut accounts.pool_token_account,
5720                &mut Account::default(),
5721            ],
5722            &constraints,
5723        )
5724        .unwrap();
5725
5726        let authority_key = accounts.authority_key;
5727
5728        let (
5729            token_a_key,
5730            mut token_a_account,
5731            token_b_key,
5732            mut token_b_account,
5733            pool_key,
5734            mut pool_account,
5735        ) = accounts.setup_token_accounts(
5736            &owner_key,
5737            &authority_key,
5738            token_a_amount,
5739            token_b_amount,
5740            0,
5741        );
5742
5743        let amount_in = token_a_amount / 2;
5744        let minimum_amount_out = 0;
5745
5746        // perform the swap
5747        do_process_instruction_with_fee_constraints(
5748            swap(
5749                &SWAP_PROGRAM_ID,
5750                &gpl_token::id(),
5751                &accounts.swap_key,
5752                &accounts.authority_key,
5753                &accounts.authority_key,
5754                &token_a_key,
5755                &accounts.token_a_key,
5756                &accounts.token_b_key,
5757                &token_b_key,
5758                &accounts.pool_mint_key,
5759                &accounts.pool_fee_key,
5760                Some(&pool_key),
5761                Swap {
5762                    amount_in,
5763                    minimum_amount_out,
5764                },
5765            )
5766            .unwrap(),
5767            vec![
5768                &mut accounts.swap_account,
5769                &mut Account::default(),
5770                &mut Account::default(),
5771                &mut token_a_account,
5772                &mut accounts.token_a_account,
5773                &mut accounts.token_b_account,
5774                &mut token_b_account,
5775                &mut accounts.pool_mint_account,
5776                &mut accounts.pool_fee_account,
5777                &mut Account::default(),
5778                &mut pool_account,
5779            ],
5780            &constraints,
5781        )
5782        .unwrap();
5783
5784        // check that fees were taken in the host fee account
5785        let host_fee_account = gpl_token::state::Account::unpack(&pool_account.data).unwrap();
5786        let owner_fee_account =
5787            gpl_token::state::Account::unpack(&accounts.pool_fee_account.data).unwrap();
5788        let total_fee = owner_fee_account.amount * host_fee_denominator
5789            / (host_fee_denominator - host_fee_numerator);
5790        assert_eq!(
5791            total_fee,
5792            host_fee_account.amount + owner_fee_account.amount
5793        );
5794    }
5795
5796    #[test]
5797    fn test_invalid_swap() {
5798        let user_key = Pubkey::new_unique();
5799        let swapper_key = Pubkey::new_unique();
5800        let trade_fee_numerator = 1;
5801        let trade_fee_denominator = 4;
5802        let owner_trade_fee_numerator = 1;
5803        let owner_trade_fee_denominator = 10;
5804        let owner_withdraw_fee_numerator = 1;
5805        let owner_withdraw_fee_denominator = 5;
5806        let host_fee_numerator = 9;
5807        let host_fee_denominator = 100;
5808        let fees = Fees {
5809            trade_fee_numerator,
5810            trade_fee_denominator,
5811            owner_trade_fee_numerator,
5812            owner_trade_fee_denominator,
5813            owner_withdraw_fee_numerator,
5814            owner_withdraw_fee_denominator,
5815            host_fee_numerator,
5816            host_fee_denominator,
5817        };
5818
5819        let token_a_amount = 1000;
5820        let token_b_amount = 5000;
5821        let curve_type = CurveType::ConstantProduct;
5822        let swap_curve = SwapCurve {
5823            curve_type,
5824            calculator: Box::new(ConstantProductCurve {}),
5825        };
5826        let mut accounts =
5827            SwapAccountInfo::new(&user_key, fees, swap_curve, token_a_amount, token_b_amount);
5828
5829        let initial_a = token_a_amount / 5;
5830        let initial_b = token_b_amount / 5;
5831        let minimum_token_b_amount = initial_b / 2;
5832
5833        let swap_token_a_key = accounts.token_a_key;
5834        let swap_token_b_key = accounts.token_b_key;
5835
5836        // swap not initialized
5837        {
5838            let (
5839                token_a_key,
5840                mut token_a_account,
5841                token_b_key,
5842                mut token_b_account,
5843                _pool_key,
5844                _pool_account,
5845            ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
5846            assert_eq!(
5847                Err(ProgramError::UninitializedAccount),
5848                accounts.swap(
5849                    &swapper_key,
5850                    &token_a_key,
5851                    &mut token_a_account,
5852                    &swap_token_a_key,
5853                    &swap_token_b_key,
5854                    &token_b_key,
5855                    &mut token_b_account,
5856                    initial_a,
5857                    minimum_token_b_amount,
5858                )
5859            );
5860        }
5861
5862        accounts.initialize_swap().unwrap();
5863
5864        // wrong swap account program id
5865        {
5866            let (
5867                token_a_key,
5868                mut token_a_account,
5869                token_b_key,
5870                mut token_b_account,
5871                _pool_key,
5872                _pool_account,
5873            ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
5874            let old_swap_account = accounts.swap_account;
5875            let mut wrong_swap_account = old_swap_account.clone();
5876            wrong_swap_account.owner = gpl_token::id();
5877            accounts.swap_account = wrong_swap_account;
5878            assert_eq!(
5879                Err(ProgramError::IncorrectProgramId),
5880                accounts.swap(
5881                    &swapper_key,
5882                    &token_a_key,
5883                    &mut token_a_account,
5884                    &swap_token_a_key,
5885                    &swap_token_b_key,
5886                    &token_b_key,
5887                    &mut token_b_account,
5888                    initial_a,
5889                    minimum_token_b_amount,
5890                )
5891            );
5892            accounts.swap_account = old_swap_account;
5893        }
5894
5895        // wrong bump seed
5896        {
5897            let (
5898                token_a_key,
5899                mut token_a_account,
5900                token_b_key,
5901                mut token_b_account,
5902                _pool_key,
5903                _pool_account,
5904            ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
5905            let old_authority = accounts.authority_key;
5906            let (bad_authority_key, _bump_seed) = Pubkey::find_program_address(
5907                &[&accounts.swap_key.to_bytes()[..]],
5908                &gpl_token::id(),
5909            );
5910            accounts.authority_key = bad_authority_key;
5911            assert_eq!(
5912                Err(SwapError::InvalidProgramAddress.into()),
5913                accounts.swap(
5914                    &swapper_key,
5915                    &token_a_key,
5916                    &mut token_a_account,
5917                    &swap_token_a_key,
5918                    &swap_token_b_key,
5919                    &token_b_key,
5920                    &mut token_b_account,
5921                    initial_a,
5922                    minimum_token_b_amount,
5923                )
5924            );
5925            accounts.authority_key = old_authority;
5926        }
5927
5928        // wrong token program id
5929        {
5930            let (
5931                token_a_key,
5932                mut token_a_account,
5933                token_b_key,
5934                mut token_b_account,
5935                _pool_key,
5936                _pool_account,
5937            ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
5938            let wrong_program_id = Pubkey::new_unique();
5939            assert_eq!(
5940                Err(SwapError::IncorrectTokenProgramId.into()),
5941                do_process_instruction(
5942                    swap(
5943                        &SWAP_PROGRAM_ID,
5944                        &wrong_program_id,
5945                        &accounts.swap_key,
5946                        &accounts.authority_key,
5947                        &accounts.authority_key,
5948                        &token_a_key,
5949                        &accounts.token_a_key,
5950                        &accounts.token_b_key,
5951                        &token_b_key,
5952                        &accounts.pool_mint_key,
5953                        &accounts.pool_fee_key,
5954                        None,
5955                        Swap {
5956                            amount_in: initial_a,
5957                            minimum_amount_out: minimum_token_b_amount,
5958                        },
5959                    )
5960                    .unwrap(),
5961                    vec![
5962                        &mut accounts.swap_account,
5963                        &mut Account::default(),
5964                        &mut Account::default(),
5965                        &mut token_a_account,
5966                        &mut accounts.token_a_account,
5967                        &mut accounts.token_b_account,
5968                        &mut token_b_account,
5969                        &mut accounts.pool_mint_account,
5970                        &mut accounts.pool_fee_account,
5971                        &mut Account::default(),
5972                    ],
5973                ),
5974            );
5975        }
5976
5977        // not enough token a to swap
5978        {
5979            let (
5980                token_a_key,
5981                mut token_a_account,
5982                token_b_key,
5983                mut token_b_account,
5984                _pool_key,
5985                _pool_account,
5986            ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
5987            assert_eq!(
5988                Err(TokenError::InsufficientFunds.into()),
5989                accounts.swap(
5990                    &swapper_key,
5991                    &token_a_key,
5992                    &mut token_a_account,
5993                    &swap_token_a_key,
5994                    &swap_token_b_key,
5995                    &token_b_key,
5996                    &mut token_b_account,
5997                    initial_a * 2,
5998                    minimum_token_b_amount * 2,
5999                )
6000            );
6001        }
6002
6003        // wrong swap token A / B accounts
6004        {
6005            let (
6006                token_a_key,
6007                mut token_a_account,
6008                token_b_key,
6009                mut token_b_account,
6010                _pool_key,
6011                _pool_account,
6012            ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
6013            let user_transfer_key = Pubkey::new_unique();
6014            assert_eq!(
6015                Err(SwapError::IncorrectSwapAccount.into()),
6016                do_process_instruction(
6017                    swap(
6018                        &SWAP_PROGRAM_ID,
6019                        &gpl_token::id(),
6020                        &accounts.swap_key,
6021                        &accounts.authority_key,
6022                        &user_transfer_key,
6023                        &token_a_key,
6024                        &token_a_key,
6025                        &token_b_key,
6026                        &token_b_key,
6027                        &accounts.pool_mint_key,
6028                        &accounts.pool_fee_key,
6029                        None,
6030                        Swap {
6031                            amount_in: initial_a,
6032                            minimum_amount_out: minimum_token_b_amount,
6033                        },
6034                    )
6035                    .unwrap(),
6036                    vec![
6037                        &mut accounts.swap_account,
6038                        &mut Account::default(),
6039                        &mut Account::default(),
6040                        &mut token_a_account.clone(),
6041                        &mut token_a_account,
6042                        &mut token_b_account.clone(),
6043                        &mut token_b_account,
6044                        &mut accounts.pool_mint_account,
6045                        &mut accounts.pool_fee_account,
6046                        &mut Account::default(),
6047                    ],
6048                ),
6049            );
6050        }
6051
6052        // wrong user token A / B accounts
6053        {
6054            let (
6055                token_a_key,
6056                mut token_a_account,
6057                token_b_key,
6058                mut token_b_account,
6059                _pool_key,
6060                _pool_account,
6061            ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
6062            assert_eq!(
6063                Err(TokenError::MintMismatch.into()),
6064                accounts.swap(
6065                    &swapper_key,
6066                    &token_b_key,
6067                    &mut token_b_account,
6068                    &swap_token_a_key,
6069                    &swap_token_b_key,
6070                    &token_a_key,
6071                    &mut token_a_account,
6072                    initial_a,
6073                    minimum_token_b_amount,
6074                )
6075            );
6076        }
6077
6078        // swap from a to a
6079        {
6080            let (
6081                token_a_key,
6082                mut token_a_account,
6083                _token_b_key,
6084                _token_b_account,
6085                _pool_key,
6086                _pool_account,
6087            ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
6088            assert_eq!(
6089                Err(SwapError::InvalidInput.into()),
6090                accounts.swap(
6091                    &swapper_key,
6092                    &token_a_key,
6093                    &mut token_a_account.clone(),
6094                    &swap_token_a_key,
6095                    &swap_token_a_key,
6096                    &token_a_key,
6097                    &mut token_a_account,
6098                    initial_a,
6099                    minimum_token_b_amount,
6100                )
6101            );
6102        }
6103
6104        // incorrect mint provided
6105        {
6106            let (
6107                token_a_key,
6108                mut token_a_account,
6109                token_b_key,
6110                mut token_b_account,
6111                _pool_key,
6112                _pool_account,
6113            ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
6114            let (pool_mint_key, pool_mint_account) =
6115                create_mint(&gpl_token::id(), &accounts.authority_key, None);
6116            let old_pool_key = accounts.pool_mint_key;
6117            let old_pool_account = accounts.pool_mint_account;
6118            accounts.pool_mint_key = pool_mint_key;
6119            accounts.pool_mint_account = pool_mint_account;
6120
6121            assert_eq!(
6122                Err(SwapError::IncorrectPoolMint.into()),
6123                accounts.swap(
6124                    &swapper_key,
6125                    &token_a_key,
6126                    &mut token_a_account,
6127                    &swap_token_a_key,
6128                    &swap_token_b_key,
6129                    &token_b_key,
6130                    &mut token_b_account,
6131                    initial_a,
6132                    minimum_token_b_amount,
6133                )
6134            );
6135
6136            accounts.pool_mint_key = old_pool_key;
6137            accounts.pool_mint_account = old_pool_account;
6138        }
6139
6140        // incorrect fee account provided
6141        {
6142            let (
6143                token_a_key,
6144                mut token_a_account,
6145                token_b_key,
6146                mut token_b_account,
6147                wrong_pool_key,
6148                wrong_pool_account,
6149            ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
6150            let old_pool_fee_account = accounts.pool_fee_account;
6151            let old_pool_fee_key = accounts.pool_fee_key;
6152            accounts.pool_fee_account = wrong_pool_account;
6153            accounts.pool_fee_key = wrong_pool_key;
6154            assert_eq!(
6155                Err(SwapError::IncorrectFeeAccount.into()),
6156                accounts.swap(
6157                    &swapper_key,
6158                    &token_a_key,
6159                    &mut token_a_account,
6160                    &swap_token_a_key,
6161                    &swap_token_b_key,
6162                    &token_b_key,
6163                    &mut token_b_account,
6164                    initial_a,
6165                    minimum_token_b_amount,
6166                )
6167            );
6168            accounts.pool_fee_account = old_pool_fee_account;
6169            accounts.pool_fee_key = old_pool_fee_key;
6170        }
6171
6172        // no approval
6173        {
6174            let (
6175                token_a_key,
6176                mut token_a_account,
6177                token_b_key,
6178                mut token_b_account,
6179                _pool_key,
6180                _pool_account,
6181            ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
6182            let user_transfer_key = Pubkey::new_unique();
6183            assert_eq!(
6184                Err(TokenError::OwnerMismatch.into()),
6185                do_process_instruction(
6186                    swap(
6187                        &SWAP_PROGRAM_ID,
6188                        &gpl_token::id(),
6189                        &accounts.swap_key,
6190                        &accounts.authority_key,
6191                        &user_transfer_key,
6192                        &token_a_key,
6193                        &accounts.token_a_key,
6194                        &accounts.token_b_key,
6195                        &token_b_key,
6196                        &accounts.pool_mint_key,
6197                        &accounts.pool_fee_key,
6198                        None,
6199                        Swap {
6200                            amount_in: initial_a,
6201                            minimum_amount_out: minimum_token_b_amount,
6202                        },
6203                    )
6204                    .unwrap(),
6205                    vec![
6206                        &mut accounts.swap_account,
6207                        &mut Account::default(),
6208                        &mut Account::default(),
6209                        &mut token_a_account,
6210                        &mut accounts.token_a_account,
6211                        &mut accounts.token_b_account,
6212                        &mut token_b_account,
6213                        &mut accounts.pool_mint_account,
6214                        &mut accounts.pool_fee_account,
6215                        &mut Account::default(),
6216                    ],
6217                ),
6218            );
6219        }
6220
6221        // output token value 0
6222        {
6223            let (
6224                token_a_key,
6225                mut token_a_account,
6226                token_b_key,
6227                mut token_b_account,
6228                _pool_key,
6229                _pool_account,
6230            ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
6231            assert_eq!(
6232                Err(SwapError::ZeroTradingTokens.into()),
6233                accounts.swap(
6234                    &swapper_key,
6235                    &token_b_key,
6236                    &mut token_b_account,
6237                    &swap_token_b_key,
6238                    &swap_token_a_key,
6239                    &token_a_key,
6240                    &mut token_a_account,
6241                    1,
6242                    1,
6243                )
6244            );
6245        }
6246
6247        // slippage exceeded: minimum out amount too high
6248        {
6249            let (
6250                token_a_key,
6251                mut token_a_account,
6252                token_b_key,
6253                mut token_b_account,
6254                _pool_key,
6255                _pool_account,
6256            ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
6257            assert_eq!(
6258                Err(SwapError::ExceededSlippage.into()),
6259                accounts.swap(
6260                    &swapper_key,
6261                    &token_a_key,
6262                    &mut token_a_account,
6263                    &swap_token_a_key,
6264                    &swap_token_b_key,
6265                    &token_b_key,
6266                    &mut token_b_account,
6267                    initial_a,
6268                    minimum_token_b_amount * 2,
6269                )
6270            );
6271        }
6272
6273        // invalid input: can't use swap pool as user source / dest
6274        {
6275            let (
6276                token_a_key,
6277                mut token_a_account,
6278                token_b_key,
6279                mut token_b_account,
6280                _pool_key,
6281                _pool_account,
6282            ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
6283            let mut swap_token_a_account = accounts.get_token_account(&swap_token_a_key).clone();
6284            let authority_key = accounts.authority_key;
6285            assert_eq!(
6286                Err(SwapError::InvalidInput.into()),
6287                accounts.swap(
6288                    &authority_key,
6289                    &swap_token_a_key,
6290                    &mut swap_token_a_account,
6291                    &swap_token_a_key,
6292                    &swap_token_b_key,
6293                    &token_b_key,
6294                    &mut token_b_account,
6295                    initial_a,
6296                    minimum_token_b_amount,
6297                )
6298            );
6299            let mut swap_token_b_account = accounts.get_token_account(&swap_token_b_key).clone();
6300            assert_eq!(
6301                Err(SwapError::InvalidInput.into()),
6302                accounts.swap(
6303                    &swapper_key,
6304                    &token_a_key,
6305                    &mut token_a_account,
6306                    &swap_token_a_key,
6307                    &swap_token_b_key,
6308                    &swap_token_b_key,
6309                    &mut swap_token_b_account,
6310                    initial_a,
6311                    minimum_token_b_amount,
6312                )
6313            );
6314        }
6315
6316        // still correct: constraint specified, no host fee account
6317        {
6318            let authority_key = accounts.authority_key;
6319            let (
6320                token_a_key,
6321                mut token_a_account,
6322                token_b_key,
6323                mut token_b_account,
6324                _pool_key,
6325                _pool_account,
6326            ) = accounts.setup_token_accounts(&user_key, &authority_key, initial_a, initial_b, 0);
6327            let owner_key = &swapper_key.to_string();
6328            let fees = Fees {
6329                trade_fee_numerator,
6330                trade_fee_denominator,
6331                owner_trade_fee_numerator,
6332                owner_trade_fee_denominator,
6333                owner_withdraw_fee_numerator,
6334                owner_withdraw_fee_denominator,
6335                host_fee_numerator,
6336                host_fee_denominator,
6337            };
6338            let constraints = Some(SwapConstraints {
6339                owner_key,
6340                valid_curve_types: &[],
6341                fees: &fees,
6342            });
6343            do_process_instruction_with_fee_constraints(
6344                swap(
6345                    &SWAP_PROGRAM_ID,
6346                    &gpl_token::id(),
6347                    &accounts.swap_key,
6348                    &accounts.authority_key,
6349                    &accounts.authority_key,
6350                    &token_a_key,
6351                    &accounts.token_a_key,
6352                    &accounts.token_b_key,
6353                    &token_b_key,
6354                    &accounts.pool_mint_key,
6355                    &accounts.pool_fee_key,
6356                    None,
6357                    Swap {
6358                        amount_in: initial_a,
6359                        minimum_amount_out: minimum_token_b_amount,
6360                    },
6361                )
6362                .unwrap(),
6363                vec![
6364                    &mut accounts.swap_account,
6365                    &mut Account::default(),
6366                    &mut Account::default(),
6367                    &mut token_a_account,
6368                    &mut accounts.token_a_account,
6369                    &mut accounts.token_b_account,
6370                    &mut token_b_account,
6371                    &mut accounts.pool_mint_account,
6372                    &mut accounts.pool_fee_account,
6373                    &mut Account::default(),
6374                ],
6375                &constraints,
6376            )
6377            .unwrap();
6378        }
6379
6380        // invalid mint for host fee account
6381        {
6382            let authority_key = accounts.authority_key;
6383            let (
6384                token_a_key,
6385                mut token_a_account,
6386                token_b_key,
6387                mut token_b_account,
6388                _pool_key,
6389                _pool_account,
6390            ) = accounts.setup_token_accounts(&user_key, &authority_key, initial_a, initial_b, 0);
6391            let (
6392                bad_token_a_key,
6393                mut bad_token_a_account,
6394                _token_b_key,
6395                mut _token_b_account,
6396                _pool_key,
6397                _pool_account,
6398            ) = accounts.setup_token_accounts(&user_key, &authority_key, initial_a, initial_b, 0);
6399            let owner_key = &swapper_key.to_string();
6400            let fees = Fees {
6401                trade_fee_numerator,
6402                trade_fee_denominator,
6403                owner_trade_fee_numerator,
6404                owner_trade_fee_denominator,
6405                owner_withdraw_fee_numerator,
6406                owner_withdraw_fee_denominator,
6407                host_fee_numerator,
6408                host_fee_denominator,
6409            };
6410            let constraints = Some(SwapConstraints {
6411                owner_key,
6412                valid_curve_types: &[],
6413                fees: &fees,
6414            });
6415            assert_eq!(
6416                Err(SwapError::IncorrectPoolMint.into()),
6417                do_process_instruction_with_fee_constraints(
6418                    swap(
6419                        &SWAP_PROGRAM_ID,
6420                        &gpl_token::id(),
6421                        &accounts.swap_key,
6422                        &accounts.authority_key,
6423                        &accounts.authority_key,
6424                        &token_a_key,
6425                        &accounts.token_a_key,
6426                        &accounts.token_b_key,
6427                        &token_b_key,
6428                        &accounts.pool_mint_key,
6429                        &accounts.pool_fee_key,
6430                        Some(&bad_token_a_key),
6431                        Swap {
6432                            amount_in: initial_a,
6433                            minimum_amount_out: 0,
6434                        },
6435                    )
6436                    .unwrap(),
6437                    vec![
6438                        &mut accounts.swap_account,
6439                        &mut Account::default(),
6440                        &mut Account::default(),
6441                        &mut token_a_account,
6442                        &mut accounts.token_a_account,
6443                        &mut accounts.token_b_account,
6444                        &mut token_b_account,
6445                        &mut accounts.pool_mint_account,
6446                        &mut accounts.pool_fee_account,
6447                        &mut Account::default(),
6448                        &mut bad_token_a_account,
6449                    ],
6450                    &constraints,
6451                ),
6452            );
6453        }
6454    }
6455
6456    #[test]
6457    fn test_overdraw_offset_curve() {
6458        let trade_fee_numerator = 1;
6459        let trade_fee_denominator = 10;
6460        let owner_trade_fee_numerator = 1;
6461        let owner_trade_fee_denominator = 30;
6462        let owner_withdraw_fee_numerator = 1;
6463        let owner_withdraw_fee_denominator = 30;
6464        let host_fee_numerator = 10;
6465        let host_fee_denominator = 100;
6466
6467        let token_a_amount = 1_000_000_000;
6468        let token_b_amount = 0;
6469        let fees = Fees {
6470            trade_fee_numerator,
6471            trade_fee_denominator,
6472            owner_trade_fee_numerator,
6473            owner_trade_fee_denominator,
6474            owner_withdraw_fee_numerator,
6475            owner_withdraw_fee_denominator,
6476            host_fee_numerator,
6477            host_fee_denominator,
6478        };
6479
6480        let token_b_offset = 2_000_000;
6481        let swap_curve = SwapCurve {
6482            curve_type: CurveType::Offset,
6483            calculator: Box::new(OffsetCurve { token_b_offset }),
6484        };
6485        let user_key = Pubkey::new_unique();
6486        let swapper_key = Pubkey::new_unique();
6487
6488        let mut accounts =
6489            SwapAccountInfo::new(&user_key, fees, swap_curve, token_a_amount, token_b_amount);
6490
6491        accounts.initialize_swap().unwrap();
6492
6493        let swap_token_a_key = accounts.token_a_key;
6494        let swap_token_b_key = accounts.token_b_key;
6495        let initial_a = 500_000;
6496        let initial_b = 1_000;
6497
6498        let (
6499            token_a_key,
6500            mut token_a_account,
6501            token_b_key,
6502            mut token_b_account,
6503            _pool_key,
6504            _pool_account,
6505        ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
6506
6507        // swap a to b way, fails, there's no liquidity
6508        let a_to_b_amount = initial_a;
6509        let minimum_token_b_amount = 0;
6510
6511        assert_eq!(
6512            Err(SwapError::ZeroTradingTokens.into()),
6513            accounts.swap(
6514                &swapper_key,
6515                &token_a_key,
6516                &mut token_a_account,
6517                &swap_token_a_key,
6518                &swap_token_b_key,
6519                &token_b_key,
6520                &mut token_b_account,
6521                a_to_b_amount,
6522                minimum_token_b_amount,
6523            )
6524        );
6525
6526        // swap b to a, succeeds at offset price
6527        let b_to_a_amount = initial_b;
6528        let minimum_token_a_amount = 0;
6529        accounts
6530            .swap(
6531                &swapper_key,
6532                &token_b_key,
6533                &mut token_b_account,
6534                &swap_token_b_key,
6535                &swap_token_a_key,
6536                &token_a_key,
6537                &mut token_a_account,
6538                b_to_a_amount,
6539                minimum_token_a_amount,
6540            )
6541            .unwrap();
6542
6543        // try a to b again, succeeds due to new liquidity
6544        accounts
6545            .swap(
6546                &swapper_key,
6547                &token_a_key,
6548                &mut token_a_account,
6549                &swap_token_a_key,
6550                &swap_token_b_key,
6551                &token_b_key,
6552                &mut token_b_account,
6553                a_to_b_amount,
6554                minimum_token_b_amount,
6555            )
6556            .unwrap();
6557
6558        // try a to b again, fails due to no more liquidity
6559        assert_eq!(
6560            Err(SwapError::ZeroTradingTokens.into()),
6561            accounts.swap(
6562                &swapper_key,
6563                &token_a_key,
6564                &mut token_a_account,
6565                &swap_token_a_key,
6566                &swap_token_b_key,
6567                &token_b_key,
6568                &mut token_b_account,
6569                a_to_b_amount,
6570                minimum_token_b_amount,
6571            )
6572        );
6573
6574        // Try to deposit, fails because deposits are not allowed for offset
6575        // curve swaps
6576        {
6577            let initial_a = 100;
6578            let initial_b = 100;
6579            let pool_amount = 100;
6580            let (
6581                token_a_key,
6582                mut token_a_account,
6583                token_b_key,
6584                mut token_b_account,
6585                pool_key,
6586                mut pool_account,
6587            ) = accounts.setup_token_accounts(&user_key, &swapper_key, initial_a, initial_b, 0);
6588            assert_eq!(
6589                Err(SwapError::UnsupportedCurveOperation.into()),
6590                accounts.deposit_all_token_types(
6591                    &swapper_key,
6592                    &token_a_key,
6593                    &mut token_a_account,
6594                    &token_b_key,
6595                    &mut token_b_account,
6596                    &pool_key,
6597                    &mut pool_account,
6598                    pool_amount,
6599                    initial_a,
6600                    initial_b,
6601                )
6602            );
6603        }
6604    }
6605
6606    #[test]
6607    fn test_withdraw_all_offset_curve() {
6608        let trade_fee_numerator = 1;
6609        let trade_fee_denominator = 10;
6610        let owner_trade_fee_numerator = 1;
6611        let owner_trade_fee_denominator = 30;
6612        let owner_withdraw_fee_numerator = 0;
6613        let owner_withdraw_fee_denominator = 30;
6614        let host_fee_numerator = 10;
6615        let host_fee_denominator = 100;
6616
6617        let token_a_amount = 1_000_000_000;
6618        let token_b_amount = 10;
6619        let fees = Fees {
6620            trade_fee_numerator,
6621            trade_fee_denominator,
6622            owner_trade_fee_numerator,
6623            owner_trade_fee_denominator,
6624            owner_withdraw_fee_numerator,
6625            owner_withdraw_fee_denominator,
6626            host_fee_numerator,
6627            host_fee_denominator,
6628        };
6629
6630        let token_b_offset = 2_000_000;
6631        let swap_curve = SwapCurve {
6632            curve_type: CurveType::Offset,
6633            calculator: Box::new(OffsetCurve { token_b_offset }),
6634        };
6635        let total_pool = swap_curve.calculator.new_pool_supply();
6636        let user_key = Pubkey::new_unique();
6637        let withdrawer_key = Pubkey::new_unique();
6638
6639        let mut accounts =
6640            SwapAccountInfo::new(&user_key, fees, swap_curve, token_a_amount, token_b_amount);
6641
6642        accounts.initialize_swap().unwrap();
6643
6644        let (
6645            token_a_key,
6646            mut token_a_account,
6647            token_b_key,
6648            mut token_b_account,
6649            _pool_key,
6650            _pool_account,
6651        ) = accounts.setup_token_accounts(&user_key, &withdrawer_key, 0, 0, 0);
6652
6653        let pool_key = accounts.pool_token_key;
6654        let mut pool_account = accounts.pool_token_account.clone();
6655
6656        // WithdrawAllTokenTypes takes all tokens for A and B.
6657        // The curve's calculation for token B will say to transfer
6658        // `token_b_offset + token_b_amount`, but only `token_b_amount` will be
6659        // moved.
6660        accounts
6661            .withdraw_all_token_types(
6662                &user_key,
6663                &pool_key,
6664                &mut pool_account,
6665                &token_a_key,
6666                &mut token_a_account,
6667                &token_b_key,
6668                &mut token_b_account,
6669                total_pool.try_into().unwrap(),
6670                0,
6671                0,
6672            )
6673            .unwrap();
6674
6675        let token_a = gpl_token::state::Account::unpack(&token_a_account.data).unwrap();
6676        assert_eq!(token_a.amount, token_a_amount);
6677        let token_b = gpl_token::state::Account::unpack(&token_b_account.data).unwrap();
6678        assert_eq!(token_b.amount, token_b_amount);
6679        let swap_token_a =
6680            gpl_token::state::Account::unpack(&accounts.token_a_account.data).unwrap();
6681        assert_eq!(swap_token_a.amount, 0);
6682        let swap_token_b =
6683            gpl_token::state::Account::unpack(&accounts.token_b_account.data).unwrap();
6684        assert_eq!(swap_token_b.amount, 0);
6685    }
6686
6687    #[test]
6688    fn test_withdraw_all_constant_price_curve() {
6689        let trade_fee_numerator = 1;
6690        let trade_fee_denominator = 10;
6691        let owner_trade_fee_numerator = 1;
6692        let owner_trade_fee_denominator = 30;
6693        let owner_withdraw_fee_numerator = 0;
6694        let owner_withdraw_fee_denominator = 30;
6695        let host_fee_numerator = 10;
6696        let host_fee_denominator = 100;
6697
6698        // initialize "unbalanced", so that withdrawing all will have some issues
6699        // A: 1_000_000_000
6700        // B: 2_000_000_000 (1_000 * 2_000_000)
6701        let swap_token_a_amount = 1_000_000_000;
6702        let swap_token_b_amount = 1_000;
6703        let token_b_price = 2_000_000;
6704        let fees = Fees {
6705            trade_fee_numerator,
6706            trade_fee_denominator,
6707            owner_trade_fee_numerator,
6708            owner_trade_fee_denominator,
6709            owner_withdraw_fee_numerator,
6710            owner_withdraw_fee_denominator,
6711            host_fee_numerator,
6712            host_fee_denominator,
6713        };
6714
6715        let swap_curve = SwapCurve {
6716            curve_type: CurveType::ConstantPrice,
6717            calculator: Box::new(ConstantPriceCurve { token_b_price }),
6718        };
6719        let total_pool = swap_curve.calculator.new_pool_supply();
6720        let user_key = Pubkey::new_unique();
6721        let withdrawer_key = Pubkey::new_unique();
6722
6723        let mut accounts = SwapAccountInfo::new(
6724            &user_key,
6725            fees,
6726            swap_curve,
6727            swap_token_a_amount,
6728            swap_token_b_amount,
6729        );
6730
6731        accounts.initialize_swap().unwrap();
6732
6733        let (
6734            token_a_key,
6735            mut token_a_account,
6736            token_b_key,
6737            mut token_b_account,
6738            _pool_key,
6739            _pool_account,
6740        ) = accounts.setup_token_accounts(&user_key, &withdrawer_key, 0, 0, 0);
6741
6742        let pool_key = accounts.pool_token_key;
6743        let mut pool_account = accounts.pool_token_account.clone();
6744
6745        // WithdrawAllTokenTypes will not take all token A and B, since their
6746        // ratio is unbalanced.  It will try to take 1_500_000_000 worth of
6747        // each token, which means 1_500_000_000 token A, and 750 token B.
6748        // With no slippage, this will leave 250 token B in the pool.
6749        assert_eq!(
6750            Err(SwapError::ExceededSlippage.into()),
6751            accounts.withdraw_all_token_types(
6752                &user_key,
6753                &pool_key,
6754                &mut pool_account,
6755                &token_a_key,
6756                &mut token_a_account,
6757                &token_b_key,
6758                &mut token_b_account,
6759                total_pool.try_into().unwrap(),
6760                swap_token_a_amount,
6761                swap_token_b_amount,
6762            )
6763        );
6764
6765        accounts
6766            .withdraw_all_token_types(
6767                &user_key,
6768                &pool_key,
6769                &mut pool_account,
6770                &token_a_key,
6771                &mut token_a_account,
6772                &token_b_key,
6773                &mut token_b_account,
6774                total_pool.try_into().unwrap(),
6775                0,
6776                0,
6777            )
6778            .unwrap();
6779
6780        let token_a = gpl_token::state::Account::unpack(&token_a_account.data).unwrap();
6781        assert_eq!(token_a.amount, swap_token_a_amount);
6782        let token_b = gpl_token::state::Account::unpack(&token_b_account.data).unwrap();
6783        assert_eq!(token_b.amount, 750);
6784        let swap_token_a =
6785            gpl_token::state::Account::unpack(&accounts.token_a_account.data).unwrap();
6786        assert_eq!(swap_token_a.amount, 0);
6787        let swap_token_b =
6788            gpl_token::state::Account::unpack(&accounts.token_b_account.data).unwrap();
6789        assert_eq!(swap_token_b.amount, 250);
6790
6791        // deposit now, not enough to cover the tokens already in there
6792        let token_b_amount = 10;
6793        let token_a_amount = token_b_amount * token_b_price;
6794        let (
6795            token_a_key,
6796            mut token_a_account,
6797            token_b_key,
6798            mut token_b_account,
6799            pool_key,
6800            mut pool_account,
6801        ) = accounts.setup_token_accounts(
6802            &user_key,
6803            &withdrawer_key,
6804            token_a_amount,
6805            token_b_amount,
6806            0,
6807        );
6808
6809        assert_eq!(
6810            Err(SwapError::ExceededSlippage.into()),
6811            accounts.deposit_all_token_types(
6812                &withdrawer_key,
6813                &token_a_key,
6814                &mut token_a_account,
6815                &token_b_key,
6816                &mut token_b_account,
6817                &pool_key,
6818                &mut pool_account,
6819                1, // doesn't matter
6820                token_a_amount,
6821                token_b_amount,
6822            )
6823        );
6824
6825        // deposit enough tokens, success!
6826        let token_b_amount = 125;
6827        let token_a_amount = token_b_amount * token_b_price;
6828        let (
6829            token_a_key,
6830            mut token_a_account,
6831            token_b_key,
6832            mut token_b_account,
6833            pool_key,
6834            mut pool_account,
6835        ) = accounts.setup_token_accounts(
6836            &user_key,
6837            &withdrawer_key,
6838            token_a_amount,
6839            token_b_amount,
6840            0,
6841        );
6842
6843        accounts
6844            .deposit_all_token_types(
6845                &withdrawer_key,
6846                &token_a_key,
6847                &mut token_a_account,
6848                &token_b_key,
6849                &mut token_b_account,
6850                &pool_key,
6851                &mut pool_account,
6852                1, // doesn't matter
6853                token_a_amount,
6854                token_b_amount,
6855            )
6856            .unwrap();
6857    }
6858}