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