manifest/validation/
loaders.rs

1use std::{cell::Ref, slice::Iter};
2
3use hypertree::{get_helper, trace};
4use solana_program::{
5    account_info::{next_account_info, AccountInfo},
6    program_error::ProgramError,
7    pubkey::Pubkey,
8    system_program,
9};
10
11use crate::{
12    program::ManifestError,
13    require,
14    state::{GlobalFixed, MarketFixed},
15    validation::{EmptyAccount, MintAccountInfo, Program, Signer, TokenAccountInfo},
16};
17
18use super::{get_vault_address, ManifestAccountInfo, TokenProgram};
19
20#[cfg(feature = "certora")]
21use early_panic::early_panic;
22
23/// CreateMarket account infos
24pub(crate) struct CreateMarketContext<'a, 'info> {
25    pub payer: Signer<'a, 'info>,
26    pub market: ManifestAccountInfo<'a, 'info, MarketFixed>,
27    pub base_mint: MintAccountInfo<'a, 'info>,
28    pub quote_mint: MintAccountInfo<'a, 'info>,
29    pub base_vault: EmptyAccount<'a, 'info>,
30    pub quote_vault: EmptyAccount<'a, 'info>,
31    pub system_program: Program<'a, 'info>,
32    pub token_program: TokenProgram<'a, 'info>,
33    pub token_program_22: TokenProgram<'a, 'info>,
34}
35
36impl<'a, 'info> CreateMarketContext<'a, 'info> {
37    pub fn load(accounts: &'a [AccountInfo<'info>]) -> Result<Self, ProgramError> {
38        let account_iter: &mut Iter<AccountInfo<'info>> = &mut accounts.iter();
39
40        let payer: Signer = Signer::new_payer(next_account_info(account_iter)?)?;
41        let market: ManifestAccountInfo<MarketFixed> =
42            ManifestAccountInfo::<MarketFixed>::new_init(next_account_info(account_iter)?)?;
43        let system_program: Program =
44            Program::new(next_account_info(account_iter)?, &system_program::id())?;
45        let base_mint: MintAccountInfo = MintAccountInfo::new(next_account_info(account_iter)?)?;
46        let quote_mint: MintAccountInfo = MintAccountInfo::new(next_account_info(account_iter)?)?;
47        let base_vault: EmptyAccount = EmptyAccount::new(next_account_info(account_iter)?)?;
48        let quote_vault: EmptyAccount = EmptyAccount::new(next_account_info(account_iter)?)?;
49
50        let (expected_base_vault, _base_vault_bump) =
51            get_vault_address(market.key, base_mint.info.key);
52        let (expected_quote_vault, _quote_vault_bump) =
53            get_vault_address(market.key, quote_mint.info.key);
54
55        require!(
56            expected_base_vault == *base_vault.info.key,
57            ManifestError::IncorrectAccount,
58            "Incorrect base vault account",
59        )?;
60        require!(
61            expected_quote_vault == *quote_vault.info.key,
62            ManifestError::IncorrectAccount,
63            "Incorrect quote vault account",
64        )?;
65        let token_program: TokenProgram = TokenProgram::new(next_account_info(account_iter)?)?;
66        let token_program_22: TokenProgram = TokenProgram::new(next_account_info(account_iter)?)?;
67
68        Ok(Self {
69            payer,
70            market,
71            base_vault,
72            quote_vault,
73            base_mint,
74            quote_mint,
75            token_program,
76            token_program_22,
77            system_program,
78        })
79    }
80}
81
82/// ClaimSeat account infos
83pub(crate) struct ClaimSeatContext<'a, 'info> {
84    pub payer: Signer<'a, 'info>,
85    pub market: ManifestAccountInfo<'a, 'info, MarketFixed>,
86    pub _system_program: Program<'a, 'info>,
87}
88
89impl<'a, 'info> ClaimSeatContext<'a, 'info> {
90    #[cfg_attr(all(feature = "certora", not(feature = "certora-test")), early_panic)]
91    pub fn load(accounts: &'a [AccountInfo<'info>]) -> Result<Self, ProgramError> {
92        let account_iter: &mut Iter<AccountInfo<'info>> = &mut accounts.iter();
93
94        let payer: Signer = Signer::new(next_account_info(account_iter)?)?;
95        let market: ManifestAccountInfo<MarketFixed> =
96            ManifestAccountInfo::<MarketFixed>::new(next_account_info(account_iter)?)?;
97        let _system_program: Program =
98            Program::new(next_account_info(account_iter)?, &system_program::id())?;
99        Ok(Self {
100            payer,
101            market,
102            _system_program,
103        })
104    }
105}
106
107/// ExpandMarketContext account infos
108pub(crate) struct ExpandMarketContext<'a, 'info> {
109    pub payer: Signer<'a, 'info>,
110    pub market: ManifestAccountInfo<'a, 'info, MarketFixed>,
111    pub _system_program: Program<'a, 'info>,
112}
113
114impl<'a, 'info> ExpandMarketContext<'a, 'info> {
115    pub fn load(accounts: &'a [AccountInfo<'info>]) -> Result<Self, ProgramError> {
116        let account_iter: &mut Iter<AccountInfo<'info>> = &mut accounts.iter();
117
118        let payer: Signer = Signer::new_payer(next_account_info(account_iter)?)?;
119        let market: ManifestAccountInfo<MarketFixed> =
120            ManifestAccountInfo::<MarketFixed>::new(next_account_info(account_iter)?)?;
121        let _system_program: Program =
122            Program::new(next_account_info(account_iter)?, &system_program::id())?;
123        Ok(Self {
124            payer,
125            market,
126            _system_program,
127        })
128    }
129}
130
131/// Deposit into a market account infos
132pub(crate) struct DepositContext<'a, 'info> {
133    pub payer: Signer<'a, 'info>,
134    pub market: ManifestAccountInfo<'a, 'info, MarketFixed>,
135    pub trader_token: TokenAccountInfo<'a, 'info>,
136    pub vault: TokenAccountInfo<'a, 'info>,
137    pub token_program: TokenProgram<'a, 'info>,
138    pub mint: MintAccountInfo<'a, 'info>,
139}
140
141impl<'a, 'info> DepositContext<'a, 'info> {
142    #[cfg_attr(all(feature = "certora", not(feature = "certora-test")), early_panic)]
143    pub fn load(accounts: &'a [AccountInfo<'info>]) -> Result<Self, ProgramError> {
144        let account_iter: &mut Iter<AccountInfo<'info>> = &mut accounts.iter();
145
146        let payer: Signer = Signer::new(next_account_info(account_iter)?)?;
147        let market: ManifestAccountInfo<MarketFixed> =
148            ManifestAccountInfo::<MarketFixed>::new(next_account_info(account_iter)?)?;
149
150        let market_fixed: Ref<MarketFixed> = market.get_fixed()?;
151        let base_mint: &Pubkey = market_fixed.get_base_mint();
152        let quote_mint: &Pubkey = market_fixed.get_quote_mint();
153
154        let token_account_info: &AccountInfo<'info> = next_account_info(account_iter)?;
155
156        // Infer the mint key from the token account.
157        let (mint, expected_vault_address) =
158            if &token_account_info.try_borrow_data()?[0..32] == base_mint.as_ref() {
159                (base_mint, market_fixed.get_base_vault())
160            } else if &token_account_info.try_borrow_data()?[0..32] == quote_mint.as_ref() {
161                (quote_mint, market_fixed.get_quote_vault())
162            } else {
163                return Err(ManifestError::InvalidWithdrawAccounts.into());
164            };
165
166        trace!("trader token account {:?}", token_account_info.key);
167        let trader_token: TokenAccountInfo =
168            TokenAccountInfo::new_with_owner(token_account_info, mint, payer.key)?;
169
170        trace!("vault token account {:?}", expected_vault_address);
171        let vault: TokenAccountInfo = TokenAccountInfo::new_with_owner_and_key(
172            next_account_info(account_iter)?,
173            mint,
174            &expected_vault_address,
175            &expected_vault_address,
176        )?;
177
178        let token_program: TokenProgram = TokenProgram::new(next_account_info(account_iter)?)?;
179        let mint: MintAccountInfo = MintAccountInfo::new(next_account_info(account_iter)?)?;
180
181        // Drop the market ref so it can be passed through the return.
182        drop(market_fixed);
183        Ok(Self {
184            payer,
185            market,
186            trader_token,
187            vault,
188            token_program,
189            mint,
190        })
191    }
192}
193
194/// Withdraw account infos
195pub(crate) struct WithdrawContext<'a, 'info> {
196    pub payer: Signer<'a, 'info>,
197    pub market: ManifestAccountInfo<'a, 'info, MarketFixed>,
198    pub trader_token: TokenAccountInfo<'a, 'info>,
199    pub vault: TokenAccountInfo<'a, 'info>,
200    pub token_program: TokenProgram<'a, 'info>,
201    pub mint: MintAccountInfo<'a, 'info>,
202}
203
204impl<'a, 'info> WithdrawContext<'a, 'info> {
205    pub fn load(accounts: &'a [AccountInfo<'info>]) -> Result<Self, ProgramError> {
206        let account_iter: &mut Iter<AccountInfo<'info>> = &mut accounts.iter();
207
208        let payer: Signer = Signer::new(next_account_info(account_iter)?)?;
209        let market: ManifestAccountInfo<MarketFixed> =
210            ManifestAccountInfo::<MarketFixed>::new(next_account_info(account_iter)?)?;
211
212        let market_fixed: Ref<MarketFixed> = market.get_fixed()?;
213        let base_mint: &Pubkey = market_fixed.get_base_mint();
214        let quote_mint: &Pubkey = market_fixed.get_quote_mint();
215
216        let token_account_info: &AccountInfo<'info> = next_account_info(account_iter)?;
217
218        let (mint, expected_vault_address) =
219            if &token_account_info.try_borrow_data()?[0..32] == base_mint.as_ref() {
220                (base_mint, market_fixed.get_base_vault())
221            } else if &token_account_info.try_borrow_data()?[0..32] == quote_mint.as_ref() {
222                (quote_mint, market_fixed.get_quote_vault())
223            } else {
224                return Err(ManifestError::InvalidWithdrawAccounts.into());
225            };
226
227        let trader_token: TokenAccountInfo =
228            TokenAccountInfo::new_with_owner(token_account_info, mint, payer.key)?;
229        let vault: TokenAccountInfo = TokenAccountInfo::new_with_owner_and_key(
230            next_account_info(account_iter)?,
231            mint,
232            &expected_vault_address,
233            &expected_vault_address,
234        )?;
235
236        let token_program: TokenProgram = TokenProgram::new(next_account_info(account_iter)?)?;
237        let mint: MintAccountInfo = MintAccountInfo::new(next_account_info(account_iter)?)?;
238
239        // Drop the market ref so it can be passed through the return.
240        drop(market_fixed);
241        Ok(Self {
242            payer,
243            market,
244            trader_token,
245            vault,
246            token_program,
247            mint,
248        })
249    }
250}
251
252/// Swap account infos
253pub(crate) struct SwapContext<'a, 'info> {
254    pub payer: Signer<'a, 'info>,
255    pub owner: Signer<'a, 'info>,
256    pub market: ManifestAccountInfo<'a, 'info, MarketFixed>,
257    pub trader_base: TokenAccountInfo<'a, 'info>,
258    pub trader_quote: TokenAccountInfo<'a, 'info>,
259    pub base_vault: TokenAccountInfo<'a, 'info>,
260    pub quote_vault: TokenAccountInfo<'a, 'info>,
261    pub token_program_base: TokenProgram<'a, 'info>,
262    pub token_program_quote: TokenProgram<'a, 'info>,
263    pub base_mint: Option<MintAccountInfo<'a, 'info>>,
264    pub quote_mint: Option<MintAccountInfo<'a, 'info>>,
265
266    // One for each side. First is base, then is quote.
267    pub global_trade_accounts_opts: [Option<GlobalTradeAccounts<'a, 'info>>; 2],
268}
269
270impl<'a, 'info> SwapContext<'a, 'info> {
271    #[cfg_attr(all(feature = "certora", not(feature = "certora-test")), early_panic)]
272    pub fn load(accounts: &'a [AccountInfo<'info>]) -> Result<Self, ProgramError> {
273        let account_iter: &mut Iter<AccountInfo<'info>> = &mut accounts.iter();
274
275        let payer: Signer = Signer::new(next_account_info(account_iter)?)?;
276
277        let owner_or_market: &'a AccountInfo<'info> = next_account_info(account_iter)?;
278        let (owner, market): (Signer, ManifestAccountInfo<MarketFixed>) =
279            if *owner_or_market.owner == crate::ID {
280                // Normal case where the payer of rent is the same as the token account
281                // owners and the caller has only included one to save ix call data
282                // bytes.
283                (
284                    payer.clone(),
285                    ManifestAccountInfo::<MarketFixed>::new(owner_or_market)?,
286                )
287            } else {
288                // Separate token account owner from rent payer. This is SwapV2.
289                (
290                    Signer::new(owner_or_market)?,
291                    ManifestAccountInfo::<MarketFixed>::new(next_account_info(account_iter)?)?,
292                )
293            };
294
295        // Included in case we need to expand for a reverse order.
296        let _system_program: Program =
297            Program::new(next_account_info(account_iter)?, &system_program::id())?;
298
299        let market_fixed: Ref<MarketFixed> = market.get_fixed()?;
300        let base_mint_key: Pubkey = *market_fixed.get_base_mint();
301        let quote_mint_key: Pubkey = *market_fixed.get_quote_mint();
302
303        let trader_base: TokenAccountInfo = TokenAccountInfo::new_with_owner(
304            next_account_info(account_iter)?,
305            &base_mint_key,
306            owner.key,
307        )?;
308        let trader_quote: TokenAccountInfo = TokenAccountInfo::new_with_owner(
309            next_account_info(account_iter)?,
310            &quote_mint_key,
311            owner.key,
312        )?;
313        let base_vault_address: &Pubkey = market_fixed.get_base_vault();
314        let quote_vault_address: &Pubkey = market_fixed.get_quote_vault();
315
316        let base_vault: TokenAccountInfo = TokenAccountInfo::new_with_owner_and_key(
317            next_account_info(account_iter)?,
318            &base_mint_key,
319            &base_vault_address,
320            &base_vault_address,
321        )?;
322        let quote_vault: TokenAccountInfo = TokenAccountInfo::new_with_owner_and_key(
323            next_account_info(account_iter)?,
324            &quote_mint_key,
325            &quote_vault_address,
326            &quote_vault_address,
327        )?;
328        drop(market_fixed);
329
330        let token_program_base: TokenProgram = TokenProgram::new(next_account_info(account_iter)?)?;
331        let mut base_mint: Option<MintAccountInfo> = None;
332
333        let mut current_account_info_or: Result<&AccountInfo<'info>, ProgramError> =
334            next_account_info(account_iter);
335
336        // Possibly includes base mint.
337        if current_account_info_or
338            .as_ref()
339            .is_ok_and(|f| *f.owner == spl_token::id() || *f.owner == spl_token_2022::id())
340        {
341            let current_account_info: &AccountInfo<'info> = current_account_info_or?;
342            base_mint = Some(MintAccountInfo::new(current_account_info)?);
343            current_account_info_or = next_account_info(account_iter);
344        }
345
346        // Clone is not a problem since we are deserializing token program
347        // anyways, so at most this is one more.
348        let mut token_program_quote: TokenProgram = token_program_base.clone();
349        let mut quote_mint: Option<MintAccountInfo> = None;
350        let mut global_trade_accounts_opts: [Option<GlobalTradeAccounts<'a, 'info>>; 2] =
351            [None, None];
352
353        // Possibly includes quote token program.
354        if current_account_info_or
355            .as_ref()
356            .is_ok_and(|f| *f.key == spl_token::id() || *f.key == spl_token_2022::id())
357        {
358            let current_account_info: &AccountInfo<'info> = current_account_info_or?;
359            token_program_quote = TokenProgram::new(current_account_info)?;
360            current_account_info_or = next_account_info(account_iter);
361        }
362        // Possibly includes quote mint if the quote token program was token22.
363        if current_account_info_or
364            .as_ref()
365            .is_ok_and(|f| *f.owner == spl_token::id() || *f.owner == spl_token_2022::id())
366        {
367            let current_account_info: &AccountInfo<'info> = current_account_info_or?;
368            quote_mint = Some(MintAccountInfo::new(current_account_info)?);
369            current_account_info_or = next_account_info(account_iter);
370        }
371
372        if current_account_info_or.is_ok() {
373            let current_account_info: &AccountInfo<'info> = current_account_info_or?;
374
375            // It is possible that the global account does not exist. Do not
376            // throw an error. This will happen when users just blindly include
377            // global accounts that have not been initialized.
378            if !current_account_info.data_is_empty() {
379                let global: ManifestAccountInfo<'a, 'info, GlobalFixed> =
380                    ManifestAccountInfo::<GlobalFixed>::new(current_account_info)?;
381                let global_data: Ref<&mut [u8]> = global.data.borrow();
382                let global_fixed: &GlobalFixed = get_helper::<GlobalFixed>(&global_data, 0_u32);
383                let global_mint_key: &Pubkey = global_fixed.get_mint();
384                let expected_global_vault_address: &Pubkey = global_fixed.get_vault();
385
386                let global_vault: TokenAccountInfo<'a, 'info> =
387                    TokenAccountInfo::new_with_owner_and_key(
388                        next_account_info(account_iter)?,
389                        global_mint_key,
390                        &expected_global_vault_address,
391                        &expected_global_vault_address,
392                    )?;
393
394                let index: usize = if *global_mint_key == base_mint_key {
395                    0
396                } else {
397                    require!(
398                        quote_mint_key == *global_mint_key,
399                        ManifestError::MissingGlobal,
400                        "Unexpected global accounts",
401                    )?;
402                    1
403                };
404
405                drop(global_data);
406                global_trade_accounts_opts[index] = Some(GlobalTradeAccounts {
407                    mint_opt: if index == 0 {
408                        base_mint.clone()
409                    } else {
410                        quote_mint.clone()
411                    },
412                    global,
413                    global_vault_opt: Some(global_vault),
414                    market_vault_opt: if index == 0 {
415                        Some(base_vault.clone())
416                    } else {
417                        Some(quote_vault.clone())
418                    },
419                    token_program_opt: if index == 0 {
420                        Some(token_program_base.clone())
421                    } else {
422                        Some(token_program_quote.clone())
423                    },
424                    gas_payer_opt: None,
425                    gas_receiver_opt: Some(payer.clone()),
426                    market: *market.info.key,
427                    system_program: None,
428                });
429            }
430        }
431
432        Ok(Self {
433            payer,
434            owner,
435            market,
436            trader_base,
437            trader_quote,
438            base_vault,
439            quote_vault,
440            token_program_base,
441            token_program_quote,
442            base_mint,
443            quote_mint,
444            global_trade_accounts_opts,
445        })
446    }
447}
448
449/// Accounts needed to make a global trade. Scope is beyond just crate so
450/// clients can place orders on markets in testing.
451pub struct GlobalTradeAccounts<'a, 'info> {
452    /// Required if this is a token22 token.
453    pub mint_opt: Option<MintAccountInfo<'a, 'info>>,
454    pub global: ManifestAccountInfo<'a, 'info, GlobalFixed>,
455
456    // These are required when matching a global order, not necessarily when
457    // cancelling since tokens dont move in that case.
458    pub global_vault_opt: Option<TokenAccountInfo<'a, 'info>>,
459    pub market_vault_opt: Option<TokenAccountInfo<'a, 'info>>,
460    pub token_program_opt: Option<TokenProgram<'a, 'info>>,
461
462    pub system_program: Option<Program<'a, 'info>>,
463
464    // Trader is sending or cancelling the order. They are the one who will pay
465    // or receive gas prepayments.
466    pub gas_payer_opt: Option<Signer<'a, 'info>>,
467    pub gas_receiver_opt: Option<Signer<'a, 'info>>,
468    pub market: Pubkey,
469}
470
471/// BatchUpdate account infos
472pub(crate) struct BatchUpdateContext<'a, 'info> {
473    pub payer: Signer<'a, 'info>,
474    pub market: ManifestAccountInfo<'a, 'info, MarketFixed>,
475    pub _system_program: Program<'a, 'info>,
476
477    // One for each side. First is base, then is quote.
478    pub global_trade_accounts_opts: [Option<GlobalTradeAccounts<'a, 'info>>; 2],
479}
480
481impl<'a, 'info> BatchUpdateContext<'a, 'info> {
482    pub fn load(accounts: &'a [AccountInfo<'info>]) -> Result<Self, ProgramError> {
483        let account_iter: &mut Iter<AccountInfo<'info>> = &mut accounts.iter();
484
485        // Does not have to be writable, but this ix will fail if removing a
486        // global or requiring expanding.
487        let payer: Signer = Signer::new(next_account_info(account_iter)?)?;
488        let market: ManifestAccountInfo<MarketFixed> =
489            ManifestAccountInfo::<MarketFixed>::new(next_account_info(account_iter)?)?;
490        let system_program: Program =
491            Program::new(next_account_info(account_iter)?, &system_program::id())?;
492        // Certora version is not mutable.
493        #[cfg(feature = "certora")]
494        let global_trade_accounts_opts: [Option<GlobalTradeAccounts<'a, 'info>>; 2] = [None, None];
495        #[cfg(not(feature = "certora"))]
496        let mut global_trade_accounts_opts: [Option<GlobalTradeAccounts<'a, 'info>>; 2] =
497            [None, None];
498
499        #[cfg(not(feature = "certora"))]
500        {
501            let market_fixed: Ref<MarketFixed> = market.get_fixed()?;
502            let base_mint: Pubkey = *market_fixed.get_base_mint();
503            let quote_mint: Pubkey = *market_fixed.get_quote_mint();
504            let base_vault: Pubkey = *market_fixed.get_base_vault();
505            let quote_vault: Pubkey = *market_fixed.get_quote_vault();
506            drop(market_fixed);
507
508            for _ in 0..2 {
509                let next_account_info_or: Result<&AccountInfo<'info>, ProgramError> =
510                    next_account_info(account_iter);
511                if next_account_info_or.is_ok() {
512                    let mint: MintAccountInfo<'a, 'info> =
513                        MintAccountInfo::new(next_account_info_or?)?;
514                    let (index, expected_market_vault_address) = if base_mint == *mint.info.key {
515                        (0, &base_vault)
516                    } else {
517                        require!(
518                            quote_mint == *mint.info.key,
519                            ManifestError::MissingGlobal,
520                            "Unexpected global mint",
521                        )?;
522                        (1, &quote_vault)
523                    };
524
525                    let global_or: Result<
526                        ManifestAccountInfo<'a, 'info, GlobalFixed>,
527                        ProgramError,
528                    > = ManifestAccountInfo::<GlobalFixed>::new(next_account_info(account_iter)?);
529
530                    // If a client blindly fills in the global account and vault,
531                    // then handle that case and allow them to try to work without
532                    // the global accounts.
533                    if global_or.is_err() {
534                        let _global_vault: Result<&AccountInfo<'info>, ProgramError> =
535                            next_account_info(account_iter);
536                        let _market_vault: Result<&AccountInfo<'info>, ProgramError> =
537                            next_account_info(account_iter);
538                        let _token_program: Result<&AccountInfo<'info>, ProgramError> =
539                            next_account_info(account_iter);
540                        continue;
541                    }
542                    let global: ManifestAccountInfo<'a, 'info, GlobalFixed> = global_or.unwrap();
543                    let global_data: Ref<&mut [u8]> = global.data.borrow();
544                    let global_fixed: &GlobalFixed = get_helper::<GlobalFixed>(&global_data, 0_u32);
545                    let expected_global_vault_address: &Pubkey = global_fixed.get_vault();
546
547                    let global_vault: TokenAccountInfo<'a, 'info> =
548                        TokenAccountInfo::new_with_owner_and_key(
549                            next_account_info(account_iter)?,
550                            mint.info.key,
551                            &expected_global_vault_address,
552                            &expected_global_vault_address,
553                        )?;
554                    drop(global_data);
555
556                    let market_vault: TokenAccountInfo<'a, 'info> =
557                        TokenAccountInfo::new_with_owner_and_key(
558                            next_account_info(account_iter)?,
559                            mint.info.key,
560                            &expected_market_vault_address,
561                            &expected_market_vault_address,
562                        )?;
563                    let token_program: TokenProgram<'a, 'info> =
564                        TokenProgram::new(next_account_info(account_iter)?)?;
565
566                    global_trade_accounts_opts[index] = Some(GlobalTradeAccounts {
567                        mint_opt: Some(mint),
568                        global,
569                        global_vault_opt: Some(global_vault),
570                        market_vault_opt: Some(market_vault),
571                        token_program_opt: Some(token_program),
572                        system_program: Some(system_program.clone()),
573                        gas_payer_opt: Some(payer.clone()),
574                        gas_receiver_opt: Some(payer.clone()),
575                        market: *market.info.key,
576                    })
577                };
578            }
579        }
580
581        Ok(Self {
582            payer,
583            market,
584            _system_program: system_program,
585            global_trade_accounts_opts,
586        })
587    }
588}
589
590/// Global create
591pub(crate) struct GlobalCreateContext<'a, 'info> {
592    pub payer: Signer<'a, 'info>,
593    pub global: EmptyAccount<'a, 'info>,
594    pub system_program: Program<'a, 'info>,
595    pub global_mint: MintAccountInfo<'a, 'info>,
596    pub global_vault: EmptyAccount<'a, 'info>,
597    pub token_program: TokenProgram<'a, 'info>,
598}
599
600impl<'a, 'info> GlobalCreateContext<'a, 'info> {
601    pub fn load(accounts: &'a [AccountInfo<'info>]) -> Result<Self, ProgramError> {
602        let account_iter: &mut Iter<AccountInfo<'info>> = &mut accounts.iter();
603
604        let payer: Signer = Signer::new_payer(next_account_info(account_iter)?)?;
605        let global: EmptyAccount = EmptyAccount::new(next_account_info(account_iter)?)?;
606        let system_program: Program =
607            Program::new(next_account_info(account_iter)?, &system_program::id())?;
608        let global_mint: MintAccountInfo = MintAccountInfo::new(next_account_info(account_iter)?)?;
609        // Address of the global vault is verified in the handler because the
610        // create will only work if the signer seeds match.
611        let global_vault: EmptyAccount = EmptyAccount::new(next_account_info(account_iter)?)?;
612        let token_program: TokenProgram = TokenProgram::new(next_account_info(account_iter)?)?;
613        Ok(Self {
614            payer,
615            global,
616            system_program,
617            global_mint,
618            global_vault,
619            token_program,
620        })
621    }
622}
623
624/// Global add trader
625pub(crate) struct GlobalAddTraderContext<'a, 'info> {
626    pub payer: Signer<'a, 'info>,
627    pub global: ManifestAccountInfo<'a, 'info, GlobalFixed>,
628    pub _system_program: Program<'a, 'info>,
629}
630
631impl<'a, 'info> GlobalAddTraderContext<'a, 'info> {
632    pub fn load(accounts: &'a [AccountInfo<'info>]) -> Result<Self, ProgramError> {
633        let account_iter: &mut Iter<AccountInfo<'info>> = &mut accounts.iter();
634
635        let payer: Signer = Signer::new_payer(next_account_info(account_iter)?)?;
636        let global: ManifestAccountInfo<GlobalFixed> =
637            ManifestAccountInfo::<GlobalFixed>::new(next_account_info(account_iter)?)?;
638        let _system_program: Program =
639            Program::new(next_account_info(account_iter)?, &system_program::id())?;
640        Ok(Self {
641            payer,
642            global,
643            _system_program,
644        })
645    }
646}
647
648/// Global deposit
649pub(crate) struct GlobalDepositContext<'a, 'info> {
650    pub payer: Signer<'a, 'info>,
651    pub global: ManifestAccountInfo<'a, 'info, GlobalFixed>,
652    pub mint: MintAccountInfo<'a, 'info>,
653    pub global_vault: TokenAccountInfo<'a, 'info>,
654    pub trader_token: TokenAccountInfo<'a, 'info>,
655    pub token_program: TokenProgram<'a, 'info>,
656}
657
658impl<'a, 'info> GlobalDepositContext<'a, 'info> {
659    pub fn load(accounts: &'a [AccountInfo<'info>]) -> Result<Self, ProgramError> {
660        let account_iter: &mut Iter<AccountInfo<'info>> = &mut accounts.iter();
661
662        let payer: Signer = Signer::new(next_account_info(account_iter)?)?;
663        let global: ManifestAccountInfo<GlobalFixed> =
664            ManifestAccountInfo::<GlobalFixed>::new(next_account_info(account_iter)?)?;
665
666        let mint: MintAccountInfo = MintAccountInfo::new(next_account_info(account_iter)?)?;
667
668        let global_data: Ref<&mut [u8]> = global.data.borrow();
669        let global_fixed: &GlobalFixed = get_helper::<GlobalFixed>(&global_data, 0_u32);
670        let expected_global_vault_address: &Pubkey = global_fixed.get_vault();
671
672        let global_vault: TokenAccountInfo = TokenAccountInfo::new_with_owner_and_key(
673            next_account_info(account_iter)?,
674            mint.info.key,
675            &expected_global_vault_address,
676            &expected_global_vault_address,
677        )?;
678        drop(global_data);
679
680        let token_account_info: &AccountInfo<'info> = next_account_info(account_iter)?;
681        let trader_token: TokenAccountInfo =
682            TokenAccountInfo::new_with_owner(token_account_info, mint.info.key, payer.key)?;
683        let token_program: TokenProgram = TokenProgram::new(next_account_info(account_iter)?)?;
684        Ok(Self {
685            payer,
686            global,
687            mint,
688            global_vault,
689            trader_token,
690            token_program,
691        })
692    }
693}
694
695/// Global withdraw
696pub(crate) struct GlobalWithdrawContext<'a, 'info> {
697    pub payer: Signer<'a, 'info>,
698    pub global: ManifestAccountInfo<'a, 'info, GlobalFixed>,
699    pub mint: MintAccountInfo<'a, 'info>,
700    pub global_vault: TokenAccountInfo<'a, 'info>,
701    pub trader_token: TokenAccountInfo<'a, 'info>,
702    pub token_program: TokenProgram<'a, 'info>,
703}
704
705impl<'a, 'info> GlobalWithdrawContext<'a, 'info> {
706    pub fn load(accounts: &'a [AccountInfo<'info>]) -> Result<Self, ProgramError> {
707        let account_iter: &mut Iter<AccountInfo<'info>> = &mut accounts.iter();
708
709        let payer: Signer = Signer::new(next_account_info(account_iter)?)?;
710        let global: ManifestAccountInfo<GlobalFixed> =
711            ManifestAccountInfo::<GlobalFixed>::new(next_account_info(account_iter)?)?;
712
713        let mint: MintAccountInfo = MintAccountInfo::new(next_account_info(account_iter)?)?;
714
715        let global_data: Ref<&mut [u8]> = global.data.borrow();
716        let global_fixed: &GlobalFixed = get_helper::<GlobalFixed>(&global_data, 0_u32);
717        let expected_global_vault_address: &Pubkey = global_fixed.get_vault();
718
719        let global_vault: TokenAccountInfo = TokenAccountInfo::new_with_owner_and_key(
720            next_account_info(account_iter)?,
721            mint.info.key,
722            &expected_global_vault_address,
723            &expected_global_vault_address,
724        )?;
725        drop(global_data);
726
727        let token_account_info: &AccountInfo<'info> = next_account_info(account_iter)?;
728        let trader_token: TokenAccountInfo =
729            TokenAccountInfo::new_with_owner(token_account_info, mint.info.key, payer.key)?;
730        let token_program: TokenProgram = TokenProgram::new(next_account_info(account_iter)?)?;
731        Ok(Self {
732            payer,
733            global,
734            mint,
735            global_vault,
736            trader_token,
737            token_program,
738        })
739    }
740}
741
742/// Global evict
743pub(crate) struct GlobalEvictContext<'a, 'info> {
744    pub payer: Signer<'a, 'info>,
745    pub global: ManifestAccountInfo<'a, 'info, GlobalFixed>,
746    pub mint: MintAccountInfo<'a, 'info>,
747    pub global_vault: TokenAccountInfo<'a, 'info>,
748    pub trader_token: TokenAccountInfo<'a, 'info>,
749    pub evictee_token: TokenAccountInfo<'a, 'info>,
750    pub token_program: TokenProgram<'a, 'info>,
751}
752
753impl<'a, 'info> GlobalEvictContext<'a, 'info> {
754    pub fn load(accounts: &'a [AccountInfo<'info>]) -> Result<Self, ProgramError> {
755        let account_iter: &mut Iter<AccountInfo<'info>> = &mut accounts.iter();
756
757        let payer: Signer = Signer::new_payer(next_account_info(account_iter)?)?;
758        let global: ManifestAccountInfo<GlobalFixed> =
759            ManifestAccountInfo::<GlobalFixed>::new(next_account_info(account_iter)?)?;
760
761        let mint: MintAccountInfo = MintAccountInfo::new(next_account_info(account_iter)?)?;
762
763        let global_data: Ref<&mut [u8]> = global.data.borrow();
764        let global_fixed: &GlobalFixed = get_helper::<GlobalFixed>(&global_data, 0_u32);
765        let expected_global_vault_address: &Pubkey = global_fixed.get_vault();
766
767        let global_vault: TokenAccountInfo = TokenAccountInfo::new_with_owner_and_key(
768            next_account_info(account_iter)?,
769            mint.info.key,
770            &expected_global_vault_address,
771            &expected_global_vault_address,
772        )?;
773        drop(global_data);
774
775        let token_account_info: &AccountInfo<'info> = next_account_info(account_iter)?;
776        let trader_token: TokenAccountInfo =
777            TokenAccountInfo::new_with_owner(token_account_info, mint.info.key, payer.key)?;
778        let token_account_info: &AccountInfo<'info> = next_account_info(account_iter)?;
779        let evictee_token: TokenAccountInfo =
780            TokenAccountInfo::new(token_account_info, mint.info.key)?;
781        let token_program: TokenProgram = TokenProgram::new(next_account_info(account_iter)?)?;
782        Ok(Self {
783            payer,
784            global,
785            mint,
786            global_vault,
787            trader_token,
788            evictee_token,
789            token_program,
790        })
791    }
792}
793
794/// Global clean
795pub(crate) struct GlobalCleanContext<'a, 'info> {
796    pub payer: Signer<'a, 'info>,
797    pub market: ManifestAccountInfo<'a, 'info, MarketFixed>,
798    pub system_program: Program<'a, 'info>,
799    pub global: ManifestAccountInfo<'a, 'info, GlobalFixed>,
800}
801
802impl<'a, 'info> GlobalCleanContext<'a, 'info> {
803    pub fn load(accounts: &'a [AccountInfo<'info>]) -> Result<Self, ProgramError> {
804        let account_iter: &mut Iter<AccountInfo<'info>> = &mut accounts.iter();
805
806        let payer: Signer = Signer::new_payer(next_account_info(account_iter)?)?;
807        let market: ManifestAccountInfo<MarketFixed> =
808            ManifestAccountInfo::<MarketFixed>::new(next_account_info(account_iter)?)?;
809        let system_program: Program =
810            Program::new(next_account_info(account_iter)?, &system_program::id())?;
811        let global: ManifestAccountInfo<GlobalFixed> =
812            ManifestAccountInfo::<GlobalFixed>::new(next_account_info(account_iter)?)?;
813
814        Ok(Self {
815            payer,
816            market,
817            system_program,
818            global,
819        })
820    }
821}