Skip to main content

exo_pinocchio_token_interface/
lib.rs

1use core::ops::Deref;
2use pinocchio::error::ProgramError;
3use solana_account_view::{AccountView, Ref};
4
5pub use pinocchio_token_2022::instructions;
6
7use pinocchio_token_2022::state::TokenAccount as T22TokenAccount;
8
9const EXTENSION_TYPE_LEN: usize = 2;
10const EXTENSION_LENGTH_LEN: usize = 2;
11
12/// SPL Token-2022 account-type byte after the 165-byte base state (`AccountType::Mint`).
13const T22_ACCOUNT_TYPE_MINT: u8 = 1;
14/// SPL Token-2022 account-type byte for a token holding account (`AccountType::Account`).
15const T22_ACCOUNT_TYPE_TOKEN_ACCOUNT: u8 = 2;
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub enum ExtensionType {
19    TransferFeeConfig,
20    MintCloseAuthority,
21    ConfidentialTransferMint,
22    PermanentDelegate,
23    TransferHook,
24    ConfidentialTransferFeeConfig,
25    MetadataPointer,
26    TokenMetadata,
27    GroupPointer,
28    TokenGroup,
29}
30
31impl ExtensionType {
32    fn from_bytes(b: [u8; 2]) -> Option<Self> {
33        match u16::from_le_bytes(b) {
34            1 => Some(Self::TransferFeeConfig),
35            3 => Some(Self::MintCloseAuthority),
36            4 => Some(Self::ConfidentialTransferMint),
37            12 => Some(Self::PermanentDelegate),
38            14 => Some(Self::TransferHook),
39            16 => Some(Self::ConfidentialTransferFeeConfig),
40            18 => Some(Self::MetadataPointer),
41            19 => Some(Self::TokenMetadata),
42            20 => Some(Self::GroupPointer),
43            21 => Some(Self::TokenGroup),
44            _ => None,
45        }
46    }
47}
48
49pub struct TokenAccount<'info>(Ref<'info, T22TokenAccount>);
50
51/// Size of multisig account for TokenAccount and Mint.
52pub const MULTISIG_ACCOUNT_LENGTH: usize = 355;
53
54/// Get the account type for a T22 account.
55pub fn get_account_type(account_view: &AccountView) -> Result<u8, ProgramError> {
56    let data = account_view.try_borrow()?;
57    // Account type is at byte 165 (`T22TokenAccount::BASE_LEN`) for extensible T22 accounts.
58    let account_type = data[T22TokenAccount::BASE_LEN];
59    Ok(account_type)
60}
61
62impl<'info> TokenAccount<'info> {
63    pub fn from_account_view(account_view: &'info AccountView) -> Result<Self, ProgramError> {
64        if account_view.owned_by(&pinocchio_token_2022::ID) {
65            let data_len = account_view.data_len();
66            if data_len > T22TokenAccount::BASE_LEN {
67                let account_type = get_account_type(account_view)?;
68                // Token holding account must have account type 2.
69                if account_type != T22_ACCOUNT_TYPE_TOKEN_ACCOUNT {
70                    return Err(ProgramError::InvalidAccountData);
71                }
72                // Multisig accounts are not supported.
73                if data_len == MULTISIG_ACCOUNT_LENGTH {
74                    return Err(ProgramError::InvalidAccountData);
75                }
76            }
77            T22TokenAccount::from_account_view(account_view)
78                .map(TokenAccount)
79                .map_err(|_| ProgramError::InvalidAccountData)
80        } else if account_view.owned_by(&pinocchio_token::ID) {
81            if account_view.data_len() != pinocchio_token::state::TokenAccount::LEN {
82                return Err(ProgramError::InvalidAccountData);
83            }
84            // SAFETY: Legacy Token and Token-2022 token account structs share the same base layout.
85            Ok(TokenAccount(Ref::map(
86                account_view.try_borrow()?,
87                |data| unsafe { T22TokenAccount::from_bytes_unchecked(data) },
88            )))
89        } else {
90            Err(ProgramError::InvalidAccountData)
91        }
92    }
93}
94
95impl Deref for TokenAccount<'_> {
96    type Target = T22TokenAccount;
97
98    fn deref(&self) -> &Self::Target {
99        &self.0
100    }
101}
102
103pub struct Mint<'info>(Ref<'info, pinocchio_token_2022::state::Mint>);
104
105impl<'info> Mint<'info> {
106    pub fn from_account_view(account_view: &'info AccountView) -> Result<Self, ProgramError> {
107        if account_view.owned_by(&pinocchio_token_2022::ID) {
108            let data_len = account_view.data_len();
109            let mint_base_len = pinocchio_token_2022::state::Mint::BASE_LEN;
110            // Use `T22TokenAccount::BASE_LEN` (165) as threshold — that is where the account-type
111            // byte lives for all extensible T22 accounts, including Mints.
112            if data_len > T22TokenAccount::BASE_LEN {
113                let account_type = get_account_type(account_view)?;
114                // Mint must have account type 1.
115                if account_type != T22_ACCOUNT_TYPE_MINT {
116                    return Err(ProgramError::InvalidAccountData);
117                }
118                // Multisig accounts are not supported.
119                if data_len == MULTISIG_ACCOUNT_LENGTH {
120                    return Err(ProgramError::InvalidAccountData);
121                }
122            } else if data_len != mint_base_len {
123                // Reject 83..=165: token-account size and other invalid mint lengths.
124                return Err(ProgramError::InvalidAccountData);
125            }
126            pinocchio_token_2022::state::Mint::from_account_view(account_view)
127                .map(Mint)
128                .map_err(|_| ProgramError::InvalidAccountData)
129        } else if account_view.owned_by(&pinocchio_token::ID) {
130            if account_view.data_len() != pinocchio_token::state::Mint::LEN {
131                return Err(ProgramError::InvalidAccountData);
132            }
133            // SAFETY: Token and Token2022 Mint structs have compatible layouts.
134            Ok(Mint(Ref::map(account_view.try_borrow()?, |data| unsafe {
135                pinocchio_token_2022::state::Mint::from_bytes_unchecked(data)
136            })))
137        } else {
138            Err(ProgramError::InvalidAccountData)
139        }
140    }
141}
142
143impl Deref for Mint<'_> {
144    type Target = pinocchio_token_2022::state::Mint;
145
146    fn deref(&self) -> &Self::Target {
147        &self.0
148    }
149}
150
151/// Iterate over TLV extension data and return all extension types present.
152/// Works for both Token-2022 Mint and TokenAccount accounts.
153pub fn get_all_extensions(acc_data_bytes: &[u8]) -> Result<Vec<ExtensionType>, ProgramError> {
154    let ext_start = T22TokenAccount::BASE_LEN + 1;
155    if acc_data_bytes.len() <= ext_start {
156        return Ok(Vec::new());
157    }
158    let account_type_byte = acc_data_bytes[T22TokenAccount::BASE_LEN];
159    if account_type_byte != T22_ACCOUNT_TYPE_MINT
160        && account_type_byte != T22_ACCOUNT_TYPE_TOKEN_ACCOUNT
161    {
162        return Err(ProgramError::InvalidAccountData);
163    }
164    let ext_bytes = &acc_data_bytes[ext_start..];
165    let mut extension_types = Vec::new();
166    let mut start = 0;
167    while start + EXTENSION_TYPE_LEN + EXTENSION_LENGTH_LEN <= ext_bytes.len() {
168        let type_bytes: [u8; 2] = ext_bytes[start..start + EXTENSION_TYPE_LEN]
169            .try_into()
170            .map_err(|_| ProgramError::InvalidAccountData)?;
171        let ext_type =
172            ExtensionType::from_bytes(type_bytes).ok_or(ProgramError::InvalidAccountData)?;
173        let len_bytes: [u8; 2] = ext_bytes
174            [start + EXTENSION_TYPE_LEN..start + EXTENSION_TYPE_LEN + EXTENSION_LENGTH_LEN]
175            .try_into()
176            .map_err(|_| ProgramError::InvalidAccountData)?;
177        let ext_len = u16::from_le_bytes(len_bytes) as usize;
178        extension_types.push(ext_type);
179        start += EXTENSION_TYPE_LEN + EXTENSION_LENGTH_LEN + ext_len;
180    }
181    Ok(extension_types)
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187    use solana_account_view::{AccountView, RuntimeAccount, NOT_BORROWED};
188
189    fn make_account(owner: [u8; 32], data: Vec<u8>) -> (Vec<u8>, AccountView) {
190        let header = core::mem::size_of::<RuntimeAccount>();
191        debug_assert_eq!(header, 88);
192        let mut buf = vec![0u8; header + data.len()];
193        buf[0] = NOT_BORROWED;
194        buf[40..72].copy_from_slice(&owner);
195        buf[80..88].copy_from_slice(&(data.len() as u64).to_le_bytes());
196        if !data.is_empty() {
197            buf[header..].copy_from_slice(&data);
198        }
199        let raw = buf.as_mut_ptr() as *mut RuntimeAccount;
200        // SAFETY: buf is live, correctly laid out, and data_len matches buf length.
201        let view = unsafe { AccountView::new_unchecked(raw) };
202        (buf, view)
203    }
204
205    fn t22_id() -> [u8; 32] {
206        // SAFETY: solana_address::Address is #[repr(transparent)] over [u8; 32].
207        unsafe { core::mem::transmute(pinocchio_token_2022::ID) }
208    }
209
210    fn token_id() -> [u8; 32] {
211        // SAFETY: solana_address::Address is #[repr(transparent)] over [u8; 32].
212        unsafe { core::mem::transmute(pinocchio_token::ID) }
213    }
214
215    // ── TokenAccount ──────────────────────────────────────────────────────────
216
217    #[test]
218    fn token_account_t22_base_len() {
219        let data = vec![0u8; T22TokenAccount::BASE_LEN];
220        let (_buf, view) = make_account(t22_id(), data);
221        assert!(TokenAccount::from_account_view(&view).is_ok());
222    }
223
224    #[test]
225    fn token_account_t22_with_extensions() {
226        let mut data = vec![0u8; T22TokenAccount::BASE_LEN + 2];
227        data[T22TokenAccount::BASE_LEN] = T22_ACCOUNT_TYPE_TOKEN_ACCOUNT;
228        let (_buf, view) = make_account(t22_id(), data);
229        assert!(TokenAccount::from_account_view(&view).is_ok());
230    }
231
232    #[test]
233    fn token_account_t22_wrong_type() {
234        let mut data = vec![0u8; T22TokenAccount::BASE_LEN + 2];
235        data[T22TokenAccount::BASE_LEN] = T22_ACCOUNT_TYPE_MINT; // wrong for token account
236        let (_buf, view) = make_account(t22_id(), data);
237        assert_eq!(
238            TokenAccount::from_account_view(&view).err().unwrap(),
239            ProgramError::InvalidAccountData,
240        );
241    }
242
243    #[test]
244    fn token_account_t22_multisig_length() {
245        let mut data = vec![0u8; MULTISIG_ACCOUNT_LENGTH];
246        data[T22TokenAccount::BASE_LEN] = T22_ACCOUNT_TYPE_TOKEN_ACCOUNT;
247        let (_buf, view) = make_account(t22_id(), data);
248        assert_eq!(
249            TokenAccount::from_account_view(&view).err().unwrap(),
250            ProgramError::InvalidAccountData,
251        );
252    }
253
254    #[test]
255    fn token_account_legacy_success() {
256        let data = vec![0u8; pinocchio_token::state::TokenAccount::LEN];
257        let (_buf, view) = make_account(token_id(), data);
258        assert!(TokenAccount::from_account_view(&view).is_ok());
259    }
260
261    #[test]
262    fn token_account_legacy_wrong_length() {
263        let data = vec![0u8; 100];
264        let (_buf, view) = make_account(token_id(), data);
265        assert_eq!(
266            TokenAccount::from_account_view(&view).err().unwrap(),
267            ProgramError::InvalidAccountData,
268        );
269    }
270
271    #[test]
272    fn token_account_wrong_owner() {
273        let data = vec![0u8; T22TokenAccount::BASE_LEN];
274        let (_buf, view) = make_account([0u8; 32], data);
275        assert_eq!(
276            TokenAccount::from_account_view(&view).err().unwrap(),
277            ProgramError::InvalidAccountData,
278        );
279    }
280
281    // ── Mint ──────────────────────────────────────────────────────────────────
282
283    #[test]
284    fn mint_t22_base_len() {
285        let data = vec![0u8; pinocchio_token_2022::state::Mint::BASE_LEN];
286        let (_buf, view) = make_account(t22_id(), data);
287        assert!(Mint::from_account_view(&view).is_ok());
288    }
289
290    #[test]
291    fn mint_t22_with_extensions() {
292        let mut data = vec![0u8; T22TokenAccount::BASE_LEN + 2];
293        data[T22TokenAccount::BASE_LEN] = T22_ACCOUNT_TYPE_MINT;
294        let (_buf, view) = make_account(t22_id(), data);
295        assert!(Mint::from_account_view(&view).is_ok());
296    }
297
298    #[test]
299    fn mint_t22_wrong_type() {
300        let mut data = vec![0u8; T22TokenAccount::BASE_LEN + 2];
301        data[T22TokenAccount::BASE_LEN] = T22_ACCOUNT_TYPE_TOKEN_ACCOUNT; // wrong for mint
302        let (_buf, view) = make_account(t22_id(), data);
303        assert_eq!(
304            Mint::from_account_view(&view).err().unwrap(),
305            ProgramError::InvalidAccountData,
306        );
307    }
308
309    #[test]
310    fn mint_t22_rejects_token_account_as_mint() {
311        let data = vec![0u8; T22TokenAccount::BASE_LEN];
312        let (_buf, view) = make_account(t22_id(), data);
313
314        // Valid token holding account at base size.
315        assert!(TokenAccount::from_account_view(&view).is_ok());
316
317        // Must not be accepted as a mint.
318        // previous implementation would accept a Token-2022 account as a mint. This is no longer allowed.
319        assert_eq!(
320            Mint::from_account_view(&view).err().unwrap(),
321            ProgramError::InvalidAccountData,
322        );
323    }
324
325    #[test]
326    fn mint_t22_invalid_intermediate_length_rejected() {
327        let data = vec![0u8; 100];
328        let (_buf, view) = make_account(t22_id(), data);
329        assert_eq!(
330            Mint::from_account_view(&view).err().unwrap(),
331            ProgramError::InvalidAccountData,
332        );
333    }
334
335    #[test]
336    fn mint_legacy_success() {
337        let data = vec![0u8; pinocchio_token::state::Mint::LEN];
338        let (_buf, view) = make_account(token_id(), data);
339        assert!(Mint::from_account_view(&view).is_ok());
340    }
341
342    #[test]
343    fn mint_legacy_wrong_length() {
344        let data = vec![0u8; 100];
345        let (_buf, view) = make_account(token_id(), data);
346        assert_eq!(
347            Mint::from_account_view(&view).err().unwrap(),
348            ProgramError::InvalidAccountData,
349        );
350    }
351
352    #[test]
353    fn mint_wrong_owner() {
354        let data = vec![0u8; pinocchio_token_2022::state::Mint::BASE_LEN];
355        let (_buf, view) = make_account([0u8; 32], data);
356        assert_eq!(
357            Mint::from_account_view(&view).err().unwrap(),
358            ProgramError::InvalidAccountData,
359        );
360    }
361
362    // ── Extensions ────────────────────────────────────────────────────────────
363
364    pub const TEST_MINT_WITH_EXTENSIONS_SLICE: &[u8] = &[
365        1, 0, 0, 0, 221, 76, 72, 108, 144, 248, 182, 240, 7, 195, 4, 239, 36, 129, 248, 5, 24, 107,
366        232, 253, 95, 82, 172, 209, 2, 92, 183, 155, 159, 103, 255, 33, 133, 204, 6, 44, 35, 140,
367        0, 0, 6, 1, 1, 0, 0, 0, 23, 133, 50, 97, 239, 106, 184, 83, 42, 103, 240, 83, 134, 90, 173,
368        49, 41, 63, 207, 7, 207, 18, 10, 181, 185, 161, 87, 6, 84, 141, 192, 43, 0, 0, 0, 0, 0, 0,
369        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
370        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
371        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
372        /*                  MintCloseAuthority Extension                                      */
373        3, 0, 32, 0, 23, 133, 50, 97, 239, 106, 184, 83, 42, 103, 240, 83, 134, 90, 173, 49, 41, 63,
374        207, 7, 207, 18, 10, 181, 185, 161, 87, 6, 84, 141, 192, 43,
375        /*                  PermanentDelegate Extension                                      */
376        12, 0, 32, 0, 23, 133, 50, 97, 239, 106, 184, 83, 42, 103, 240, 83, 134, 90, 173, 49, 41,
377        63, 207, 7, 207, 18, 10, 181, 185, 161, 87, 6, 84, 141, 192, 43,
378        /*                  TransferFeeConfig Extension                                      */
379        1, 0, 108, 0, 23, 133, 50, 97, 239, 106, 184, 83, 42, 103, 240, 83, 134, 90, 173, 49, 41,
380        63, 207, 7, 207, 18, 10, 181, 185, 161, 87, 6, 84, 141, 192, 43, 23, 133, 50, 97, 239, 106,
381        184, 83, 42, 103, 240, 83, 134, 90, 173, 49, 41, 63, 207, 7, 207, 18, 10, 181, 185, 161,
382        87, 6, 84, 141, 192, 43, 0, 0, 0, 0, 0, 0, 0, 0, 93, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
383        0, 0, 0, 0, 93, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
384        /*                  ConfidentialTransferMint Extension                                      */
385        4, 0, 65, 0, 23, 133, 50, 97, 239, 106, 184, 83, 42, 103, 240, 83, 134, 90, 173, 49, 41, 63,
386        207, 7, 207, 18, 10, 181, 185, 161, 87, 6, 84, 141, 192, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
387        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
388        /*                  ConfidentialTransferFeeConfig Extension                                      */
389        16, 0, 129, 0, 23, 133, 50, 97, 239, 106, 184, 83, 42, 103, 240, 83, 134, 90, 173, 49, 41,
390        63, 207, 7, 207, 18, 10, 181, 185, 161, 87, 6, 84, 141, 192, 43, 28, 55, 230, 67, 59, 115,
391        4, 221, 130, 115, 122, 228, 13, 155, 139, 243, 196, 159, 91, 14, 108, 73, 168, 213, 51, 40,
392        179, 229, 6, 144, 28, 87, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
393        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
394        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
395        /*                  TransferHook Extension                                      */
396        14, 0, 64, 0, 23, 133, 50, 97, 239, 106, 184, 83, 42, 103, 240, 83, 134, 90, 173, 49, 41,
397        63, 207, 7, 207, 18, 10, 181, 185, 161, 87, 6, 84, 141, 192, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0,
398        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
399        /*                  MetadataPointer Extension                                      */
400        18, 0, 64, 0, 23, 133, 50, 97, 239, 106, 184, 83, 42, 103, 240, 83, 134, 90, 173, 49, 41,
401        63, 207, 7, 207, 18, 10, 181, 185, 161, 87, 6, 84, 141, 192, 43, 23, 146, 72, 59, 108, 138,
402        42, 135, 183, 71, 29, 129, 79, 149, 145, 249, 57, 92, 132, 10, 156, 227, 217, 244, 213,
403        186, 125, 58, 75, 138, 116, 158,
404        /*                  TokenMetadata Extension                                      */
405        19, 0, 174, 0, 23, 133, 50, 97, 239, 106, 184, 83, 42, 103, 240, 83, 134, 90, 173, 49, 41,
406        63, 207, 7, 207, 18, 10, 181, 185, 161, 87, 6, 84, 141, 192, 43, 23, 146, 72, 59, 108, 138,
407        42, 135, 183, 71, 29, 129, 79, 149, 145, 249, 57, 92, 132, 10, 156, 227, 217, 244, 213,
408        186, 125, 58, 75, 138, 116, 158, 10, 0, 0, 0, 80, 97, 121, 80, 97, 108, 32, 85, 83, 68, 5,
409        0, 0, 0, 80, 89, 85, 83, 68, 79, 0, 0, 0, 104, 116, 116, 112, 115, 58, 47, 47, 116, 111,
410        107, 101, 110, 45, 109, 101, 116, 97, 100, 97, 116, 97, 46, 112, 97, 120, 111, 115, 46, 99,
411        111, 109, 47, 112, 121, 117, 115, 100, 95, 109, 101, 116, 97, 100, 97, 116, 97, 47, 112,
412        114, 111, 100, 47, 115, 111, 108, 97, 110, 97, 47, 112, 121, 117, 115, 100, 95, 109, 101,
413        116, 97, 100, 97, 116, 97, 46, 106, 115, 111, 110, 0, 0, 0, 0,
414        /*                  GroupPointer Extension                                      */
415        20, 0, 64, 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,
416        1, 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,
417        2, 2, 2, 2, 2, 2, 2, 2,
418        /*                  TokenGroup Extension                                      */
419        21, 0, 80, 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,
420        1, 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,
421        2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0,
422    ];
423
424    #[test]
425    fn test_get_all_extensions_for_mint() {
426        let extension_types = get_all_extensions(TEST_MINT_WITH_EXTENSIONS_SLICE).unwrap();
427        assert_eq!(
428            extension_types,
429            vec![
430                ExtensionType::MintCloseAuthority,
431                ExtensionType::PermanentDelegate,
432                ExtensionType::TransferFeeConfig,
433                ExtensionType::ConfidentialTransferMint,
434                ExtensionType::ConfidentialTransferFeeConfig,
435                ExtensionType::TransferHook,
436                ExtensionType::MetadataPointer,
437                ExtensionType::TokenMetadata,
438                ExtensionType::GroupPointer,
439                ExtensionType::TokenGroup,
440            ]
441        );
442    }
443
444    #[test]
445    fn test_get_all_extensions_no_extensions() {
446        let data = vec![0u8; T22TokenAccount::BASE_LEN + 1];
447        assert_eq!(get_all_extensions(&data).unwrap(), vec![]);
448    }
449
450    #[test]
451    fn test_get_all_extensions_wrong_account_type() {
452        let base = T22TokenAccount::BASE_LEN;
453        let mut data = vec![0u8; base + 2];
454        data[base] = 0; // Uninitialized AccountType
455        assert_eq!(
456            get_all_extensions(&data).err().unwrap(),
457            ProgramError::InvalidAccountData,
458        );
459    }
460}