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