spl_token_2022/
generic_token_account.rs

1//! Generic Token Account, copied from `spl_token::state`
2// Remove all of this and use spl-token's version once token 3.4.0 is released
3use {
4    crate::state::AccountState,
5    solana_program::pubkey::{Pubkey, PUBKEY_BYTES},
6};
7
8const SPL_TOKEN_ACCOUNT_MINT_OFFSET: usize = 0;
9const SPL_TOKEN_ACCOUNT_OWNER_OFFSET: usize = 32;
10
11/// A trait for token Account structs to enable efficiently unpacking various
12/// fields without unpacking the complete state.
13pub trait GenericTokenAccount {
14    /// Check if the account data is a valid token account
15    fn valid_account_data(account_data: &[u8]) -> bool;
16
17    /// Call after account length has already been verified to unpack the
18    /// account owner
19    fn unpack_account_owner_unchecked(account_data: &[u8]) -> &Pubkey {
20        Self::unpack_pubkey_unchecked(account_data, SPL_TOKEN_ACCOUNT_OWNER_OFFSET)
21    }
22
23    /// Call after account length has already been verified to unpack the
24    /// account mint
25    fn unpack_account_mint_unchecked(account_data: &[u8]) -> &Pubkey {
26        Self::unpack_pubkey_unchecked(account_data, SPL_TOKEN_ACCOUNT_MINT_OFFSET)
27    }
28
29    /// Call after account length has already been verified to unpack a Pubkey
30    /// at the specified offset. Panics if `account_data.len()` is less than
31    /// `PUBKEY_BYTES`
32    fn unpack_pubkey_unchecked(account_data: &[u8], offset: usize) -> &Pubkey {
33        bytemuck::from_bytes(&account_data[offset..offset + PUBKEY_BYTES])
34    }
35
36    /// Unpacks an account's owner from opaque account data.
37    fn unpack_account_owner(account_data: &[u8]) -> Option<&Pubkey> {
38        if Self::valid_account_data(account_data) {
39            Some(Self::unpack_account_owner_unchecked(account_data))
40        } else {
41            None
42        }
43    }
44
45    /// Unpacks an account's mint from opaque account data.
46    fn unpack_account_mint(account_data: &[u8]) -> Option<&Pubkey> {
47        if Self::valid_account_data(account_data) {
48            Some(Self::unpack_account_mint_unchecked(account_data))
49        } else {
50            None
51        }
52    }
53}
54
55/// The offset of state field in Account's C representation
56pub const ACCOUNT_INITIALIZED_INDEX: usize = 108;
57
58/// Check if the account data buffer represents an initialized account.
59/// This is checking the `state` (`AccountState`) field of an Account object.
60pub fn is_initialized_account(account_data: &[u8]) -> bool {
61    *account_data
62        .get(ACCOUNT_INITIALIZED_INDEX)
63        .unwrap_or(&(AccountState::Uninitialized as u8))
64        != AccountState::Uninitialized as u8
65}