spl_generic_token/
token.rs

1//! Partial SPL Token declarations to avoid a dependency on the spl-token crate.
2
3use {
4    solana_pubkey::{Pubkey, PUBKEY_BYTES},
5    std::mem,
6};
7
8solana_pubkey::declare_id!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
9
10/*
11    spl_token::state::Account {
12        mint: Pubkey,
13        owner: Pubkey,
14        amount: u64,
15        delegate: COption<Pubkey>,
16        state: AccountState,
17        is_native: COption<u64>,
18        delegated_amount: u64,
19        close_authority: COption<Pubkey>,
20    }
21*/
22pub const SPL_TOKEN_ACCOUNT_MINT_OFFSET: usize = 0;
23pub const SPL_TOKEN_ACCOUNT_OWNER_OFFSET: usize = 32;
24const SPL_TOKEN_ACCOUNT_AMOUNT_OFFSET: usize = 64;
25const SPL_TOKEN_ACCOUNT_STATE_OFFSET: usize = 108;
26pub(crate) const SPL_TOKEN_ACCOUNT_LENGTH: usize = 165;
27
28/*
29    spl_token::state::Mint {
30        mint_authority: COption<Pubkey>,
31        supply: u64,
32        decimals: u8,
33        is_initialized: bool,
34        freeze_authority: COption<Pubkey>,
35    }
36*/
37const SPL_TOKEN_MINT_SUPPLY_OFFSET: usize = 36;
38const SPL_TOKEN_MINT_DECIMALS_OFFSET: usize = 44;
39const SPL_TOKEN_MINT_IS_INITIALIZED_OFFSET: usize = 45;
40pub(crate) const SPL_TOKEN_MINT_LENGTH: usize = 82;
41
42pub(crate) fn is_initialized_account(account_data: &[u8]) -> bool {
43    is_initialized_token_data(account_data, SPL_TOKEN_ACCOUNT_STATE_OFFSET)
44}
45
46pub(crate) fn is_initialized_mint(account_data: &[u8]) -> bool {
47    is_initialized_token_data(account_data, SPL_TOKEN_MINT_IS_INITIALIZED_OFFSET)
48}
49
50fn is_initialized_token_data(account_data: &[u8], offset: usize) -> bool {
51    *account_data.get(offset).unwrap_or(&0) != 0
52}
53
54macro_rules! define_checked_getter {
55    ($checked_fn:ident, $unchecked_fn:ident, $typ:ty) => {
56        fn $checked_fn(account_data: &[u8]) -> Option<$typ> {
57            if Self::valid_account_data(account_data) {
58                Some(Self::$unchecked_fn(account_data))
59            } else {
60                None
61            }
62        }
63    };
64}
65
66// necessary to forgo bytemuck to treat endianness correctly on BE systems
67fn unpack_u64_unchecked(account_data: &[u8], offset: usize) -> u64 {
68    let mut bytes = [0u8; 8];
69    bytes.copy_from_slice(&account_data[offset..offset.wrapping_add(mem::size_of::<u64>())]);
70    u64::from_le_bytes(bytes)
71}
72
73// Trait for retrieving mint address, owner, and amount from any token account-like buffer.
74// A token program that copies the spl_token layout need only impl `valid_account_data()`.
75pub trait GenericTokenAccount {
76    fn valid_account_data(account_data: &[u8]) -> bool;
77
78    define_checked_getter!(unpack_account_mint, unpack_account_mint_unchecked, &Pubkey);
79    define_checked_getter!(
80        unpack_account_owner,
81        unpack_account_owner_unchecked,
82        &Pubkey
83    );
84    define_checked_getter!(unpack_account_amount, unpack_account_amount_unchecked, u64);
85
86    // Call after account length has already been verified
87    fn unpack_account_mint_unchecked(account_data: &[u8]) -> &Pubkey {
88        Self::unpack_pubkey_unchecked(account_data, SPL_TOKEN_ACCOUNT_MINT_OFFSET)
89    }
90
91    // Call after account length has already been verified
92    fn unpack_account_owner_unchecked(account_data: &[u8]) -> &Pubkey {
93        Self::unpack_pubkey_unchecked(account_data, SPL_TOKEN_ACCOUNT_OWNER_OFFSET)
94    }
95
96    // Call after account length has already been verified
97    fn unpack_account_amount_unchecked(account_data: &[u8]) -> u64 {
98        unpack_u64_unchecked(account_data, SPL_TOKEN_ACCOUNT_AMOUNT_OFFSET)
99    }
100
101    // Call after account length has already been verified
102    fn unpack_pubkey_unchecked(account_data: &[u8], offset: usize) -> &Pubkey {
103        bytemuck::from_bytes(&account_data[offset..offset.wrapping_add(PUBKEY_BYTES)])
104    }
105}
106
107pub struct Account;
108impl Account {
109    pub const fn get_packed_len() -> usize {
110        SPL_TOKEN_ACCOUNT_LENGTH
111    }
112}
113
114impl GenericTokenAccount for Account {
115    fn valid_account_data(account_data: &[u8]) -> bool {
116        account_data.len() == SPL_TOKEN_ACCOUNT_LENGTH && is_initialized_account(account_data)
117    }
118}
119
120// Trait for retrieving supply and decimals from any token mint-like buffer.
121// A token program that copies the spl_token layout need only impl `valid_account_data()`.
122// We do not use bytemuck for this because Mint is an unaligned struct.
123pub trait GenericTokenMint {
124    fn valid_account_data(account_data: &[u8]) -> bool;
125
126    define_checked_getter!(unpack_mint_supply, unpack_mint_supply_unchecked, u64);
127    define_checked_getter!(unpack_mint_decimals, unpack_mint_decimals_unchecked, u8);
128
129    // Call after account length has already been verified
130    fn unpack_mint_supply_unchecked(account_data: &[u8]) -> u64 {
131        unpack_u64_unchecked(account_data, SPL_TOKEN_MINT_SUPPLY_OFFSET)
132    }
133
134    // Call after account length has already been verified
135    fn unpack_mint_decimals_unchecked(account_data: &[u8]) -> u8 {
136        account_data[SPL_TOKEN_MINT_DECIMALS_OFFSET]
137    }
138}
139
140pub struct Mint;
141impl Mint {
142    pub const fn get_packed_len() -> usize {
143        SPL_TOKEN_MINT_LENGTH
144    }
145}
146
147impl GenericTokenMint for Mint {
148    fn valid_account_data(account_data: &[u8]) -> bool {
149        account_data.len() == SPL_TOKEN_MINT_LENGTH && is_initialized_mint(account_data)
150    }
151}
152
153pub mod native_mint {
154    solana_pubkey::declare_id!("So11111111111111111111111111111111111111112");
155
156    /*
157        spl_token::state::Mint {
158            mint_authority: COption::None,
159            supply: 0,
160            decimals: 9,
161            is_initialized: true,
162            freeze_authority: COption::None,
163        }
164    */
165    pub const ACCOUNT_DATA: [u8; 82] = [
166        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
167        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
168        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
169    ];
170}