1use {
4 solana_pubkey::{Pubkey, PUBKEY_BYTES},
5 std::mem,
6};
7
8solana_pubkey::declare_id!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
9
10pub 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
28const 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
66fn 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
73pub 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 fn unpack_account_mint_unchecked(account_data: &[u8]) -> &Pubkey {
88 Self::unpack_pubkey_unchecked(account_data, SPL_TOKEN_ACCOUNT_MINT_OFFSET)
89 }
90
91 fn unpack_account_owner_unchecked(account_data: &[u8]) -> &Pubkey {
93 Self::unpack_pubkey_unchecked(account_data, SPL_TOKEN_ACCOUNT_OWNER_OFFSET)
94 }
95
96 fn unpack_account_amount_unchecked(account_data: &[u8]) -> u64 {
98 unpack_u64_unchecked(account_data, SPL_TOKEN_ACCOUNT_AMOUNT_OFFSET)
99 }
100
101 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
120pub 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 fn unpack_mint_supply_unchecked(account_data: &[u8]) -> u64 {
131 unpack_u64_unchecked(account_data, SPL_TOKEN_MINT_SUPPLY_OFFSET)
132 }
133
134 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 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}