1use {
4 crate::{
5 extension::AccountType,
6 generic_token_account::{is_initialized_account, GenericTokenAccount},
7 instruction::MAX_SIGNERS,
8 },
9 arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs},
10 num_enum::{IntoPrimitive, TryFromPrimitive},
11 solana_program_error::ProgramError,
12 solana_program_option::COption,
13 solana_program_pack::{IsInitialized, Pack, Sealed},
14 solana_pubkey::Pubkey,
15};
16
17pub trait PackedSizeOf {
21 const SIZE_OF: usize;
23}
24
25#[repr(C)]
27#[derive(Clone, Copy, Debug, Default, PartialEq)]
28pub struct Mint {
29 pub mint_authority: COption<Pubkey>,
34 pub supply: u64,
36 pub decimals: u8,
38 pub is_initialized: bool,
40 pub freeze_authority: COption<Pubkey>,
42}
43impl Sealed for Mint {}
44impl IsInitialized for Mint {
45 fn is_initialized(&self) -> bool {
46 self.is_initialized
47 }
48}
49impl Pack for Mint {
50 const LEN: usize = 82;
51 fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
52 let src = array_ref![src, 0, 82];
53 let (mint_authority, supply, decimals, is_initialized, freeze_authority) =
54 array_refs![src, 36, 8, 1, 1, 36];
55 let mint_authority = unpack_coption_key(mint_authority)?;
56 let supply = u64::from_le_bytes(*supply);
57 let decimals = decimals[0];
58 let is_initialized = match is_initialized {
59 [0] => false,
60 [1] => true,
61 _ => return Err(ProgramError::InvalidAccountData),
62 };
63 let freeze_authority = unpack_coption_key(freeze_authority)?;
64 Ok(Mint {
65 mint_authority,
66 supply,
67 decimals,
68 is_initialized,
69 freeze_authority,
70 })
71 }
72 fn pack_into_slice(&self, dst: &mut [u8]) {
73 let dst = array_mut_ref![dst, 0, 82];
74 let (
75 mint_authority_dst,
76 supply_dst,
77 decimals_dst,
78 is_initialized_dst,
79 freeze_authority_dst,
80 ) = mut_array_refs![dst, 36, 8, 1, 1, 36];
81 let &Mint {
82 ref mint_authority,
83 supply,
84 decimals,
85 is_initialized,
86 ref freeze_authority,
87 } = self;
88 pack_coption_key(mint_authority, mint_authority_dst);
89 *supply_dst = supply.to_le_bytes();
90 decimals_dst[0] = decimals;
91 is_initialized_dst[0] = is_initialized as u8;
92 pack_coption_key(freeze_authority, freeze_authority_dst);
93 }
94}
95impl PackedSizeOf for Mint {
96 const SIZE_OF: usize = Self::LEN;
97}
98
99#[repr(C)]
101#[derive(Clone, Copy, Debug, Default, PartialEq)]
102pub struct Account {
103 pub mint: Pubkey,
105 pub owner: Pubkey,
107 pub amount: u64,
109 pub delegate: COption<Pubkey>,
112 pub state: AccountState,
114 pub is_native: COption<u64>,
119 pub delegated_amount: u64,
121 pub close_authority: COption<Pubkey>,
123}
124impl Account {
125 pub fn is_frozen(&self) -> bool {
127 self.state == AccountState::Frozen
128 }
129 pub fn is_native(&self) -> bool {
131 self.is_native.is_some()
132 }
133 pub fn is_owned_by_system_program_or_incinerator(&self) -> bool {
136 solana_sdk_ids::system_program::check_id(&self.owner)
137 || solana_sdk_ids::incinerator::check_id(&self.owner)
138 }
139}
140impl Sealed for Account {}
141impl IsInitialized for Account {
142 fn is_initialized(&self) -> bool {
143 self.state != AccountState::Uninitialized
144 }
145}
146impl Pack for Account {
147 const LEN: usize = 165;
148 fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
149 let src = array_ref![src, 0, 165];
150 let (mint, owner, amount, delegate, state, is_native, delegated_amount, close_authority) =
151 array_refs![src, 32, 32, 8, 36, 1, 12, 8, 36];
152 Ok(Account {
153 mint: Pubkey::new_from_array(*mint),
154 owner: Pubkey::new_from_array(*owner),
155 amount: u64::from_le_bytes(*amount),
156 delegate: unpack_coption_key(delegate)?,
157 state: AccountState::try_from_primitive(state[0])
158 .or(Err(ProgramError::InvalidAccountData))?,
159 is_native: unpack_coption_u64(is_native)?,
160 delegated_amount: u64::from_le_bytes(*delegated_amount),
161 close_authority: unpack_coption_key(close_authority)?,
162 })
163 }
164 fn pack_into_slice(&self, dst: &mut [u8]) {
165 let dst = array_mut_ref![dst, 0, 165];
166 let (
167 mint_dst,
168 owner_dst,
169 amount_dst,
170 delegate_dst,
171 state_dst,
172 is_native_dst,
173 delegated_amount_dst,
174 close_authority_dst,
175 ) = mut_array_refs![dst, 32, 32, 8, 36, 1, 12, 8, 36];
176 let &Account {
177 ref mint,
178 ref owner,
179 amount,
180 ref delegate,
181 state,
182 ref is_native,
183 delegated_amount,
184 ref close_authority,
185 } = self;
186 mint_dst.copy_from_slice(mint.as_ref());
187 owner_dst.copy_from_slice(owner.as_ref());
188 *amount_dst = amount.to_le_bytes();
189 pack_coption_key(delegate, delegate_dst);
190 state_dst[0] = state as u8;
191 pack_coption_u64(is_native, is_native_dst);
192 *delegated_amount_dst = delegated_amount.to_le_bytes();
193 pack_coption_key(close_authority, close_authority_dst);
194 }
195}
196impl PackedSizeOf for Account {
197 const SIZE_OF: usize = Self::LEN;
198}
199
200#[repr(u8)]
202#[derive(Clone, Copy, Debug, Default, PartialEq, IntoPrimitive, TryFromPrimitive)]
203pub enum AccountState {
204 #[default]
206 Uninitialized,
207 Initialized,
210 Frozen,
214}
215
216#[repr(C)]
218#[derive(Clone, Copy, Debug, Default, PartialEq)]
219pub struct Multisig {
220 pub m: u8,
222 pub n: u8,
224 pub is_initialized: bool,
226 pub signers: [Pubkey; MAX_SIGNERS],
228}
229impl Sealed for Multisig {}
230impl IsInitialized for Multisig {
231 fn is_initialized(&self) -> bool {
232 self.is_initialized
233 }
234}
235impl Pack for Multisig {
236 const LEN: usize = 355;
237 fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
238 let src = array_ref![src, 0, 355];
239 #[allow(clippy::ptr_offset_with_cast)]
240 let (m, n, is_initialized, signers_flat) = array_refs![src, 1, 1, 1, 32 * MAX_SIGNERS];
241 let mut result = Multisig {
242 m: m[0],
243 n: n[0],
244 is_initialized: match is_initialized {
245 [0] => false,
246 [1] => true,
247 _ => return Err(ProgramError::InvalidAccountData),
248 },
249 signers: [Pubkey::new_from_array([0u8; 32]); MAX_SIGNERS],
250 };
251 for (src, dst) in signers_flat.chunks(32).zip(result.signers.iter_mut()) {
252 *dst = Pubkey::try_from(src).map_err(|_| ProgramError::InvalidAccountData)?;
253 }
254 Ok(result)
255 }
256 fn pack_into_slice(&self, dst: &mut [u8]) {
257 let dst = array_mut_ref![dst, 0, 355];
258 #[allow(clippy::ptr_offset_with_cast)]
259 let (m, n, is_initialized, signers_flat) = mut_array_refs![dst, 1, 1, 1, 32 * MAX_SIGNERS];
260 *m = [self.m];
261 *n = [self.n];
262 *is_initialized = [self.is_initialized as u8];
263 for (i, src) in self.signers.iter().enumerate() {
264 let dst_array = array_mut_ref![signers_flat, 32 * i, 32];
265 dst_array.copy_from_slice(src.as_ref());
266 }
267 }
268}
269impl PackedSizeOf for Multisig {
270 const SIZE_OF: usize = Self::LEN;
271}
272
273pub(crate) fn pack_coption_key(src: &COption<Pubkey>, dst: &mut [u8; 36]) {
275 let (tag, body) = mut_array_refs![dst, 4, 32];
276 match src {
277 COption::Some(key) => {
278 *tag = [1, 0, 0, 0];
279 body.copy_from_slice(key.as_ref());
280 }
281 COption::None => {
282 *tag = [0; 4];
283 }
284 }
285}
286pub(crate) fn unpack_coption_key(src: &[u8; 36]) -> Result<COption<Pubkey>, ProgramError> {
287 let (tag, body) = array_refs![src, 4, 32];
288 match *tag {
289 [0, 0, 0, 0] => Ok(COption::None),
290 [1, 0, 0, 0] => Ok(COption::Some(Pubkey::new_from_array(*body))),
291 _ => Err(ProgramError::InvalidAccountData),
292 }
293}
294fn pack_coption_u64(src: &COption<u64>, dst: &mut [u8; 12]) {
295 let (tag, body) = mut_array_refs![dst, 4, 8];
296 match src {
297 COption::Some(amount) => {
298 *tag = [1, 0, 0, 0];
299 *body = amount.to_le_bytes();
300 }
301 COption::None => {
302 *tag = [0; 4];
303 }
304 }
305}
306fn unpack_coption_u64(src: &[u8; 12]) -> Result<COption<u64>, ProgramError> {
307 let (tag, body) = array_refs![src, 4, 8];
308 match *tag {
309 [0, 0, 0, 0] => Ok(COption::None),
310 [1, 0, 0, 0] => Ok(COption::Some(u64::from_le_bytes(*body))),
311 _ => Err(ProgramError::InvalidAccountData),
312 }
313}
314
315const ACCOUNTTYPE_ACCOUNT: u8 = AccountType::Account as u8;
317impl GenericTokenAccount for Account {
318 fn valid_account_data(account_data: &[u8]) -> bool {
319 account_data.len() == Account::LEN && is_initialized_account(account_data)
321 || (account_data.len() > Account::LEN
322 && account_data.len() != Multisig::LEN
323 && ACCOUNTTYPE_ACCOUNT == account_data[Account::LEN]
324 && is_initialized_account(account_data))
325 }
326}
327
328#[cfg(test)]
329pub(crate) mod test {
330 use {super::*, crate::generic_token_account::ACCOUNT_INITIALIZED_INDEX};
331
332 pub const TEST_MINT: Mint = Mint {
333 mint_authority: COption::Some(Pubkey::new_from_array([1; 32])),
334 supply: 42,
335 decimals: 7,
336 is_initialized: true,
337 freeze_authority: COption::Some(Pubkey::new_from_array([2; 32])),
338 };
339 pub const TEST_MINT_SLICE: &[u8] = &[
340 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
341 1, 1, 1, 1, 1, 1, 42, 0, 0, 0, 0, 0, 0, 0, 7, 1, 1, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
342 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
343 ];
344
345 pub const TEST_ACCOUNT: Account = Account {
346 mint: Pubkey::new_from_array([1; 32]),
347 owner: Pubkey::new_from_array([2; 32]),
348 amount: 3,
349 delegate: COption::Some(Pubkey::new_from_array([4; 32])),
350 state: AccountState::Frozen,
351 is_native: COption::Some(5),
352 delegated_amount: 6,
353 close_authority: COption::Some(Pubkey::new_from_array([7; 32])),
354 };
355 pub const TEST_ACCOUNT_SLICE: &[u8] = &[
356 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
357 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
358 2, 2, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
359 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0,
360 0, 6, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
361 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
362 ];
363 pub const TEST_MULTISIG: Multisig = Multisig {
364 m: 1,
365 n: 11,
366 is_initialized: true,
367 signers: [
368 Pubkey::new_from_array([1; 32]),
369 Pubkey::new_from_array([2; 32]),
370 Pubkey::new_from_array([3; 32]),
371 Pubkey::new_from_array([4; 32]),
372 Pubkey::new_from_array([5; 32]),
373 Pubkey::new_from_array([6; 32]),
374 Pubkey::new_from_array([7; 32]),
375 Pubkey::new_from_array([8; 32]),
376 Pubkey::new_from_array([9; 32]),
377 Pubkey::new_from_array([10; 32]),
378 Pubkey::new_from_array([11; 32]),
379 ],
380 };
381 pub const TEST_MULTISIG_SLICE: &[u8] = &[
382 1, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
383 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
384 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
385 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
386 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
387 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
388 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
389 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
390 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
391 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10,
392 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
393 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
394 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
395 ];
396
397 #[test]
398 fn test_pack_unpack() {
399 let check = TEST_MINT;
401 let mut packed = vec![0; Mint::get_packed_len() + 1];
402 assert_eq!(
403 Err(ProgramError::InvalidAccountData),
404 Mint::pack(check, &mut packed)
405 );
406 let mut packed = vec![0; Mint::get_packed_len() - 1];
407 assert_eq!(
408 Err(ProgramError::InvalidAccountData),
409 Mint::pack(check, &mut packed)
410 );
411 let mut packed = vec![0; Mint::get_packed_len()];
412 Mint::pack(check, &mut packed).unwrap();
413 assert_eq!(packed, TEST_MINT_SLICE);
414 let unpacked = Mint::unpack(&packed).unwrap();
415 assert_eq!(unpacked, check);
416
417 let check = TEST_ACCOUNT;
419 let mut packed = vec![0; Account::get_packed_len() + 1];
420 assert_eq!(
421 Err(ProgramError::InvalidAccountData),
422 Account::pack(check, &mut packed)
423 );
424 let mut packed = vec![0; Account::get_packed_len() - 1];
425 assert_eq!(
426 Err(ProgramError::InvalidAccountData),
427 Account::pack(check, &mut packed)
428 );
429 let mut packed = vec![0; Account::get_packed_len()];
430 Account::pack(check, &mut packed).unwrap();
431 let expect = TEST_ACCOUNT_SLICE;
432 assert_eq!(packed, expect);
433 let unpacked = Account::unpack(&packed).unwrap();
434 assert_eq!(unpacked, check);
435
436 let check = TEST_MULTISIG;
438 let mut packed = vec![0; Multisig::get_packed_len() + 1];
439 assert_eq!(
440 Err(ProgramError::InvalidAccountData),
441 Multisig::pack(check, &mut packed)
442 );
443 let mut packed = vec![0; Multisig::get_packed_len() - 1];
444 assert_eq!(
445 Err(ProgramError::InvalidAccountData),
446 Multisig::pack(check, &mut packed)
447 );
448 let mut packed = vec![0; Multisig::get_packed_len()];
449 Multisig::pack(check, &mut packed).unwrap();
450 let expect = TEST_MULTISIG_SLICE;
451 assert_eq!(packed, expect);
452 let unpacked = Multisig::unpack(&packed).unwrap();
453 assert_eq!(unpacked, check);
454 }
455
456 #[test]
457 fn test_unpack_token_owner() {
458 let src: [u8; 12] = [0; 12];
460 let result = Account::unpack_account_owner(&src);
461 assert_eq!(result, Option::None);
462
463 let mut src: [u8; Account::LEN] = [0; Account::LEN];
465 src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
466 let result = Account::unpack_account_owner(&src);
467 assert!(result.is_some());
468
469 src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Frozen as u8;
471 let result = Account::unpack_account_owner(&src);
472 assert!(result.is_some());
473
474 let mut src: [u8; Account::LEN + 5] = [0; Account::LEN + 5];
477 src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
478 let result = Account::unpack_account_owner(&src);
479 assert_eq!(result, Option::None);
480
481 let mut src: [u8; Account::LEN + 5] = [0; Account::LEN + 5];
484 src[Account::LEN] = AccountType::Account as u8;
485 src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
486 let result = Account::unpack_account_owner(&src);
487 assert!(result.is_some());
488
489 src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Uninitialized as u8;
492 let result = Account::unpack_account_owner(&src);
493 assert!(result.is_none());
494
495 let mut src: [u8; Multisig::LEN] = [0; Multisig::LEN];
498 src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
499 src[Account::LEN] = AccountType::Account as u8;
500 let result = Account::unpack_account_owner(&src);
501 assert!(result.is_none());
502 }
503
504 #[test]
505 fn test_unpack_token_mint() {
506 let src: [u8; 12] = [0; 12];
508 let result = Account::unpack_account_mint(&src);
509 assert_eq!(result, Option::None);
510
511 let mut src: [u8; Account::LEN] = [0; Account::LEN];
513 src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
514 let result = Account::unpack_account_mint(&src);
515 assert!(result.is_some());
516
517 src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Frozen as u8;
519 let result = Account::unpack_account_mint(&src);
520 assert!(result.is_some());
521
522 let mut src: [u8; Account::LEN + 5] = [0; Account::LEN + 5];
525 src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
526 let result = Account::unpack_account_mint(&src);
527 assert_eq!(result, Option::None);
528
529 let mut src: [u8; Account::LEN + 5] = [0; Account::LEN + 5];
532 src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
533 src[Account::LEN] = AccountType::Account as u8;
534 let result = Account::unpack_account_mint(&src);
535 assert!(result.is_some());
536
537 src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Uninitialized as u8;
540 let result = Account::unpack_account_mint(&src);
541 assert!(result.is_none());
542
543 let mut src: [u8; Multisig::LEN] = [0; Multisig::LEN];
546 src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
547 src[Account::LEN] = AccountType::Account as u8;
548 let result = Account::unpack_account_mint(&src);
549 assert!(result.is_none());
550 }
551}