gfx_swap/contexts/
swap.rs

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        // transfer token_src to the pool
110        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        // transfer token_dst to the user
119        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        // trading fees
127
128        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            // Allow error to fall through
142
143            // transfer some fee to the host
144            // if let Ok(host_fee_account_info) = next_account_info(account_info_iter) {
145            //     let host_fee_account = Self::unpack_token_account(
146            //         host_fee_account_info,
147            //         token_swap.token_program_id(),
148            //     )?;
149            //     if *pool_mint_info.key != host_fee_account.mint {
150            //         return Err(SwapError::IncorrectPoolMint.into());
151            //     }
152            //     let host_fee = token_swap
153            //         .fees()
154            //         .host_fee(pool_token_amount)
155            //         .ok_or(SwapError::FeeCalculationFailure)?;
156            //     if host_fee > 0 {
157            //         pool_token_amount = pool_token_amount
158            //             .checked_sub(host_fee)
159            //             .ok_or(SwapError::FeeCalculationFailure)?;
160            //         Self::token_mint_to(
161            //             swap_info.key,
162            //             token_program_info.clone(),
163            //             pool_mint_info.clone(),
164            //             host_fee_account_info.clone(),
165            //             authority_info.clone(),
166            //             token_swap.nonce(),
167            //             to_u64(host_fee)?,
168            //         )?;
169            //     }
170            // }
171
172            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}