phoenix/program/validation/checkers/
token_checkers.rs

1use crate::program::error::assert_with_msg;
2use solana_program::{
3    account_info::AccountInfo, program_error::ProgramError, program_pack::Pack, pubkey::Pubkey,
4};
5use spl_token::state::{Account, Mint};
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        assert_with_msg(
17            info.owner == &spl_token::id(),
18            ProgramError::IllegalOwner,
19            "Mint account must be owned by the Token Program",
20        )?;
21        let mint = Mint::unpack(&info.try_borrow_data()?)?;
22
23        Ok(Self { mint, info })
24    }
25}
26
27impl<'a, 'info> AsRef<AccountInfo<'info>> for MintAccountInfo<'a, 'info> {
28    fn as_ref(&self) -> &AccountInfo<'info> {
29        self.info
30    }
31}
32
33impl<'a, 'info> Deref for MintAccountInfo<'a, 'info> {
34    type Target = Mint;
35
36    fn deref(&self) -> &Self::Target {
37        &self.mint
38    }
39}
40
41#[derive(Clone)]
42pub struct TokenAccountInfo<'a, 'info> {
43    pub info: &'a AccountInfo<'info>,
44}
45
46impl<'a, 'info> TokenAccountInfo<'a, 'info> {
47    pub fn new(
48        info: &'a AccountInfo<'info>,
49        mint: &Pubkey,
50    ) -> Result<TokenAccountInfo<'a, 'info>, ProgramError> {
51        assert_with_msg(
52            info.owner == &spl_token::id(),
53            ProgramError::IllegalOwner,
54            "Token account must be owned by the Token Program",
55        )?;
56        assert_with_msg(
57            info.data_len() == Account::LEN,
58            ProgramError::InvalidAccountData,
59            "Token account data length must be 165 bytes",
60        )?;
61        // The mint key is found at offset 0 of the token account
62        assert_with_msg(
63            &info.try_borrow_data()?[0..32] == mint.as_ref(),
64            ProgramError::InvalidAccountData,
65            "Token account mint mismatch",
66        )?;
67        Ok(Self { info })
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        assert_with_msg(
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        assert_with_msg(
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> TokenAccountInfo<'a, 'info> {
101    /// Function copied from https://github.com/coral-xyz/anchor/blob/master/spl/src/token.rs#L534
102    pub fn amount(&self) -> Result<u64, ProgramError> {
103        let bytes = self.info.try_borrow_data()?;
104        let mut amount_bytes = [0u8; 8];
105        amount_bytes.copy_from_slice(&bytes[64..72]);
106        Ok(u64::from_le_bytes(amount_bytes))
107    }
108}
109
110impl<'a, 'info> AsRef<AccountInfo<'info>> for TokenAccountInfo<'a, 'info> {
111    fn as_ref(&self) -> &AccountInfo<'info> {
112        self.info
113    }
114}
115
116impl<'a, 'info> Deref for TokenAccountInfo<'a, 'info> {
117    type Target = AccountInfo<'info>;
118
119    fn deref(&self) -> &Self::Target {
120        self.info
121    }
122}