1use {
4 crate::program::{
5 spl_token,
6 spl_token_2022::{
7 extension::AccountType,
8 generic_token_account::{is_initialized_account, GenericTokenAccount},
9 instruction::MAX_SIGNERS,
10 },
11 },
12 arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs},
13 num_enum::{IntoPrimitive, TryFromPrimitive},
14 solana_sdk::{
15 incinerator,
16 program_error::ProgramError,
17 program_option::COption,
18 program_pack::{IsInitialized, Pack, Sealed},
19 pubkey::Pubkey,
20 system_program,
21 },
22};
23
24#[repr(C)]
26#[derive(Clone, Copy, Debug, Default, PartialEq)]
27pub struct Mint {
28 pub mint_authority: COption<Pubkey>,
32 pub supply: u64,
34 pub decimals: u8,
36 pub is_initialized: bool,
38 pub freeze_authority: COption<Pubkey>,
40}
41impl Sealed for Mint {}
42impl IsInitialized for Mint {
43 fn is_initialized(&self) -> bool {
44 self.is_initialized
45 }
46}
47impl Pack for Mint {
48 const LEN: usize = 82;
49 fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
50 let src = array_ref![src, 0, 82];
51 let (mint_authority, supply, decimals, is_initialized, freeze_authority) =
52 array_refs![src, 36, 8, 1, 1, 36];
53 let mint_authority = unpack_coption_key(mint_authority)?;
54 let supply = u64::from_le_bytes(*supply);
55 let decimals = decimals[0];
56 let is_initialized = match is_initialized {
57 [0] => false,
58 [1] => true,
59 _ => return Err(ProgramError::InvalidAccountData),
60 };
61 let freeze_authority = unpack_coption_key(freeze_authority)?;
62 Ok(Mint {
63 mint_authority,
64 supply,
65 decimals,
66 is_initialized,
67 freeze_authority,
68 })
69 }
70 fn pack_into_slice(&self, dst: &mut [u8]) {
71 let dst = array_mut_ref![dst, 0, 82];
72 let (
73 mint_authority_dst,
74 supply_dst,
75 decimals_dst,
76 is_initialized_dst,
77 freeze_authority_dst,
78 ) = mut_array_refs![dst, 36, 8, 1, 1, 36];
79 let &Mint {
80 ref mint_authority,
81 supply,
82 decimals,
83 is_initialized,
84 ref freeze_authority,
85 } = self;
86 pack_coption_key(mint_authority, mint_authority_dst);
87 *supply_dst = supply.to_le_bytes();
88 decimals_dst[0] = decimals;
89 is_initialized_dst[0] = is_initialized as u8;
90 pack_coption_key(freeze_authority, freeze_authority_dst);
91 }
92}
93
94#[repr(C)]
96#[derive(Clone, Copy, Debug, Default, PartialEq)]
97pub struct Account {
98 pub mint: Pubkey,
100 pub owner: Pubkey,
102 pub amount: u64,
104 pub delegate: COption<Pubkey>,
107 pub state: AccountState,
109 pub is_native: COption<u64>,
113 pub delegated_amount: u64,
115 pub close_authority: COption<Pubkey>,
117}
118impl Account {
119 pub fn is_frozen(&self) -> bool {
121 self.state == AccountState::Frozen
122 }
123 pub fn is_native(&self) -> bool {
125 self.is_native.is_some()
126 }
127 pub fn is_owned_by_system_program_or_incinerator(&self) -> bool {
129 system_program::check_id(&self.owner) || incinerator::check_id(&self.owner)
130 }
131}
132impl Sealed for Account {}
133impl IsInitialized for Account {
134 fn is_initialized(&self) -> bool {
135 self.state != AccountState::Uninitialized
136 }
137}
138impl Pack for Account {
139 const LEN: usize = 165;
140 fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
141 let src = array_ref![src, 0, 165];
142 let (mint, owner, amount, delegate, state, is_native, delegated_amount, close_authority) =
143 array_refs![src, 32, 32, 8, 36, 1, 12, 8, 36];
144 Ok(Account {
145 mint: Pubkey::new_from_array(*mint),
146 owner: Pubkey::new_from_array(*owner),
147 amount: u64::from_le_bytes(*amount),
148 delegate: unpack_coption_key(delegate)?,
149 state: AccountState::try_from_primitive(state[0])
150 .or(Err(ProgramError::InvalidAccountData))?,
151 is_native: unpack_coption_u64(is_native)?,
152 delegated_amount: u64::from_le_bytes(*delegated_amount),
153 close_authority: unpack_coption_key(close_authority)?,
154 })
155 }
156 fn pack_into_slice(&self, dst: &mut [u8]) {
157 let dst = array_mut_ref![dst, 0, 165];
158 let (
159 mint_dst,
160 owner_dst,
161 amount_dst,
162 delegate_dst,
163 state_dst,
164 is_native_dst,
165 delegated_amount_dst,
166 close_authority_dst,
167 ) = mut_array_refs![dst, 32, 32, 8, 36, 1, 12, 8, 36];
168 let &Account {
169 ref mint,
170 ref owner,
171 amount,
172 ref delegate,
173 state,
174 ref is_native,
175 delegated_amount,
176 ref close_authority,
177 } = self;
178 mint_dst.copy_from_slice(mint.as_ref());
179 owner_dst.copy_from_slice(owner.as_ref());
180 *amount_dst = amount.to_le_bytes();
181 pack_coption_key(delegate, delegate_dst);
182 state_dst[0] = state as u8;
183 pack_coption_u64(is_native, is_native_dst);
184 *delegated_amount_dst = delegated_amount.to_le_bytes();
185 pack_coption_key(close_authority, close_authority_dst);
186 }
187}
188
189#[repr(u8)]
191#[derive(Clone, Copy, Debug, PartialEq, Default, IntoPrimitive, TryFromPrimitive)]
192pub enum AccountState {
193 #[default]
195 Uninitialized,
196 Initialized,
199 Frozen,
202}
203
204#[repr(C)]
206#[derive(Clone, Copy, Debug, Default, PartialEq)]
207pub struct Multisig {
208 pub m: u8,
210 pub n: u8,
212 pub is_initialized: bool,
214 pub signers: [Pubkey; MAX_SIGNERS],
216}
217impl Sealed for Multisig {}
218impl IsInitialized for Multisig {
219 fn is_initialized(&self) -> bool {
220 self.is_initialized
221 }
222}
223impl Pack for Multisig {
224 const LEN: usize = 355;
225 fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
226 let src = array_ref![src, 0, 355];
227 #[allow(clippy::ptr_offset_with_cast)]
228 let (m, n, is_initialized, signers_flat) = array_refs![src, 1, 1, 1, 32 * MAX_SIGNERS];
229 let mut result = Multisig {
230 m: m[0],
231 n: n[0],
232 is_initialized: match is_initialized {
233 [0] => false,
234 [1] => true,
235 _ => return Err(ProgramError::InvalidAccountData),
236 },
237 signers: [Pubkey::new_from_array([0u8; 32]); MAX_SIGNERS],
238 };
239 for (src, dst) in signers_flat.chunks(32).zip(result.signers.iter_mut()) {
240 *dst = Pubkey::try_from(src).unwrap();
241 }
242 Ok(result)
243 }
244 fn pack_into_slice(&self, dst: &mut [u8]) {
245 let dst = array_mut_ref![dst, 0, 355];
246 #[allow(clippy::ptr_offset_with_cast)]
247 let (m, n, is_initialized, signers_flat) = mut_array_refs![dst, 1, 1, 1, 32 * MAX_SIGNERS];
248 *m = [self.m];
249 *n = [self.n];
250 *is_initialized = [self.is_initialized as u8];
251 for (i, src) in self.signers.iter().enumerate() {
252 let dst_array = array_mut_ref![signers_flat, 32 * i, 32];
253 dst_array.copy_from_slice(src.as_ref());
254 }
255 }
256}
257
258pub(crate) fn pack_coption_key(src: &COption<Pubkey>, dst: &mut [u8; 36]) {
260 let (tag, body) = mut_array_refs![dst, 4, 32];
261 match src {
262 COption::Some(key) => {
263 *tag = [1, 0, 0, 0];
264 body.copy_from_slice(key.as_ref());
265 }
266 COption::None => {
267 *tag = [0; 4];
268 }
269 }
270}
271pub(crate) fn unpack_coption_key(src: &[u8; 36]) -> Result<COption<Pubkey>, ProgramError> {
272 let (tag, body) = array_refs![src, 4, 32];
273 match *tag {
274 [0, 0, 0, 0] => Ok(COption::None),
275 [1, 0, 0, 0] => Ok(COption::Some(Pubkey::new_from_array(*body))),
276 _ => Err(ProgramError::InvalidAccountData),
277 }
278}
279fn pack_coption_u64(src: &COption<u64>, dst: &mut [u8; 12]) {
280 let (tag, body) = mut_array_refs![dst, 4, 8];
281 match src {
282 COption::Some(amount) => {
283 *tag = [1, 0, 0, 0];
284 *body = amount.to_le_bytes();
285 }
286 COption::None => {
287 *tag = [0; 4];
288 }
289 }
290}
291fn unpack_coption_u64(src: &[u8; 12]) -> Result<COption<u64>, ProgramError> {
292 let (tag, body) = array_refs![src, 4, 8];
293 match *tag {
294 [0, 0, 0, 0] => Ok(COption::None),
295 [1, 0, 0, 0] => Ok(COption::Some(u64::from_le_bytes(*body))),
296 _ => Err(ProgramError::InvalidAccountData),
297 }
298}
299
300const ACCOUNTTYPE_ACCOUNT: u8 = AccountType::Account as u8;
302impl GenericTokenAccount for Account {
303 fn valid_account_data(account_data: &[u8]) -> bool {
304 account_data.len() == Account::LEN && is_initialized_account(account_data)
306 || (account_data.len() >= Account::LEN
307 && account_data.len() != Multisig::LEN
308 && ACCOUNTTYPE_ACCOUNT
309 == *account_data
310 .get(spl_token::state::Account::get_packed_len())
311 .unwrap_or(&(AccountType::Uninitialized as u8))
312 && is_initialized_account(account_data))
313 }
314}