1use crate::curve::TradeDirection;
2use crate::errors::ErrorCode::*;
3use crate::states::{DerivedAccountIdentifier, LPMint, Pool, PoolExt};
4use crate::utils::{self, TupleExt};
5use anchor_lang::prelude::*;
6use anchor_spl::token::{Mint, Token, TokenAccount};
7use fehler::{throw, throws};
8
9#[derive(Accounts)]
10pub struct Swap<'info> {
11 #[account(seeds = [Pool::IDENT, &pool.seed], bump = pool.bump)]
12 pub pool: Account<'info, Pool>,
13
14 #[account(
15 mut,
16 constraint = in_token_vault.owner == pool.key() @ WrongATAOwner,
17 constraint = (pool.token_vault_1, pool.token_vault_2).contains(&in_token_vault.key()) @ TokenNotSupportedByPool,
18 )]
19 pub in_token_vault: Box<Account<'info, TokenAccount>>,
20 #[account(
21 mut,
22 constraint = out_token_vault.owner == pool.key() @ WrongATAOwner,
23 constraint = (pool.token_vault_1, pool.token_vault_2).contains(&out_token_vault.key()) @ TokenNotSupportedByPool,
24 constraint = in_token_vault.mint != out_token_vault.mint @ SameToken,
25 )]
26 pub out_token_vault: Box<Account<'info, TokenAccount>>,
27
28 #[account(
29 mut,
30 seeds = [LPMint::IDENT, &pool.seed],
31 bump = pool.lp_bump,
32 constraint = pool.mint == lp_token_mint.key() @ WrongLPMint,
33 )]
34 pub lp_token_mint: Box<Account<'info, Mint>>,
35
36 #[account(
37 mut,
38 constraint = fee_vault.owner == pool.key() @ WrongFeeVault,
39 constraint = pool.fee_vault == fee_vault.key() @ WrongFeeVault,
40 )]
41 pub fee_vault: Box<Account<'info, TokenAccount>>,
42
43 #[account(
44 mut,
45 constraint = in_token_ata_user.owner == user_wallet.key() @ WrongATAOwner,
46 constraint = (pool.token_mint_1, pool.token_mint_2).contains(&in_token_ata_user.mint) @ TokenNotSupportedByPool,
47 )]
48 pub in_token_ata_user: Box<Account<'info, TokenAccount>>,
49 #[account(
50 mut,
51 constraint = out_token_ata_user.owner == user_wallet.key() @ WrongATAOwner,
52 constraint = (pool.token_mint_1, pool.token_mint_2).contains(&out_token_ata_user.mint) @ TokenNotSupportedByPool,
53 constraint = in_token_ata_user.mint != out_token_ata_user.mint @ SameToken,
54 )]
55 pub out_token_ata_user: Box<Account<'info, TokenAccount>>,
56
57 pub user_wallet: Signer<'info>,
58 pub token_program: Program<'info, Token>,
59}
60
61impl<'info> Swap<'info> {
62 #[throws(ProgramError)]
63 pub fn process(&mut self, amount_in: u64, minimum_amount_out: u64) {
64 let Swap {
65 pool,
66 in_token_vault: in_token_ata_pool,
67 out_token_vault: out_token_ata_pool,
68 lp_token_mint,
69 fee_vault: lp_token_ata_fee,
70 user_wallet,
71 in_token_ata_user,
72 out_token_ata_user,
73 token_program,
74 } = self;
75
76 let trade_direction = if in_token_ata_user.mint == pool.token_mint_1 {
77 TradeDirection::AtoB
78 } else if in_token_ata_user.mint == pool.token_mint_2 {
79 TradeDirection::BtoA
80 } else {
81 throw!(IncorrectSwapAccount);
82 };
83
84 let result = pool
85 .curve
86 .swap(
87 utils::to_u128(amount_in)?,
88 utils::to_u128(in_token_ata_pool.amount)?,
89 utils::to_u128(out_token_ata_pool.amount)?,
90 trade_direction,
91 &pool.fees,
92 )
93 .ok_or(ZeroTradingTokens)?;
94 if result.destination_amount_swapped < utils::to_u128(minimum_amount_out)? {
95 throw!(ExceededSlippage);
96 }
97
98 let (swap_token_a_amount, swap_token_b_amount) = match trade_direction {
99 TradeDirection::AtoB => (
100 result.new_swap_source_amount,
101 result.new_swap_destination_amount,
102 ),
103 TradeDirection::BtoA => (
104 result.new_swap_destination_amount,
105 result.new_swap_source_amount,
106 ),
107 };
108
109 pool.transfer_to_pool(
111 user_wallet,
112 in_token_ata_user,
113 in_token_ata_pool,
114 token_program,
115 utils::to_u64(result.source_amount_swapped)?,
116 )?;
117
118 pool.transfer_to_user(
120 out_token_ata_pool,
121 out_token_ata_user,
122 token_program,
123 utils::to_u64(result.destination_amount_swapped)?,
124 )?;
125
126 let lp_token_amount = pool
129 .curve
130 .withdraw_single_token_type_exact_out(
131 result.owner_fee,
132 swap_token_a_amount,
133 swap_token_b_amount,
134 utils::to_u128(lp_token_mint.supply)?,
135 trade_direction,
136 &pool.fees,
137 )
138 .ok_or(FeeCalculationFailure)?;
139
140 if lp_token_amount > 0 {
141 pool.mint_lp_to(
173 lp_token_mint,
174 lp_token_ata_fee,
175 token_program,
176 utils::to_u64(lp_token_amount)?,
177 )?;
178 }
179 }
180}