serum_swap/
lib.rs

1//! Program to perform instantly settled token swaps on the Serum DEX.
2//!
3//! Before using any instruction here, a user must first create an open orders
4//! account on all markets being used. This only needs to be done once, either
5//! via the system program create account instruction in the same transaction
6//! as the user's first trade or via the explicit `init_account` and
7//! `close_account` instructions provided here, which can be included in
8//! transactions.
9pub extern crate karima_anchor_lang as anchor_lang;
10pub extern crate karima_anchor_spl as anchor_spl;
11use anchor_lang::prelude::*;
12use anchor_spl::dex;
13use anchor_spl::dex::serum_dex::instruction::SelfTradeBehavior;
14use anchor_spl::dex::serum_dex::matching::{OrderType, Side as SerumSide};
15use anchor_spl::dex::serum_dex::state::MarketState;
16use anchor_spl::token;
17use solana_program::declare_id;
18use std::num::NonZeroU64;
19
20declare_id!("22Y43yTVxuUkoRKdm9thyRhQ3SdgQS7c7kB6UNCiaczD");
21
22// Associated token account for Pubkey::default.
23mod empty {
24    use super::*;
25    declare_id!("HJt8Tjdsc9ms9i4WCZEzhzr4oyf3ANcdzXrNdLPFqm3M");
26}
27
28#[program]
29pub mod serum_swap {
30    use super::*;
31
32    /// Convenience API to initialize an open orders account on the Serum DEX.
33    pub fn init_account<'info>(ctx: Context<'_, '_, '_, 'info, InitAccount<'info>>) -> Result<()> {
34        let ctx = CpiContext::new(ctx.accounts.dex_program.clone(), ctx.accounts.into());
35        dex::init_open_orders(ctx)?;
36        Ok(())
37    }
38
39    /// Convenience API to close an open orders account on the Serum DEX.
40    pub fn close_account<'info>(
41        ctx: Context<'_, '_, '_, 'info, CloseAccount<'info>>,
42    ) -> Result<()> {
43        let ctx = CpiContext::new(ctx.accounts.dex_program.clone(), ctx.accounts.into());
44        dex::close_open_orders(ctx)?;
45        Ok(())
46    }
47
48    /// Swaps two tokens on a single A/B market, where A is the base currency
49    /// and B is the quote currency. This is just a direct IOC trade that
50    /// instantly settles.
51    ///
52    /// When side is "bid", then swaps B for A. When side is "ask", then swaps
53    /// A for B.
54    ///
55    /// Arguments:
56    ///
57    /// * `side`              - The direction to swap.
58    /// * `amount`            - The amount to swap *from*
59    /// * `min_exchange_rate` - The exchange rate to use when determining
60    ///    whether the transaction should abort.
61    #[access_control(is_valid_swap(&ctx))]
62    pub fn swap<'info>(
63        ctx: Context<'_, '_, '_, 'info, Swap<'info>>,
64        side: Side,
65        amount: u64,
66        min_exchange_rate: ExchangeRate,
67    ) -> Result<()> {
68        let mut min_exchange_rate = min_exchange_rate;
69
70        // Not used for direct swaps.
71        min_exchange_rate.quote_decimals = 0;
72        msg!("swap 0");
73        // Optional referral account (earns a referral fee).
74        let referral = ctx.remaining_accounts.iter().next().map(Clone::clone);
75        msg!("swap 1");
76        // Side determines swap direction.
77        let (from_token, to_token) = match side {
78            Side::Bid => (&ctx.accounts.pc_wallet, &ctx.accounts.market.coin_wallet),
79            Side::Ask => (&ctx.accounts.market.coin_wallet, &ctx.accounts.pc_wallet),
80        };
81        msg!("swap 2");
82        // Token balances before the trade.
83        let from_amount_before = token::accessor::amount(from_token)?;
84        let to_amount_before = token::accessor::amount(to_token)?;
85        msg!("swap 3 from_token {:?} to_token {:?} ",from_token,to_token);
86        // Execute trade.
87        let orderbook: OrderbookClient<'info> = (&*ctx.accounts).into();
88        msg!("swap 4 amount {:?}",amount);
89        match side {
90            Side::Bid => orderbook.buy(amount, None)?,
91            Side::Ask => orderbook.sell(amount, None)?,
92        };
93        msg!("swap 5");
94        orderbook.settle(referral)?;
95        msg!("swap 6");
96        // Token balances after the trade.
97        let from_amount_after = token::accessor::amount(from_token)?;
98        let to_amount_after = token::accessor::amount(to_token)?;
99        msg!("swap 7 from_amount_after {:?} to_amount_after {:?}",from_amount_after,to_amount_after);
100        //  Calculate the delta, i.e. the amount swapped.
101        let from_amount = from_amount_before.checked_sub(from_amount_after).unwrap();
102        let to_amount = to_amount_after.checked_sub(to_amount_before).unwrap();
103        msg!("swap 8: before-after-from {:?} before-after-to {:?}",from_amount,to_amount);
104        // Safety checks.
105        apply_risk_checks(DidSwap {
106            authority: *ctx.accounts.authority.key,
107            given_amount: amount,
108            min_exchange_rate,
109            from_amount,
110            to_amount,
111            quote_amount: 0,
112            spill_amount: 0,
113            from_mint: token::accessor::mint(from_token)?,
114            to_mint: token::accessor::mint(to_token)?,
115            quote_mint: match side {
116                Side::Bid => token::accessor::mint(from_token)?,
117                Side::Ask => token::accessor::mint(to_token)?,
118            },
119        })?;
120
121        Ok(())
122    }
123
124    /// Swaps two base currencies across two different markets.
125    ///
126    /// That is, suppose there are two markets, A/USD(x) and B/USD(x).
127    /// Then swaps token A for token B via
128    ///
129    /// * IOC (immediate or cancel) sell order on A/USD(x) market.
130    /// * Settle open orders to get USD(x).
131    /// * IOC buy order on B/USD(x) market to convert USD(x) to token B.
132    /// * Settle open orders to get token B.
133    ///
134    /// Arguments:
135    ///
136    /// * `amount`            - The amount to swap *from*.
137    /// * `min_exchange_rate` - The exchange rate to use when determining
138    ///    whether the transaction should abort.
139    #[access_control(is_valid_swap_transitive(&ctx))]
140    pub fn swap_transitive<'info>(
141        ctx: Context<'_, '_, '_, 'info, SwapTransitive<'info>>,
142        amount: u64,
143        min_exchange_rate: ExchangeRate,
144    ) -> Result<()> {
145        // Optional referral account (earns a referral fee).
146        let referral = ctx.remaining_accounts.iter().next().map(Clone::clone);
147
148        // Leg 1: Sell Token A for USD(x) (or whatever quote currency is used).
149        let (from_amount, sell_proceeds) = {
150            // Token balances before the trade.
151            let base_before = token::accessor::amount(&ctx.accounts.from.coin_wallet)?;
152            let quote_before = token::accessor::amount(&ctx.accounts.pc_wallet)?;
153
154            // Execute the trade.
155            let orderbook = ctx.accounts.orderbook_from();
156            orderbook.sell(amount, None)?;
157            orderbook.settle(referral.clone())?;
158
159            // Token balances after the trade.
160            let base_after = token::accessor::amount(&ctx.accounts.from.coin_wallet)?;
161            let quote_after = token::accessor::amount(&ctx.accounts.pc_wallet)?;
162
163            // Report the delta.
164            (
165                base_before.checked_sub(base_after).unwrap(),
166                quote_after.checked_sub(quote_before).unwrap(),
167            )
168        };
169
170        // Leg 2: Buy Token B with USD(x) (or whatever quote currency is used).
171        let (to_amount, buy_proceeds) = {
172            // Token balances before the trade.
173            let base_before = token::accessor::amount(&ctx.accounts.to.coin_wallet)?;
174            let quote_before = token::accessor::amount(&ctx.accounts.pc_wallet)?;
175
176            // Execute the trade.
177            let orderbook = ctx.accounts.orderbook_to();
178            orderbook.buy(sell_proceeds, None)?;
179            orderbook.settle(referral)?;
180
181            // Token balances after the trade.
182            let base_after = token::accessor::amount(&ctx.accounts.to.coin_wallet)?;
183            let quote_after = token::accessor::amount(&ctx.accounts.pc_wallet)?;
184
185            // Report the delta.
186            (
187                base_after.checked_sub(base_before).unwrap(),
188                quote_before.checked_sub(quote_after).unwrap(),
189            )
190        };
191
192        // The amount of surplus quote currency *not* fully consumed by the
193        // second half of the swap.
194        let spill_amount = sell_proceeds.checked_sub(buy_proceeds).unwrap();
195
196        // Safety checks.
197        apply_risk_checks(DidSwap {
198            given_amount: amount,
199            min_exchange_rate,
200            from_amount,
201            to_amount,
202            quote_amount: sell_proceeds,
203            spill_amount,
204            from_mint: token::accessor::mint(&ctx.accounts.from.coin_wallet)?,
205            to_mint: token::accessor::mint(&ctx.accounts.to.coin_wallet)?,
206            quote_mint: token::accessor::mint(&ctx.accounts.pc_wallet)?,
207            authority: *ctx.accounts.authority.key,
208        })?;
209
210        Ok(())
211    }
212}
213
214// Asserts the swap event executed at an exchange rate acceptable to the client.
215fn apply_risk_checks(event: DidSwap) -> Result<()> {
216    // Emit the event for client consumption.
217    emit!(event);
218    msg!("apply_risk_checks ");
219    msg!("apply_risk_checks {:?}",event.to_amount);
220    if event.to_amount == 0 {
221        return Err(ErrorCode::ZeroSwap.into());
222    }
223  
224    // Use the exchange rate to calculate the client's expectation.
225    //
226    // The exchange rate given must always have decimals equal to the
227    // `to_mint` decimals, guaranteeing the `min_expected_amount`
228    // always has decimals equal to
229    //
230    // `decimals(from_mint) + decimals(to_mint) + decimals(quote_mint)`.
231    //
232    // We avoid truncating by adding `decimals(quote_mint)`.
233    let min_expected_amount = u128::from(
234        // decimals(from).
235        event.from_amount,
236    )
237    .checked_mul(
238        // decimals(from) + decimals(to).
239        event.min_exchange_rate.rate.into(),
240    )
241    .unwrap()
242    .checked_mul(
243        // decimals(from) + decimals(to) + decimals(quote).
244        10u128
245            .checked_pow(event.min_exchange_rate.quote_decimals.into())
246            .unwrap(),
247    )
248    .unwrap();
249
250    // If there is spill (i.e. quote tokens *not* fully consumed for
251    // the buy side of a transitive swap), then credit those tokens marked
252    // at the executed exchange rate to create an "effective" to_amount.
253    let effective_to_amount = {
254        // Translates the leftover spill amount into "to" units via
255        //
256        // `(to_amount_received/quote_amount_given) * spill_amount`
257        //
258        let spill_surplus = match event.spill_amount == 0 || event.min_exchange_rate.strict {
259            true => 0,
260            false => u128::from(
261                // decimals(to).
262                event.to_amount,
263            )
264            .checked_mul(
265                // decimals(to) + decimals(quote).
266                event.spill_amount.into(),
267            )
268            .unwrap()
269            .checked_mul(
270                // decimals(to) + decimals(quote) + decimals(from).
271                10u128
272                    .checked_pow(event.min_exchange_rate.from_decimals.into())
273                    .unwrap(),
274            )
275            .unwrap()
276            .checked_mul(
277                // decimals(to) + decimals(quote)*2 + decimals(from).
278                10u128
279                    .checked_pow(event.min_exchange_rate.quote_decimals.into())
280                    .unwrap(),
281            )
282            .unwrap()
283            .checked_div(
284                // decimals(to) + decimals(quote) + decimals(from).
285                event
286                    .quote_amount
287                    .checked_sub(event.spill_amount)
288                    .unwrap()
289                    .into(),
290            )
291            .unwrap(),
292        };
293
294        // Translate the `to_amount` into a common number of decimals.
295        let to_amount = u128::from(
296            // decimals(to).
297            event.to_amount,
298        )
299        .checked_mul(
300            // decimals(to) + decimals(from).
301            10u128
302                .checked_pow(event.min_exchange_rate.from_decimals.into())
303                .unwrap(),
304        )
305        .unwrap()
306        .checked_mul(
307            // decimals(to) + decimals(from) + decimals(quote).
308            10u128
309                .checked_pow(event.min_exchange_rate.quote_decimals.into())
310                .unwrap(),
311        )
312        .unwrap();
313
314        to_amount.checked_add(spill_surplus).unwrap()
315    };
316
317    // Abort if the resulting amount is less than the client's expectation.
318    if effective_to_amount < min_expected_amount {
319        msg!(
320            "effective_to_amount, min_expected_amount: {:?}, {:?}",
321            effective_to_amount,
322            min_expected_amount,
323        );
324        return Err(ErrorCode::SlippageExceeded.into());
325    }
326
327    Ok(())
328}
329
330#[derive(Accounts)]
331pub struct InitAccount<'info> {
332    #[account(mut)]
333    open_orders: AccountInfo<'info>,
334    #[account(signer)]
335    authority: AccountInfo<'info>,
336    market: AccountInfo<'info>,
337    dex_program: AccountInfo<'info>,
338    rent: AccountInfo<'info>,
339}
340
341impl<'info> From<&mut InitAccount<'info>> for dex::InitOpenOrders<'info> {
342    fn from(accs: &mut InitAccount<'info>) -> dex::InitOpenOrders<'info> {
343        dex::InitOpenOrders {
344            open_orders: accs.open_orders.clone(),
345            authority: accs.authority.clone(),
346            market: accs.market.clone(),
347            rent: accs.rent.clone(),
348        }
349    }
350}
351
352#[derive(Accounts)]
353pub struct CloseAccount<'info> {
354    #[account(mut)]
355    open_orders: AccountInfo<'info>,
356    #[account(signer)]
357    authority: AccountInfo<'info>,
358    #[account(mut)]
359    destination: AccountInfo<'info>,
360    market: AccountInfo<'info>,
361    dex_program: AccountInfo<'info>,
362}
363
364impl<'info> From<&mut CloseAccount<'info>> for dex::CloseOpenOrders<'info> {
365    fn from(accs: &mut CloseAccount<'info>) -> dex::CloseOpenOrders<'info> {
366        dex::CloseOpenOrders {
367            open_orders: accs.open_orders.clone(),
368            authority: accs.authority.clone(),
369            destination: accs.destination.clone(),
370            market: accs.market.clone(),
371        }
372    }
373}
374
375// The only constraint imposed on these accounts is that the market's base
376// currency mint is not equal to the quote currency's. All other checks are
377// done by the DEX on CPI.
378#[derive(Accounts)]
379pub struct Swap<'info> {
380    pub market: MarketAccounts<'info>,
381    #[account(signer)]
382    pub authority: AccountInfo<'info>,
383    #[account(mut, constraint = pc_wallet.key != &empty::ID)]
384    pub pc_wallet: AccountInfo<'info>,
385    // Programs.
386    pub dex_program: AccountInfo<'info>,
387    pub token_program: AccountInfo<'info>,
388    // Sysvars.
389    pub rent: AccountInfo<'info>,
390}
391
392impl<'info> From<&Swap<'info>> for OrderbookClient<'info> {
393    fn from(accounts: &Swap<'info>) -> OrderbookClient<'info> {
394        OrderbookClient {
395            market: accounts.market.clone(),
396            authority: accounts.authority.clone(),
397            pc_wallet: accounts.pc_wallet.clone(),
398            dex_program: accounts.dex_program.clone(),
399            token_program: accounts.token_program.clone(),
400            rent: accounts.rent.clone(),
401        }
402    }
403}
404
405// The only constraint imposed on these accounts is that the from market's
406// base currency's is not equal to the to market's base currency. All other
407// checks are done by the DEX on CPI (and the quote currency is ensured to be
408// the same on both markets since there's only one account field for it).
409#[derive(Accounts)]
410pub struct SwapTransitive<'info> {
411    pub from: MarketAccounts<'info>,
412    pub to: MarketAccounts<'info>,
413    // Must be the authority over all open orders accounts used.
414    #[account(signer)]
415    pub authority: AccountInfo<'info>,
416    #[account(mut, constraint = pc_wallet.key != &empty::ID)]
417    pub pc_wallet: AccountInfo<'info>,
418    // Programs.
419    pub dex_program: AccountInfo<'info>,
420    pub token_program: AccountInfo<'info>,
421    // Sysvars.
422    pub rent: AccountInfo<'info>,
423}
424
425impl<'info> SwapTransitive<'info> {
426    fn orderbook_from(&self) -> OrderbookClient<'info> {
427        OrderbookClient {
428            market: self.from.clone(),
429            authority: self.authority.clone(),
430            pc_wallet: self.pc_wallet.clone(),
431            dex_program: self.dex_program.clone(),
432            token_program: self.token_program.clone(),
433            rent: self.rent.clone(),
434        }
435    }
436    fn orderbook_to(&self) -> OrderbookClient<'info> {
437        OrderbookClient {
438            market: self.to.clone(),
439            authority: self.authority.clone(),
440            pc_wallet: self.pc_wallet.clone(),
441            dex_program: self.dex_program.clone(),
442            token_program: self.token_program.clone(),
443            rent: self.rent.clone(),
444        }
445    }
446}
447
448// Client for sending orders to the Serum DEX.
449#[derive(Clone)]
450struct OrderbookClient<'info> {
451    market: MarketAccounts<'info>,
452    authority: AccountInfo<'info>,
453    pc_wallet: AccountInfo<'info>,
454    dex_program: AccountInfo<'info>,
455    token_program: AccountInfo<'info>,
456    rent: AccountInfo<'info>,
457}
458
459impl<'info> OrderbookClient<'info> {
460    // Executes the sell order portion of the swap, purchasing as much of the
461    // quote currency as possible for the given `base_amount`.
462    //
463    // `base_amount` is the "native" amount of the base currency, i.e., token
464    // amount including decimals.
465    fn sell(
466        &self,
467        base_amount: u64,
468        srm_msrm_discount: Option<AccountInfo<'info>>,
469    ) -> ProgramResult {
470        let limit_price = 1;
471        msg!("sell 0");
472        let max_coin_qty = {
473            // The loaded market must be dropped before CPI.
474            let market = MarketState::load(&self.market.market, &dex::ID)?;
475            coin_lots(&market, base_amount)
476        };
477        msg!("sell 1 max_coin_qty {:?} limit_price {:?}",max_coin_qty,limit_price);
478        let max_native_pc_qty = u64::MAX;
479        msg!("sell 111 max_native_pc_qty {:?}  ",max_native_pc_qty);
480        self.order_cpi(
481            limit_price,
482            max_coin_qty,
483            max_native_pc_qty,
484            Side::Ask,
485            srm_msrm_discount,
486        )
487    }
488
489    // Executes the buy order portion of the swap, purchasing as much of the
490    // base currency as possible, for the given `quote_amount`.
491    //
492    // `quote_amount` is the "native" amount of the quote currency, i.e., token
493    // amount including decimals.
494    fn buy(
495        &self,
496        quote_amount: u64,
497        srm_msrm_discount: Option<AccountInfo<'info>>,
498    ) -> ProgramResult {
499        let limit_price = u64::MAX;
500        let max_coin_qty = u64::MAX;
501        let max_native_pc_qty = quote_amount;
502        msg!("buy 1 quote_amount {:?}",quote_amount);
503        msg!("buy 2 limit_price {:?} max_coin_qty {:?} max_native_pc_qty {:?} ",limit_price,max_coin_qty,max_native_pc_qty);
504        self.order_cpi(
505            limit_price,
506            max_coin_qty,
507            max_native_pc_qty,
508            Side::Bid,
509            srm_msrm_discount,
510        )
511    }
512
513    // Executes a new order on the serum dex via CPI.
514    //
515    // * `limit_price` - the limit order price in lot units.
516    // * `max_coin_qty`- the max number of the base currency lot units.
517    // * `max_native_pc_qty` - the max number of quote currency in native token
518    //                         units (includes decimals).
519    // * `side` - bid or ask, i.e. the type of order.
520    // * `referral` - referral account, earning a fee.
521    fn order_cpi(
522        &self,
523        limit_price: u64,
524        max_coin_qty: u64,
525        max_native_pc_qty: u64,
526        side: Side,
527        srm_msrm_discount: Option<AccountInfo<'info>>,
528    ) -> ProgramResult {
529        // Client order id is only used for cancels. Not used here so hardcode.
530        let client_order_id = 0;
531        // Limit is the dex's custom compute budge parameter, setting an upper
532        // bound on the number of matching cycles the program can perform
533        // before giving up and posting the remaining unmatched order.
534        let limit = 65535;
535       msg!("order_cpi 1");
536        let mut ctx = CpiContext::new(self.dex_program.clone(), self.clone().into());
537        if let Some(srm_msrm_discount) = srm_msrm_discount {
538            ctx = ctx.with_remaining_accounts(vec![srm_msrm_discount]);
539        }
540        msg!("order_cpi 2");
541        dex::new_order_v3(
542            ctx,
543            side.into(),
544            NonZeroU64::new(limit_price).unwrap(),
545            NonZeroU64::new(max_coin_qty).unwrap(),
546            NonZeroU64::new(max_native_pc_qty).unwrap(),
547            SelfTradeBehavior::DecrementTake,
548            OrderType::ImmediateOrCancel,
549            client_order_id,
550            limit,
551        )
552    }
553
554    fn settle(&self, referral: Option<AccountInfo<'info>>) -> ProgramResult {
555        let settle_accs = dex::SettleFunds {
556            market: self.market.market.clone(),
557            open_orders: self.market.open_orders.clone(),
558            open_orders_authority: self.authority.clone(),
559            coin_vault: self.market.coin_vault.clone(),
560            pc_vault: self.market.pc_vault.clone(),
561            coin_wallet: self.market.coin_wallet.clone(),
562            pc_wallet: self.pc_wallet.clone(),
563            vault_signer: self.market.vault_signer.clone(),
564            token_program: self.token_program.clone(),
565        };
566        let mut ctx = CpiContext::new(self.dex_program.clone(), settle_accs);
567        if let Some(referral) = referral {
568            ctx = ctx.with_remaining_accounts(vec![referral]);
569        }
570        dex::settle_funds(ctx)
571    }
572}
573
574impl<'info> From<OrderbookClient<'info>> for dex::NewOrderV3<'info> {
575    fn from(c: OrderbookClient<'info>) -> dex::NewOrderV3<'info> {
576        dex::NewOrderV3 {
577            market: c.market.market.clone(),
578            open_orders: c.market.open_orders.clone(),
579            request_queue: c.market.request_queue.clone(),
580            event_queue: c.market.event_queue.clone(),
581            market_bids: c.market.bids.clone(),
582            market_asks: c.market.asks.clone(),
583            order_payer_token_account: c.market.order_payer_token_account.clone(),
584            open_orders_authority: c.authority.clone(),
585            coin_vault: c.market.coin_vault.clone(),
586            pc_vault: c.market.pc_vault.clone(),
587            token_program: c.token_program.clone(),
588            rent: c.rent.clone(),
589        }
590    }
591}
592
593// Returns the amount of lots for the base currency of a trade with `size`.
594fn coin_lots(market: &MarketState, size: u64) -> u64 {
595    size.checked_div(market.coin_lot_size).unwrap()
596}
597
598// Market accounts are the accounts used to place orders against the dex minus
599// common accounts, i.e., program ids, sysvars, and the `pc_wallet`.
600#[derive(Accounts, Clone)]
601pub struct MarketAccounts<'info> {
602    #[account(mut)]
603    pub market: AccountInfo<'info>,
604    #[account(mut)]
605    pub open_orders: AccountInfo<'info>,
606    #[account(mut)]
607    pub request_queue: AccountInfo<'info>,
608    #[account(mut)]
609    pub event_queue: AccountInfo<'info>,
610    #[account(mut)]
611    pub bids: AccountInfo<'info>,
612    #[account(mut)]
613    pub asks: AccountInfo<'info>,
614    // The `spl_token::Account` that funds will be taken from, i.e., transferred
615    // from the user into the market's vault.
616    //
617    // For bids, this is the base currency. For asks, the quote.
618    #[account(mut, constraint = order_payer_token_account.key != &empty::ID)]
619    pub order_payer_token_account: AccountInfo<'info>,
620    // Also known as the "base" currency. For a given A/B market,
621    // this is the vault for the A mint.
622    #[account(mut)]
623    pub coin_vault: AccountInfo<'info>,
624    // Also known as the "quote" currency. For a given A/B market,
625    // this is the vault for the B mint.
626    #[account(mut)]
627    pub pc_vault: AccountInfo<'info>,
628    // PDA owner of the DEX's token accounts for base + quote currencies.
629    pub vault_signer: AccountInfo<'info>,
630    // User wallets.
631    #[account(mut, constraint = coin_wallet.key != &empty::ID)]
632    pub coin_wallet: AccountInfo<'info>,
633}
634
635#[derive(AnchorSerialize, AnchorDeserialize)]
636pub enum Side {
637    Bid,
638    Ask,
639}
640
641impl From<Side> for SerumSide {
642    fn from(side: Side) -> SerumSide {
643        match side {
644            Side::Bid => SerumSide::Bid,
645            Side::Ask => SerumSide::Ask,
646        }
647    }
648}
649
650// Access control modifiers.
651
652fn is_valid_swap(ctx: &Context<Swap>) -> Result<()> {
653    _is_valid_swap(&ctx.accounts.market.coin_wallet, &ctx.accounts.pc_wallet)
654}
655
656fn is_valid_swap_transitive(ctx: &Context<SwapTransitive>) -> Result<()> {
657    _is_valid_swap(&ctx.accounts.from.coin_wallet, &ctx.accounts.to.coin_wallet)
658}
659
660// Validates the tokens being swapped are of different mints.
661fn _is_valid_swap<'info>(from: &AccountInfo<'info>, to: &AccountInfo<'info>) -> Result<()> {
662    let from_token_mint = token::accessor::mint(from)?;
663    let to_token_mint = token::accessor::mint(to)?;
664    if from_token_mint == to_token_mint {
665        return Err(ErrorCode::SwapTokensCannotMatch.into());
666    }
667    Ok(())
668}
669
670// Event emitted when a swap occurs for two base currencies on two different
671// markets (quoted in the same token).
672#[event]
673pub struct DidSwap {
674    // User given (max) amount  of the "from" token to swap.
675    pub given_amount: u64,
676    // The minimum exchange rate for swapping `from_amount` to `to_amount` in
677    // native units with decimals equal to the `to_amount`'s mint--specified
678    // by the client.
679    pub min_exchange_rate: ExchangeRate,
680    // Amount of the `from` token sold.
681    pub from_amount: u64,
682    // Amount of the `to` token purchased.
683    pub to_amount: u64,
684    // The amount of the quote currency used for a *transitive* swap. This is
685    // the amount *received* for selling on the first leg of the swap.
686    pub quote_amount: u64,
687    // Amount of the quote currency accumulated from a *transitive* swap, i.e.,
688    // the difference between the amount gained from the first leg of the swap
689    // (to sell) and the amount used in the second leg of the swap (to buy).
690    pub spill_amount: u64,
691    // Mint sold.
692    pub from_mint: Pubkey,
693    // Mint purchased.
694    pub to_mint: Pubkey,
695    // Mint of the token used as the quote currency in the two markets used
696    // for swapping.
697    pub quote_mint: Pubkey,
698    // User that signed the transaction.
699    pub authority: Pubkey,
700}
701
702// An exchange rate for swapping *from* one token *to* another.
703#[derive(AnchorSerialize, AnchorDeserialize)]
704pub struct ExchangeRate {
705    // The amount of *to* tokens one should receive for a single *from token.
706    // This number must be in native *to* units with the same amount of decimals
707    // as the *to* mint.
708    pub rate: u64,
709    // Number of decimals of the *from* token's mint.
710    pub from_decimals: u8,
711    // Number of decimals of the *to* token's mint.
712    // For a direct swap, this should be zero.
713    pub quote_decimals: u8,
714    // True if *all* of the *from* currency sold should be used when calculating
715    // the executed exchange rate.
716    //
717    // To perform a transitive swap, one sells on one market and buys on
718    // another, where both markets are quoted in the same currency. Now suppose
719    // one swaps A for B across A/USDC and B/USDC. Further suppose the first
720    // leg swaps the entire *from* amount A for USDC, and then only half of
721    // the USDC is used to swap for B on the second leg. How should we calculate
722    // the exchange rate?
723    //
724    // If strict is true, then the exchange rate will be calculated as a direct
725    // function of the A tokens lost and B tokens gained, ignoring the surplus
726    // in USDC received. If strict is false, an effective exchange rate will be
727    // used. I.e. the surplus in USDC will be marked at the exchange rate from
728    // the second leg of the swap and that amount will be added to the
729    // *to* mint received before calculating the swap's exchange rate.
730    //
731    // Transitive swaps only. For direct swaps, this field is ignored.
732    pub strict: bool,
733}
734
735#[error]
736pub enum ErrorCode {
737    #[msg("The tokens being swapped must have different mints")]
738    SwapTokensCannotMatch,
739    #[msg("Slippage tolerance exceeded")]
740    SlippageExceeded,
741    #[msg("No tokens received when swapping")]
742    ZeroSwap,
743}