spl_token_2022/
pod_instruction.rs

1//! Rewrites of the instruction data types represented as Pods
2
3use {
4    bytemuck::{Pod, Zeroable},
5    num_enum::{IntoPrimitive, TryFromPrimitive},
6    solana_program_error::ProgramError,
7    solana_pubkey::{Pubkey, PUBKEY_BYTES},
8    spl_pod::{
9        bytemuck::{pod_from_bytes, pod_get_packed_len},
10        primitives::PodU64,
11    },
12    spl_token_2022_interface::pod::PodCOption,
13};
14
15#[repr(C)]
16#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
17pub(crate) struct InitializeMintData {
18    /// Number of base 10 digits to the right of the decimal place.
19    pub(crate) decimals: u8,
20    /// The authority/multisignature to mint tokens.
21    pub(crate) mint_authority: Pubkey,
22    // The freeze authority option comes later, but cannot be included as
23    // plain old data in this struct
24}
25#[repr(C)]
26#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
27pub(crate) struct InitializeMultisigData {
28    /// The number of signers (M) required to validate this multisignature
29    /// account.
30    pub(crate) m: u8,
31}
32#[repr(C)]
33#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
34pub(crate) struct AmountData {
35    /// The amount of tokens to transfer.
36    pub(crate) amount: PodU64,
37}
38#[repr(C)]
39#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
40pub(crate) struct AmountCheckedData {
41    /// The amount of tokens to transfer.
42    pub(crate) amount: PodU64,
43    /// Decimals of the mint
44    pub(crate) decimals: u8,
45}
46#[repr(C)]
47#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
48pub(crate) struct SetAuthorityData {
49    /// The type of authority to update.
50    pub(crate) authority_type: u8,
51    // The new authority option comes later, but cannot be included as
52    // plain old data in this struct
53}
54
55/// All of the base instructions in Token-2022, reduced down to their one-byte
56/// discriminant.
57///
58/// All instructions that expect data afterwards include a comment with the data
59/// type expected. For example, `PodTokenInstruction::InitializeMint` expects
60/// `InitializeMintData`.
61#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)]
62#[repr(u8)]
63pub(crate) enum PodTokenInstruction {
64    // 0
65    InitializeMint, // InitializeMintData
66    InitializeAccount,
67    InitializeMultisig, // InitializeMultisigData
68    Transfer,           // AmountData
69    Approve,            // AmountData
70    // 5
71    Revoke,
72    SetAuthority, // SetAuthorityData
73    MintTo,       // AmountData
74    Burn,         // AmountData
75    CloseAccount,
76    // 10
77    FreezeAccount,
78    ThawAccount,
79    TransferChecked, // AmountCheckedData
80    ApproveChecked,  // AmountCheckedData
81    MintToChecked,   // AmountCheckedData
82    // 15
83    BurnChecked,        // AmountCheckedData
84    InitializeAccount2, // Pubkey
85    SyncNative,
86    InitializeAccount3,  // Pubkey
87    InitializeMultisig2, // InitializeMultisigData
88    // 20
89    InitializeMint2,    // InitializeMintData
90    GetAccountDataSize, // &[ExtensionType]
91    InitializeImmutableOwner,
92    AmountToUiAmount, // AmountData
93    UiAmountToAmount, // &str
94    // 25
95    InitializeMintCloseAuthority, // COption<Pubkey>
96    TransferFeeExtension,
97    ConfidentialTransferExtension,
98    DefaultAccountStateExtension,
99    Reallocate, // &[ExtensionType]
100    // 30
101    MemoTransferExtension,
102    CreateNativeMint,
103    InitializeNonTransferableMint,
104    InterestBearingMintExtension,
105    CpiGuardExtension,
106    // 35
107    InitializePermanentDelegate, // Pubkey
108    TransferHookExtension,
109    ConfidentialTransferFeeExtension,
110    WithdrawExcessLamports,
111    MetadataPointerExtension,
112    // 40
113    GroupPointerExtension,
114    GroupMemberPointerExtension,
115    ConfidentialMintBurnExtension,
116    ScaledUiAmountExtension,
117    PausableExtension,
118}
119
120fn unpack_pubkey_option(input: &[u8]) -> Result<PodCOption<Pubkey>, ProgramError> {
121    match input.split_first() {
122        Option::Some((&0, _)) => Ok(PodCOption::none()),
123        Option::Some((&1, rest)) => {
124            let pk = rest
125                .get(..PUBKEY_BYTES)
126                .and_then(|x| Pubkey::try_from(x).ok())
127                .ok_or(ProgramError::InvalidInstructionData)?;
128            Ok(PodCOption::some(pk))
129        }
130        _ => Err(ProgramError::InvalidInstructionData),
131    }
132}
133
134/// Specialty function for deserializing `Pod` data and a `COption<Pubkey>`
135///
136/// `COption<T>` is not `Pod` compatible when serialized in an instruction, but
137/// since it is always at the end of an instruction, so we can do this safely
138pub(crate) fn decode_instruction_data_with_coption_pubkey<T: Pod>(
139    input_with_type: &[u8],
140) -> Result<(&T, PodCOption<Pubkey>), ProgramError> {
141    let end_of_t = pod_get_packed_len::<T>().saturating_add(1);
142    let value = input_with_type
143        .get(1..end_of_t)
144        .ok_or(ProgramError::InvalidInstructionData)
145        .and_then(pod_from_bytes)?;
146    let pubkey = unpack_pubkey_option(&input_with_type[end_of_t..])?;
147    Ok((value, pubkey))
148}
149
150#[cfg(test)]
151mod tests {
152    use {
153        super::*,
154        crate::{
155            extension::ExtensionType,
156            instruction::{
157                decode_instruction_data, decode_instruction_type, AuthorityType, TokenInstruction,
158            },
159        },
160        proptest::prelude::*,
161        solana_program_option::COption,
162    };
163
164    // Test function that mimics the "unpacking" in `Processor::process` by
165    // trying to deserialize the relevant type data after the instruction type
166    fn check_pod_instruction(input: &[u8]) -> Result<(), ProgramError> {
167        if let Ok(instruction_type) = decode_instruction_type(input) {
168            match instruction_type {
169                PodTokenInstruction::InitializeMint | PodTokenInstruction::InitializeMint2 => {
170                    let _ =
171                        decode_instruction_data_with_coption_pubkey::<InitializeMintData>(input)?;
172                }
173                PodTokenInstruction::InitializeAccount2
174                | PodTokenInstruction::InitializeAccount3
175                | PodTokenInstruction::InitializePermanentDelegate => {
176                    let _ = decode_instruction_data::<Pubkey>(input)?;
177                }
178                PodTokenInstruction::InitializeMultisig
179                | PodTokenInstruction::InitializeMultisig2 => {
180                    let _ = decode_instruction_data::<InitializeMultisigData>(input)?;
181                }
182                PodTokenInstruction::SetAuthority => {
183                    let _ = decode_instruction_data_with_coption_pubkey::<SetAuthorityData>(input)?;
184                }
185                PodTokenInstruction::Transfer
186                | PodTokenInstruction::Approve
187                | PodTokenInstruction::MintTo
188                | PodTokenInstruction::Burn
189                | PodTokenInstruction::AmountToUiAmount => {
190                    let _ = decode_instruction_data::<AmountData>(input)?;
191                }
192                PodTokenInstruction::TransferChecked
193                | PodTokenInstruction::ApproveChecked
194                | PodTokenInstruction::MintToChecked
195                | PodTokenInstruction::BurnChecked => {
196                    let _ = decode_instruction_data::<AmountCheckedData>(input)?;
197                }
198                PodTokenInstruction::InitializeMintCloseAuthority => {
199                    let _ = decode_instruction_data_with_coption_pubkey::<()>(input)?;
200                }
201                PodTokenInstruction::UiAmountToAmount => {
202                    let _ = std::str::from_utf8(&input[1..])
203                        .map_err(|_| ProgramError::InvalidInstructionData)?;
204                }
205                PodTokenInstruction::GetAccountDataSize | PodTokenInstruction::Reallocate => {
206                    let _ = input[1..]
207                        .chunks(std::mem::size_of::<ExtensionType>())
208                        .map(ExtensionType::try_from)
209                        .collect::<Result<Vec<_>, _>>()?;
210                }
211                _ => {
212                    // no extra data to deserialize
213                }
214            }
215        }
216        Ok(())
217    }
218
219    proptest! {
220        #![proptest_config(ProptestConfig::with_cases(1024))]
221        #[test]
222        fn test_instruction_unpack_proptest(
223            data in prop::collection::vec(any::<u8>(), 0..255)
224        ) {
225            let _no_panic = check_pod_instruction(&data);
226        }
227    }
228
229    #[test]
230    fn test_initialize_mint_packing() {
231        let decimals = 2;
232        let mint_authority = Pubkey::new_from_array([1u8; 32]);
233        let freeze_authority = COption::None;
234        let check = TokenInstruction::InitializeMint {
235            decimals,
236            mint_authority,
237            freeze_authority,
238        };
239        let packed = check.pack();
240        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
241        assert_eq!(instruction_type, PodTokenInstruction::InitializeMint);
242        let (pod, pod_freeze_authority) =
243            decode_instruction_data_with_coption_pubkey::<InitializeMintData>(&packed).unwrap();
244        assert_eq!(pod.decimals, decimals);
245        assert_eq!(pod.mint_authority, mint_authority);
246        assert_eq!(pod_freeze_authority, freeze_authority.into());
247
248        let mint_authority = Pubkey::new_from_array([2u8; 32]);
249        let freeze_authority = COption::Some(Pubkey::new_from_array([3u8; 32]));
250        let check = TokenInstruction::InitializeMint {
251            decimals,
252            mint_authority,
253            freeze_authority,
254        };
255        let packed = check.pack();
256
257        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
258        assert_eq!(instruction_type, PodTokenInstruction::InitializeMint);
259        let (pod, pod_freeze_authority) =
260            decode_instruction_data_with_coption_pubkey::<InitializeMintData>(&packed).unwrap();
261        assert_eq!(pod.decimals, decimals);
262        assert_eq!(pod.mint_authority, mint_authority);
263        assert_eq!(pod_freeze_authority, freeze_authority.into());
264    }
265
266    #[test]
267    fn test_initialize_account_packing() {
268        let check = TokenInstruction::InitializeAccount;
269        let packed = check.pack();
270        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
271        assert_eq!(instruction_type, PodTokenInstruction::InitializeAccount);
272    }
273
274    #[test]
275    fn test_initialize_multisig_packing() {
276        let m = 1;
277        let check = TokenInstruction::InitializeMultisig { m };
278        let packed = check.pack();
279        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
280        assert_eq!(instruction_type, PodTokenInstruction::InitializeMultisig);
281        let pod = decode_instruction_data::<InitializeMultisigData>(&packed).unwrap();
282        assert_eq!(pod.m, m);
283    }
284
285    #[test]
286    fn test_transfer_packing() {
287        let amount = 1;
288        #[allow(deprecated)]
289        let check = TokenInstruction::Transfer { amount };
290        let packed = check.pack();
291
292        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
293        assert_eq!(instruction_type, PodTokenInstruction::Transfer);
294        let pod = decode_instruction_data::<AmountData>(&packed).unwrap();
295        assert_eq!(pod.amount, amount.into());
296    }
297
298    #[test]
299    fn test_approve_packing() {
300        let amount = 1;
301        let check = TokenInstruction::Approve { amount };
302        let packed = check.pack();
303
304        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
305        assert_eq!(instruction_type, PodTokenInstruction::Approve);
306        let pod = decode_instruction_data::<AmountData>(&packed).unwrap();
307        assert_eq!(pod.amount, amount.into());
308    }
309
310    #[test]
311    fn test_revoke_packing() {
312        let check = TokenInstruction::Revoke;
313        let packed = check.pack();
314        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
315        assert_eq!(instruction_type, PodTokenInstruction::Revoke);
316    }
317
318    #[test]
319    fn test_set_authority_packing() {
320        let authority_type = AuthorityType::FreezeAccount;
321        let new_authority = COption::Some(Pubkey::new_from_array([4u8; 32]));
322        let check = TokenInstruction::SetAuthority {
323            authority_type: authority_type.clone(),
324            new_authority,
325        };
326        let packed = check.pack();
327
328        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
329        assert_eq!(instruction_type, PodTokenInstruction::SetAuthority);
330        let (pod, pod_new_authority) =
331            decode_instruction_data_with_coption_pubkey::<SetAuthorityData>(&packed).unwrap();
332        assert_eq!(
333            AuthorityType::from(pod.authority_type).unwrap(),
334            authority_type
335        );
336        assert_eq!(pod_new_authority, new_authority.into());
337    }
338
339    #[test]
340    fn test_mint_to_packing() {
341        let amount = 1;
342        let check = TokenInstruction::MintTo { amount };
343        let packed = check.pack();
344
345        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
346        assert_eq!(instruction_type, PodTokenInstruction::MintTo);
347        let pod = decode_instruction_data::<AmountData>(&packed).unwrap();
348        assert_eq!(pod.amount, amount.into());
349    }
350
351    #[test]
352    fn test_burn_packing() {
353        let amount = 1;
354        let check = TokenInstruction::Burn { amount };
355        let packed = check.pack();
356
357        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
358        assert_eq!(instruction_type, PodTokenInstruction::Burn);
359        let pod = decode_instruction_data::<AmountData>(&packed).unwrap();
360        assert_eq!(pod.amount, amount.into());
361    }
362
363    #[test]
364    fn test_close_account_packing() {
365        let check = TokenInstruction::CloseAccount;
366        let packed = check.pack();
367        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
368        assert_eq!(instruction_type, PodTokenInstruction::CloseAccount);
369    }
370
371    #[test]
372    fn test_freeze_account_packing() {
373        let check = TokenInstruction::FreezeAccount;
374        let packed = check.pack();
375        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
376        assert_eq!(instruction_type, PodTokenInstruction::FreezeAccount);
377    }
378
379    #[test]
380    fn test_thaw_account_packing() {
381        let check = TokenInstruction::ThawAccount;
382        let packed = check.pack();
383        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
384        assert_eq!(instruction_type, PodTokenInstruction::ThawAccount);
385    }
386
387    #[test]
388    fn test_transfer_checked_packing() {
389        let amount = 1;
390        let decimals = 2;
391        let check = TokenInstruction::TransferChecked { amount, decimals };
392        let packed = check.pack();
393
394        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
395        assert_eq!(instruction_type, PodTokenInstruction::TransferChecked);
396        let pod = decode_instruction_data::<AmountCheckedData>(&packed).unwrap();
397        assert_eq!(pod.amount, amount.into());
398        assert_eq!(pod.decimals, decimals);
399    }
400
401    #[test]
402    fn test_approve_checked_packing() {
403        let amount = 1;
404        let decimals = 2;
405
406        let check = TokenInstruction::ApproveChecked { amount, decimals };
407        let packed = check.pack();
408
409        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
410        assert_eq!(instruction_type, PodTokenInstruction::ApproveChecked);
411        let pod = decode_instruction_data::<AmountCheckedData>(&packed).unwrap();
412        assert_eq!(pod.amount, amount.into());
413        assert_eq!(pod.decimals, decimals);
414    }
415
416    #[test]
417    fn test_mint_to_checked_packing() {
418        let amount = 1;
419        let decimals = 2;
420        let check = TokenInstruction::MintToChecked { amount, decimals };
421        let packed = check.pack();
422        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
423        assert_eq!(instruction_type, PodTokenInstruction::MintToChecked);
424        let pod = decode_instruction_data::<AmountCheckedData>(&packed).unwrap();
425        assert_eq!(pod.amount, amount.into());
426        assert_eq!(pod.decimals, decimals);
427    }
428
429    #[test]
430    fn test_burn_checked_packing() {
431        let amount = 1;
432        let decimals = 2;
433        let check = TokenInstruction::BurnChecked { amount, decimals };
434        let packed = check.pack();
435
436        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
437        assert_eq!(instruction_type, PodTokenInstruction::BurnChecked);
438        let pod = decode_instruction_data::<AmountCheckedData>(&packed).unwrap();
439        assert_eq!(pod.amount, amount.into());
440        assert_eq!(pod.decimals, decimals);
441    }
442
443    #[test]
444    fn test_initialize_account2_packing() {
445        let owner = Pubkey::new_from_array([2u8; 32]);
446        let check = TokenInstruction::InitializeAccount2 { owner };
447        let packed = check.pack();
448
449        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
450        assert_eq!(instruction_type, PodTokenInstruction::InitializeAccount2);
451        let pod_owner = decode_instruction_data::<Pubkey>(&packed).unwrap();
452        assert_eq!(*pod_owner, owner);
453    }
454
455    #[test]
456    fn test_sync_native_packing() {
457        let check = TokenInstruction::SyncNative;
458        let packed = check.pack();
459
460        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
461        assert_eq!(instruction_type, PodTokenInstruction::SyncNative);
462    }
463
464    #[test]
465    fn test_initialize_account3_packing() {
466        let owner = Pubkey::new_from_array([2u8; 32]);
467        let check = TokenInstruction::InitializeAccount3 { owner };
468        let packed = check.pack();
469
470        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
471        assert_eq!(instruction_type, PodTokenInstruction::InitializeAccount3);
472        let pod_owner = decode_instruction_data::<Pubkey>(&packed).unwrap();
473        assert_eq!(*pod_owner, owner);
474    }
475
476    #[test]
477    fn test_initialize_multisig2_packing() {
478        let m = 1;
479        let check = TokenInstruction::InitializeMultisig2 { m };
480        let packed = check.pack();
481
482        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
483        assert_eq!(instruction_type, PodTokenInstruction::InitializeMultisig2);
484        let pod = decode_instruction_data::<InitializeMultisigData>(&packed).unwrap();
485        assert_eq!(pod.m, m);
486    }
487
488    #[test]
489    fn test_initialize_mint2_packing() {
490        let decimals = 2;
491        let mint_authority = Pubkey::new_from_array([1u8; 32]);
492        let freeze_authority = COption::None;
493        let check = TokenInstruction::InitializeMint2 {
494            decimals,
495            mint_authority,
496            freeze_authority,
497        };
498        let packed = check.pack();
499
500        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
501        assert_eq!(instruction_type, PodTokenInstruction::InitializeMint2);
502        let (pod, pod_freeze_authority) =
503            decode_instruction_data_with_coption_pubkey::<InitializeMintData>(&packed).unwrap();
504        assert_eq!(pod.decimals, decimals);
505        assert_eq!(pod.mint_authority, mint_authority);
506        assert_eq!(pod_freeze_authority, freeze_authority.into());
507
508        let decimals = 2;
509        let mint_authority = Pubkey::new_from_array([2u8; 32]);
510        let freeze_authority = COption::Some(Pubkey::new_from_array([3u8; 32]));
511        let check = TokenInstruction::InitializeMint2 {
512            decimals,
513            mint_authority,
514            freeze_authority,
515        };
516        let packed = check.pack();
517
518        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
519        assert_eq!(instruction_type, PodTokenInstruction::InitializeMint2);
520        let (pod, pod_freeze_authority) =
521            decode_instruction_data_with_coption_pubkey::<InitializeMintData>(&packed).unwrap();
522        assert_eq!(pod.decimals, decimals);
523        assert_eq!(pod.mint_authority, mint_authority);
524        assert_eq!(pod_freeze_authority, freeze_authority.into());
525    }
526
527    #[test]
528    fn test_get_account_data_size_packing() {
529        let extension_types = vec![];
530        let check = TokenInstruction::GetAccountDataSize {
531            extension_types: extension_types.clone(),
532        };
533        let packed = check.pack();
534
535        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
536        assert_eq!(instruction_type, PodTokenInstruction::GetAccountDataSize);
537        let pod_extension_types = packed[1..]
538            .chunks(std::mem::size_of::<ExtensionType>())
539            .map(ExtensionType::try_from)
540            .collect::<Result<Vec<_>, _>>()
541            .unwrap();
542        assert_eq!(pod_extension_types, extension_types);
543
544        let extension_types = vec![
545            ExtensionType::TransferFeeConfig,
546            ExtensionType::TransferFeeAmount,
547        ];
548        let check = TokenInstruction::GetAccountDataSize {
549            extension_types: extension_types.clone(),
550        };
551        let packed = check.pack();
552
553        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
554        assert_eq!(instruction_type, PodTokenInstruction::GetAccountDataSize);
555        let pod_extension_types = packed[1..]
556            .chunks(std::mem::size_of::<ExtensionType>())
557            .map(ExtensionType::try_from)
558            .collect::<Result<Vec<_>, _>>()
559            .unwrap();
560        assert_eq!(pod_extension_types, extension_types);
561    }
562
563    #[test]
564    fn test_amount_to_ui_amount_packing() {
565        let amount = 42;
566        let check = TokenInstruction::AmountToUiAmount { amount };
567        let packed = check.pack();
568
569        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
570        assert_eq!(instruction_type, PodTokenInstruction::AmountToUiAmount);
571        let data = decode_instruction_data::<AmountData>(&packed).unwrap();
572        assert_eq!(data.amount, amount.into());
573    }
574
575    #[test]
576    fn test_ui_amount_to_amount_packing() {
577        let ui_amount = "0.42";
578        let check = TokenInstruction::UiAmountToAmount { ui_amount };
579        let packed = check.pack();
580
581        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
582        assert_eq!(instruction_type, PodTokenInstruction::UiAmountToAmount);
583        let pod_ui_amount = std::str::from_utf8(&packed[1..]).unwrap();
584        assert_eq!(pod_ui_amount, ui_amount);
585    }
586
587    #[test]
588    fn test_initialize_mint_close_authority_packing() {
589        let close_authority = COption::Some(Pubkey::new_from_array([10u8; 32]));
590        let check = TokenInstruction::InitializeMintCloseAuthority { close_authority };
591        let packed = check.pack();
592
593        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
594        assert_eq!(
595            instruction_type,
596            PodTokenInstruction::InitializeMintCloseAuthority
597        );
598        let (_, pod_close_authority) =
599            decode_instruction_data_with_coption_pubkey::<()>(&packed).unwrap();
600        assert_eq!(pod_close_authority, close_authority.into());
601    }
602
603    #[test]
604    fn test_create_native_mint_packing() {
605        let check = TokenInstruction::CreateNativeMint;
606        let packed = check.pack();
607
608        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
609        assert_eq!(instruction_type, PodTokenInstruction::CreateNativeMint);
610    }
611
612    #[test]
613    fn test_initialize_permanent_delegate_packing() {
614        let delegate = Pubkey::new_from_array([11u8; 32]);
615        let check = TokenInstruction::InitializePermanentDelegate { delegate };
616        let packed = check.pack();
617
618        let instruction_type = decode_instruction_type::<PodTokenInstruction>(&packed).unwrap();
619        assert_eq!(
620            instruction_type,
621            PodTokenInstruction::InitializePermanentDelegate
622        );
623        let pod_delegate = decode_instruction_data::<Pubkey>(&packed).unwrap();
624        assert_eq!(*pod_delegate, delegate);
625    }
626}