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