express_relay/
swap.rs

1use {
2    crate::{
3        error::ErrorCode,
4        state::{
5            ExpressRelayMetadata,
6            FEE_BPS_TO_PPM,
7            FEE_SPLIT_PRECISION_PPM,
8        },
9        token::check_receiver_and_transfer_token_if_needed,
10        FeeToken,
11        Swap,
12        SwapArgs,
13        SwapV2Args,
14        FEE_SPLIT_PRECISION,
15    },
16    anchor_lang::{
17        accounts::interface_account::InterfaceAccount,
18        error::ErrorCode as AnchorErrorCode,
19        prelude::*,
20    },
21    anchor_spl::token_interface::TokenAccount,
22};
23
24pub struct PostFeeSwapArgs {
25    pub amount_searcher_after_fees: u64,
26    pub amount_user_after_fees:     u64,
27}
28
29
30impl Swap<'_> {
31    pub fn convert_to_v2(&self, args: &SwapArgs) -> SwapV2Args {
32        args.convert_to_v2(self.express_relay_metadata.swap_platform_fee_bps)
33    }
34}
35
36impl<'info> Swap<'info> {
37    fn check_mint_user(&self, fee_token: FeeToken) -> Result<()> {
38        let correct_mint_fee_key = if fee_token == FeeToken::Searcher {
39            self.mint_searcher.key()
40        } else {
41            self.mint_user.key()
42        };
43
44        // Preserve API compatibility by returning the same error as the anchor validator did
45        (self.mint_fee.key() == correct_mint_fee_key)
46            .then_some(())
47            .ok_or(AnchorErrorCode::ConstraintRaw.into())
48    }
49
50    fn check_token_program_fee(&self, fee_token: FeeToken) -> Result<()> {
51        let correct_token_program_fee = if fee_token == FeeToken::Searcher {
52            self.token_program_searcher.key()
53        } else {
54            self.token_program_user.key()
55        };
56
57        // Preserve API compatibility by returning the same error as the anchor validator did
58        (self.token_program_fee.key() == correct_token_program_fee)
59            .then_some(())
60            .ok_or(AnchorErrorCode::ConstraintRaw.into())
61    }
62
63    pub fn check_raw_constraints(&self, fee_token: FeeToken) -> Result<()> {
64        self.check_mint_user(fee_token)?;
65        self.check_token_program_fee(fee_token)?;
66
67        Ok(())
68    }
69
70    pub fn compute_swap_fees<'a>(
71        &'a self,
72        args: &SwapV2Args,
73    ) -> Result<(TransferSwapFeeArgs<'info, 'a>, PostFeeSwapArgs)> {
74        match args.fee_token {
75            FeeToken::Searcher => {
76                let SwapFeesWithRemainingAmount {
77                    fees,
78                    remaining_amount,
79                } = self.express_relay_metadata.compute_swap_fees(
80                    args.referral_fee_ppm,
81                    args.swap_platform_fee_ppm,
82                    args.amount_searcher,
83                )?;
84                Ok((
85                    TransferSwapFeeArgs {
86                        fees,
87                        from: &self.searcher_ta_mint_searcher,
88                        authority: &self.searcher,
89                    },
90                    PostFeeSwapArgs {
91                        amount_searcher_after_fees: remaining_amount,
92                        amount_user_after_fees:     args.amount_user,
93                    },
94                ))
95            }
96            FeeToken::User => {
97                let SwapFeesWithRemainingAmount {
98                    fees,
99                    remaining_amount,
100                } = self.express_relay_metadata.compute_swap_fees(
101                    args.referral_fee_ppm,
102                    args.swap_platform_fee_ppm,
103                    args.amount_user,
104                )?;
105                Ok((
106                    TransferSwapFeeArgs {
107                        fees,
108                        from: &self.user_ata_mint_user,
109                        authority: &self.user,
110                    },
111                    PostFeeSwapArgs {
112                        amount_searcher_after_fees: args.amount_searcher,
113                        amount_user_after_fees:     remaining_amount,
114                    },
115                ))
116            }
117        }
118    }
119
120    pub fn transfer_swap_fees_cpi<'a>(&self, args: &TransferSwapFeeArgs<'info, 'a>) -> Result<()> {
121        self.transfer_swap_fee_cpi(
122            args.fees.router_fee,
123            &self.router_fee_receiver_ta,
124            None,
125            args,
126        )?;
127        self.transfer_swap_fee_cpi(
128            args.fees.relayer_fee,
129            &self.relayer_fee_receiver_ata,
130            Some(&self.express_relay_metadata.fee_receiver_relayer),
131            args,
132        )?;
133        self.transfer_swap_fee_cpi(
134            args.fees.express_relay_fee,
135            &self.express_relay_fee_receiver_ata,
136            Some(self.express_relay_metadata.to_account_info().key),
137            args,
138        )?;
139        Ok(())
140    }
141
142    fn transfer_swap_fee_cpi<'a>(
143        &self,
144        fee: u64,
145        receiver_ta: &UncheckedAccount<'info>,
146        receiver: Option<&Pubkey>,
147        args: &TransferSwapFeeArgs<'info, 'a>,
148    ) -> Result<()> {
149        check_receiver_and_transfer_token_if_needed(
150            args.from,
151            receiver_ta,
152            receiver,
153            &self.token_program_fee,
154            args.authority,
155            &self.mint_fee,
156            fee,
157        )?;
158        Ok(())
159    }
160
161    pub fn check_enough_balances(&self, args: &SwapV2Args) -> Result<()> {
162        require_gte!(
163            self.searcher_ta_mint_searcher.amount,
164            args.amount_searcher,
165            ErrorCode::InsufficientSearcherFunds
166        );
167        require_gte!(
168            self.user_ata_mint_user.amount,
169            args.amount_user,
170            ErrorCode::InsufficientUserFunds
171        );
172        Ok(())
173    }
174}
175
176pub struct TransferSwapFeeArgs<'info, 'a> {
177    pub fees:      SwapFees,
178    pub from:      &'a InterfaceAccount<'info, TokenAccount>,
179    pub authority: &'a Signer<'info>,
180}
181
182pub struct SwapFeesWithRemainingAmount {
183    pub fees:             SwapFees,
184    pub remaining_amount: u64,
185}
186
187pub struct SwapFees {
188    pub router_fee:        u64,
189    pub relayer_fee:       u64,
190    pub express_relay_fee: u64,
191}
192impl ExpressRelayMetadata {
193    pub fn check_relayer_signer(&self, relayer_signer: &Pubkey) -> Result<()> {
194        if !self.relayer_signer.eq(relayer_signer)
195            && !self.secondary_relayer_signer.eq(relayer_signer)
196        {
197            // TODO: change to a better error code
198            return Err(AnchorErrorCode::ConstraintHasOne.into());
199        }
200        Ok(())
201    }
202
203    pub fn compute_swap_fees_with_default_platform_fee(
204        &self,
205        referral_fee_ppm: u64,
206        amount: u64,
207    ) -> Result<SwapFeesWithRemainingAmount> {
208        let swap_platform_fees_ppm = self.swap_platform_fee_bps * FEE_BPS_TO_PPM;
209
210        self.compute_swap_fees(referral_fee_ppm, swap_platform_fees_ppm, amount)
211    }
212
213    pub fn compute_swap_fees(
214        &self,
215        referral_fee_ppm: u64,
216        swap_platform_fee_ppm: u64,
217        amount: u64,
218    ) -> Result<SwapFeesWithRemainingAmount> {
219        if referral_fee_ppm > FEE_SPLIT_PRECISION_PPM {
220            return Err(ErrorCode::InvalidReferralFee.into());
221        }
222        let router_fee = amount
223            .checked_mul(referral_fee_ppm)
224            .ok_or(ProgramError::ArithmeticOverflow)?
225            / FEE_SPLIT_PRECISION_PPM;
226        let platform_fee = amount
227            .checked_mul(swap_platform_fee_ppm)
228            .ok_or(ProgramError::ArithmeticOverflow)?
229            / FEE_SPLIT_PRECISION_PPM;
230        let relayer_fee = platform_fee
231            .checked_mul(self.split_relayer)
232            .ok_or(ProgramError::ArithmeticOverflow)?
233            / FEE_SPLIT_PRECISION;
234
235        let remaining_amount = amount
236            .checked_sub(router_fee)
237            .ok_or(ProgramError::ArithmeticOverflow)?
238            .checked_sub(platform_fee)
239            .ok_or(ProgramError::ArithmeticOverflow)?;
240
241        let express_relay_fee = platform_fee
242            .checked_sub(relayer_fee)
243            .ok_or(ProgramError::ArithmeticOverflow)?;
244
245        Ok(SwapFeesWithRemainingAmount {
246            fees: SwapFees {
247                router_fee,
248                relayer_fee,
249                express_relay_fee,
250            },
251            remaining_amount,
252        })
253    }
254}