spl_token_2022/
state.rs

1//! State transition types
2
3use {
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/// Simplified version of the `Pack` trait which only gives the size of the
20/// packed struct. Useful when a function doesn't need a type to implement all
21/// of `Pack`, but a size is still needed.
22pub trait PackedSizeOf {
23    /// The packed size of the struct
24    const SIZE_OF: usize;
25}
26
27/// Mint data.
28#[repr(C)]
29#[derive(Clone, Copy, Debug, Default, PartialEq)]
30pub struct Mint {
31    /// Optional authority used to mint new tokens. The mint authority may only
32    /// be provided during mint creation. If no mint authority is present
33    /// then the mint has a fixed supply and no further tokens may be
34    /// minted.
35    pub mint_authority: COption<Pubkey>,
36    /// Total supply of tokens.
37    pub supply: u64,
38    /// Number of base 10 digits to the right of the decimal place.
39    pub decimals: u8,
40    /// Is `true` if this structure has been initialized
41    pub is_initialized: bool,
42    /// Optional authority to freeze token accounts.
43    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/// Account data.
102#[repr(C)]
103#[derive(Clone, Copy, Debug, Default, PartialEq)]
104pub struct Account {
105    /// The mint associated with this account
106    pub mint: Pubkey,
107    /// The owner of this account.
108    pub owner: Pubkey,
109    /// The amount of tokens this account holds.
110    pub amount: u64,
111    /// If `delegate` is `Some` then `delegated_amount` represents
112    /// the amount authorized by the delegate
113    pub delegate: COption<Pubkey>,
114    /// The account's state
115    pub state: AccountState,
116    /// If `is_some`, this is a native token, and the value logs the rent-exempt
117    /// reserve. An Account is required to be rent-exempt, so the value is
118    /// used by the Processor to ensure that wrapped SOL accounts do not
119    /// drop below this threshold.
120    pub is_native: COption<u64>,
121    /// The amount delegated
122    pub delegated_amount: u64,
123    /// Optional authority to close the account.
124    pub close_authority: COption<Pubkey>,
125}
126impl Account {
127    /// Checks if account is frozen
128    pub fn is_frozen(&self) -> bool {
129        self.state == AccountState::Frozen
130    }
131    /// Checks if account is native
132    pub fn is_native(&self) -> bool {
133        self.is_native.is_some()
134    }
135    /// Checks if a token Account's owner is the `system_program` or the
136    /// incinerator
137    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/// Account state.
203#[repr(u8)]
204#[derive(Clone, Copy, Debug, Default, PartialEq, IntoPrimitive, TryFromPrimitive)]
205pub enum AccountState {
206    /// Account is not yet initialized
207    #[default]
208    Uninitialized,
209    /// Account is initialized; the account owner and/or delegate may perform
210    /// permitted operations on this account
211    Initialized,
212    /// Account has been frozen by the mint freeze authority. Neither the
213    /// account owner nor the delegate are able to perform operations on
214    /// this account.
215    Frozen,
216}
217
218/// Multisignature data.
219#[repr(C)]
220#[derive(Clone, Copy, Debug, Default, PartialEq)]
221pub struct Multisig {
222    /// Number of signers required
223    pub m: u8,
224    /// Number of valid signers
225    pub n: u8,
226    /// Is `true` if this structure has been initialized
227    pub is_initialized: bool,
228    /// Signer public keys
229    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
275// Helpers
276pub(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
317// `spl_token_program_2022::extension::AccountType::Account` ordinal value
318const ACCOUNTTYPE_ACCOUNT: u8 = AccountType::Account as u8;
319impl GenericTokenAccount for Account {
320    fn valid_account_data(account_data: &[u8]) -> bool {
321        // Use spl_token::state::Account::valid_account_data once possible
322        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        // Mint
402        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        // Account
420        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        // Multisig
439        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        // Account data length < Account::LEN, unpack will not return a key
461        let src: [u8; 12] = [0; 12];
462        let result = Account::unpack_account_owner(&src);
463        assert_eq!(result, Option::None);
464
465        // The right account data size and initialized, unpack will return some key
466        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        // The right account data size and frozen, unpack will return some key
472        src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Frozen as u8;
473        let result = Account::unpack_account_owner(&src);
474        assert!(result.is_some());
475
476        // Account data length > account data size, but not a valid extension,
477        // unpack will not return a key
478        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        // Account data length > account data size with a valid extension and
484        // initialized, expect some key returned
485        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        // Account data length > account data size with a valid extension but
492        // uninitialized, expect None
493        src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Uninitialized as u8;
494        let result = Account::unpack_account_owner(&src);
495        assert!(result.is_none());
496
497        // Account data length is multi-sig data size with a valid extension and
498        // initialized, expect none
499        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        // Account data length < Account::LEN, unpack will not return a key
509        let src: [u8; 12] = [0; 12];
510        let result = Account::unpack_account_mint(&src);
511        assert_eq!(result, Option::None);
512
513        // The right account data size and initialized, unpack will return some key
514        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        // The right account data size and frozen, unpack will return some key
520        src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Frozen as u8;
521        let result = Account::unpack_account_mint(&src);
522        assert!(result.is_some());
523
524        // Account data length > account data size, but not a valid extension,
525        // unpack will not return a key
526        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        // Account data length > account data size with a valid extension and
532        // initialized, expect some key returned
533        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        // Account data length > account data size with a valid extension but
540        // uninitialized, expect none
541        src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Uninitialized as u8;
542        let result = Account::unpack_account_mint(&src);
543        assert!(result.is_none());
544
545        // Account data length is multi-sig data size with a valid extension and
546        // initialized, expect none
547        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}