manifest/validation/
token_checkers.rs

1use crate::require;
2use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey};
3use spl_token_2022::{
4    check_spl_token_program_account, extension::StateWithExtensions, state::Mint,
5};
6use std::ops::Deref;
7
8#[derive(Clone)]
9pub struct MintAccountInfo<'a, 'info> {
10    pub mint: Mint,
11    pub info: &'a AccountInfo<'info>,
12}
13
14impl<'a, 'info> MintAccountInfo<'a, 'info> {
15    pub fn new(info: &'a AccountInfo<'info>) -> Result<MintAccountInfo<'a, 'info>, ProgramError> {
16        check_spl_token_program_account(info.owner)?;
17
18        let mint: Mint = StateWithExtensions::<Mint>::unpack(&info.data.borrow())?.base;
19
20        Ok(Self { mint, info })
21    }
22}
23
24impl<'a, 'info> AsRef<AccountInfo<'info>> for MintAccountInfo<'a, 'info> {
25    fn as_ref(&self) -> &AccountInfo<'info> {
26        self.info
27    }
28}
29
30#[derive(Clone)]
31pub struct TokenAccountInfo<'a, 'info> {
32    pub info: &'a AccountInfo<'info>,
33}
34
35impl<'a, 'info> TokenAccountInfo<'a, 'info> {
36    pub fn new(
37        info: &'a AccountInfo<'info>,
38        mint: &Pubkey,
39    ) -> Result<TokenAccountInfo<'a, 'info>, ProgramError> {
40        require!(
41            info.owner == &spl_token::id() || info.owner == &spl_token_2022::id(),
42            ProgramError::IllegalOwner,
43            "Token account must be owned by the Token Program",
44        )?;
45        // The mint key is found at offset 0 of the token account
46        require!(
47            &info.try_borrow_data()?[0..32] == mint.as_ref(),
48            ProgramError::InvalidAccountData,
49            "Token account mint mismatch",
50        )?;
51        Ok(Self { info })
52    }
53
54    pub fn get_owner(&self) -> Pubkey {
55        Pubkey::new_from_array(
56            self.info.try_borrow_data().unwrap()[32..64]
57                .try_into()
58                .unwrap(),
59        )
60    }
61
62    pub fn get_balance_atoms(&self) -> u64 {
63        u64::from_le_bytes(
64            self.info.try_borrow_data().unwrap()[64..72]
65                .try_into()
66                .unwrap(),
67        )
68    }
69
70    pub fn new_with_owner(
71        info: &'a AccountInfo<'info>,
72        mint: &Pubkey,
73        owner: &Pubkey,
74    ) -> Result<TokenAccountInfo<'a, 'info>, ProgramError> {
75        let token_account_info = Self::new(info, mint)?;
76        // The owner key is found at offset 32 of the token account
77        require!(
78            &info.try_borrow_data()?[32..64] == owner.as_ref(),
79            ProgramError::IllegalOwner,
80            "Token account owner mismatch",
81        )?;
82        Ok(token_account_info)
83    }
84
85    pub fn new_with_owner_and_key(
86        info: &'a AccountInfo<'info>,
87        mint: &Pubkey,
88        owner: &Pubkey,
89        key: &Pubkey,
90    ) -> Result<TokenAccountInfo<'a, 'info>, ProgramError> {
91        require!(
92            info.key == key,
93            ProgramError::InvalidInstructionData,
94            "Invalid pubkey for Token Account",
95        )?;
96        Self::new_with_owner(info, mint, owner)
97    }
98}
99
100impl<'a, 'info> AsRef<AccountInfo<'info>> for TokenAccountInfo<'a, 'info> {
101    fn as_ref(&self) -> &AccountInfo<'info> {
102        self.info
103    }
104}
105
106impl<'a, 'info> Deref for TokenAccountInfo<'a, 'info> {
107    type Target = AccountInfo<'info>;
108
109    fn deref(&self) -> &Self::Target {
110        self.info
111    }
112}
113
114#[macro_export]
115macro_rules! market_vault_seeds {
116    ( $market:expr, $mint:expr ) => {
117        &[b"vault", $market.as_ref(), $mint.as_ref()]
118    };
119}
120
121#[macro_export]
122macro_rules! market_vault_seeds_with_bump {
123    ( $market:expr, $mint:expr, $bump:expr ) => {
124        &[&[b"vault", $market.as_ref(), $mint.as_ref(), &[$bump]]]
125    };
126}
127
128#[macro_export]
129macro_rules! global_vault_seeds {
130    ( $mint:expr ) => {
131        &[b"global-vault", $mint.as_ref()]
132    };
133}
134
135#[macro_export]
136macro_rules! global_vault_seeds_with_bump {
137    ( $mint:expr, $bump:expr ) => {
138        &[&[b"global-vault", $mint.as_ref(), &[$bump]]]
139    };
140}
141
142pub fn get_vault_address(market: &Pubkey, mint: &Pubkey) -> (Pubkey, u8) {
143    Pubkey::find_program_address(market_vault_seeds!(market, mint), &crate::ID)
144}
145
146pub fn get_global_vault_address(mint: &Pubkey) -> (Pubkey, u8) {
147    Pubkey::find_program_address(global_vault_seeds!(mint), &crate::ID)
148}