1use super::instruction::MAX_SIGNERS;
4use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs};
5use num_enum::TryFromPrimitive;
6use solana_sdk::{
7 incinerator,
8 program_error::ProgramError,
9 program_option::COption,
10 program_pack::{IsInitialized, Pack, Sealed},
11 pubkey::{Pubkey, PUBKEY_BYTES},
12 system_program,
13};
14
15#[repr(C)]
17#[derive(Clone, Copy, Debug, Default, PartialEq)]
18pub struct Mint {
19 pub mint_authority: COption<Pubkey>,
23 pub supply: u64,
25 pub decimals: u8,
27 pub is_initialized: bool,
29 pub freeze_authority: COption<Pubkey>,
31}
32impl Sealed for Mint {}
33impl IsInitialized for Mint {
34 fn is_initialized(&self) -> bool {
35 self.is_initialized
36 }
37}
38impl Pack for Mint {
39 const LEN: usize = 82;
40 fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
41 let src = array_ref![src, 0, 82];
42 let (mint_authority, supply, decimals, is_initialized, freeze_authority) =
43 array_refs![src, 36, 8, 1, 1, 36];
44 let mint_authority = unpack_coption_key(mint_authority)?;
45 let supply = u64::from_le_bytes(*supply);
46 let decimals = decimals[0];
47 let is_initialized = match is_initialized {
48 [0] => false,
49 [1] => true,
50 _ => return Err(ProgramError::InvalidAccountData),
51 };
52 let freeze_authority = unpack_coption_key(freeze_authority)?;
53 Ok(Mint {
54 mint_authority,
55 supply,
56 decimals,
57 is_initialized,
58 freeze_authority,
59 })
60 }
61 fn pack_into_slice(&self, dst: &mut [u8]) {
62 let dst = array_mut_ref![dst, 0, 82];
63 let (
64 mint_authority_dst,
65 supply_dst,
66 decimals_dst,
67 is_initialized_dst,
68 freeze_authority_dst,
69 ) = mut_array_refs![dst, 36, 8, 1, 1, 36];
70 let &Mint {
71 ref mint_authority,
72 supply,
73 decimals,
74 is_initialized,
75 ref freeze_authority,
76 } = self;
77 pack_coption_key(mint_authority, mint_authority_dst);
78 *supply_dst = supply.to_le_bytes();
79 decimals_dst[0] = decimals;
80 is_initialized_dst[0] = is_initialized as u8;
81 pack_coption_key(freeze_authority, freeze_authority_dst);
82 }
83}
84
85#[repr(C)]
87#[derive(Clone, Copy, Debug, Default, PartialEq)]
88pub struct Account {
89 pub mint: Pubkey,
91 pub owner: Pubkey,
93 pub amount: u64,
95 pub delegate: COption<Pubkey>,
98 pub state: AccountState,
100 pub is_native: COption<u64>,
104 pub delegated_amount: u64,
106 pub close_authority: COption<Pubkey>,
108}
109impl Account {
110 pub fn is_frozen(&self) -> bool {
112 self.state == AccountState::Frozen
113 }
114 pub fn is_native(&self) -> bool {
116 self.is_native.is_some()
117 }
118 pub fn is_owned_by_system_program_or_incinerator(&self) -> bool {
120 system_program::check_id(&self.owner) || incinerator::check_id(&self.owner)
121 }
122}
123impl Sealed for Account {}
124impl IsInitialized for Account {
125 fn is_initialized(&self) -> bool {
126 self.state != AccountState::Uninitialized
127 }
128}
129impl Pack for Account {
130 const LEN: usize = 165;
131 fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
132 let src = array_ref![src, 0, 165];
133 let (mint, owner, amount, delegate, state, is_native, delegated_amount, close_authority) =
134 array_refs![src, 32, 32, 8, 36, 1, 12, 8, 36];
135 Ok(Account {
136 mint: Pubkey::new_from_array(*mint),
137 owner: Pubkey::new_from_array(*owner),
138 amount: u64::from_le_bytes(*amount),
139 delegate: unpack_coption_key(delegate)?,
140 state: AccountState::try_from_primitive(state[0])
141 .or(Err(ProgramError::InvalidAccountData))?,
142 is_native: unpack_coption_u64(is_native)?,
143 delegated_amount: u64::from_le_bytes(*delegated_amount),
144 close_authority: unpack_coption_key(close_authority)?,
145 })
146 }
147 fn pack_into_slice(&self, dst: &mut [u8]) {
148 let dst = array_mut_ref![dst, 0, 165];
149 let (
150 mint_dst,
151 owner_dst,
152 amount_dst,
153 delegate_dst,
154 state_dst,
155 is_native_dst,
156 delegated_amount_dst,
157 close_authority_dst,
158 ) = mut_array_refs![dst, 32, 32, 8, 36, 1, 12, 8, 36];
159 let &Account {
160 ref mint,
161 ref owner,
162 amount,
163 ref delegate,
164 state,
165 ref is_native,
166 delegated_amount,
167 ref close_authority,
168 } = self;
169 mint_dst.copy_from_slice(mint.as_ref());
170 owner_dst.copy_from_slice(owner.as_ref());
171 *amount_dst = amount.to_le_bytes();
172 pack_coption_key(delegate, delegate_dst);
173 state_dst[0] = state as u8;
174 pack_coption_u64(is_native, is_native_dst);
175 *delegated_amount_dst = delegated_amount.to_le_bytes();
176 pack_coption_key(close_authority, close_authority_dst);
177 }
178}
179
180#[repr(u8)]
182#[derive(Clone, Copy, Debug, PartialEq, Default, TryFromPrimitive)]
183pub enum AccountState {
184 #[default]
186 Uninitialized,
187 Initialized,
190 Frozen,
193}
194
195#[repr(C)]
197#[derive(Clone, Copy, Debug, Default, PartialEq)]
198pub struct Multisig {
199 pub m: u8,
201 pub n: u8,
203 pub is_initialized: bool,
205 pub signers: [Pubkey; MAX_SIGNERS],
207}
208impl Sealed for Multisig {}
209impl IsInitialized for Multisig {
210 fn is_initialized(&self) -> bool {
211 self.is_initialized
212 }
213}
214impl Pack for Multisig {
215 const LEN: usize = 355;
216 fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
217 let src = array_ref![src, 0, 355];
218 #[allow(clippy::ptr_offset_with_cast)]
219 let (m, n, is_initialized, signers_flat) = array_refs![src, 1, 1, 1, 32 * MAX_SIGNERS];
220 let mut result = Multisig {
221 m: m[0],
222 n: n[0],
223 is_initialized: match is_initialized {
224 [0] => false,
225 [1] => true,
226 _ => return Err(ProgramError::InvalidAccountData),
227 },
228 signers: [Pubkey::new_from_array([0u8; 32]); MAX_SIGNERS],
229 };
230 for (src, dst) in signers_flat.chunks(32).zip(result.signers.iter_mut()) {
231 *dst = Pubkey::try_from(src).unwrap();
232 }
233 Ok(result)
234 }
235 fn pack_into_slice(&self, dst: &mut [u8]) {
236 let dst = array_mut_ref![dst, 0, 355];
237 #[allow(clippy::ptr_offset_with_cast)]
238 let (m, n, is_initialized, signers_flat) = mut_array_refs![dst, 1, 1, 1, 32 * MAX_SIGNERS];
239 *m = [self.m];
240 *n = [self.n];
241 *is_initialized = [self.is_initialized as u8];
242 for (i, src) in self.signers.iter().enumerate() {
243 let dst_array = array_mut_ref![signers_flat, 32 * i, 32];
244 dst_array.copy_from_slice(src.as_ref());
245 }
246 }
247}
248
249fn pack_coption_key(src: &COption<Pubkey>, dst: &mut [u8; 36]) {
251 let (tag, body) = mut_array_refs![dst, 4, 32];
252 match src {
253 COption::Some(key) => {
254 *tag = [1, 0, 0, 0];
255 body.copy_from_slice(key.as_ref());
256 }
257 COption::None => {
258 *tag = [0; 4];
259 }
260 }
261}
262fn unpack_coption_key(src: &[u8; 36]) -> Result<COption<Pubkey>, ProgramError> {
263 let (tag, body) = array_refs![src, 4, 32];
264 match *tag {
265 [0, 0, 0, 0] => Ok(COption::None),
266 [1, 0, 0, 0] => Ok(COption::Some(Pubkey::new_from_array(*body))),
267 _ => Err(ProgramError::InvalidAccountData),
268 }
269}
270fn pack_coption_u64(src: &COption<u64>, dst: &mut [u8; 12]) {
271 let (tag, body) = mut_array_refs![dst, 4, 8];
272 match src {
273 COption::Some(amount) => {
274 *tag = [1, 0, 0, 0];
275 *body = amount.to_le_bytes();
276 }
277 COption::None => {
278 *tag = [0; 4];
279 }
280 }
281}
282fn unpack_coption_u64(src: &[u8; 12]) -> Result<COption<u64>, ProgramError> {
283 let (tag, body) = array_refs![src, 4, 8];
284 match *tag {
285 [0, 0, 0, 0] => Ok(COption::None),
286 [1, 0, 0, 0] => Ok(COption::Some(u64::from_le_bytes(*body))),
287 _ => Err(ProgramError::InvalidAccountData),
288 }
289}
290
291const SPL_TOKEN_ACCOUNT_MINT_OFFSET: usize = 0;
292const SPL_TOKEN_ACCOUNT_OWNER_OFFSET: usize = 32;
293
294pub trait GenericTokenAccount {
297 fn valid_account_data(account_data: &[u8]) -> bool;
299
300 fn unpack_account_owner_unchecked(account_data: &[u8]) -> &Pubkey {
302 Self::unpack_pubkey_unchecked(account_data, SPL_TOKEN_ACCOUNT_OWNER_OFFSET)
303 }
304
305 fn unpack_account_mint_unchecked(account_data: &[u8]) -> &Pubkey {
307 Self::unpack_pubkey_unchecked(account_data, SPL_TOKEN_ACCOUNT_MINT_OFFSET)
308 }
309
310 fn unpack_pubkey_unchecked(account_data: &[u8], offset: usize) -> &Pubkey {
313 bytemuck::from_bytes(&account_data[offset..offset + PUBKEY_BYTES])
314 }
315
316 fn unpack_account_owner(account_data: &[u8]) -> Option<&Pubkey> {
318 if Self::valid_account_data(account_data) {
319 Some(Self::unpack_account_owner_unchecked(account_data))
320 } else {
321 None
322 }
323 }
324
325 fn unpack_account_mint(account_data: &[u8]) -> Option<&Pubkey> {
327 if Self::valid_account_data(account_data) {
328 Some(Self::unpack_account_mint_unchecked(account_data))
329 } else {
330 None
331 }
332 }
333}
334
335pub const ACCOUNT_INITIALIZED_INDEX: usize = 108;
337
338pub fn is_initialized_account(account_data: &[u8]) -> bool {
341 *account_data
342 .get(ACCOUNT_INITIALIZED_INDEX)
343 .unwrap_or(&(AccountState::Uninitialized as u8))
344 != AccountState::Uninitialized as u8
345}
346
347impl GenericTokenAccount for Account {
348 fn valid_account_data(account_data: &[u8]) -> bool {
349 account_data.len() == Account::LEN && is_initialized_account(account_data)
350 }
351}