Skip to main content

spl_token_interface/
instruction.rs

1//! Instruction types
2
3use {
4    crate::{check_program_account, error::TokenError},
5    solana_instruction::{AccountMeta, Instruction},
6    solana_program_error::ProgramError,
7    solana_program_option::COption,
8    solana_pubkey::Pubkey,
9    solana_sdk_ids::sysvar,
10    std::{convert::TryInto, mem::size_of},
11};
12
13/// Minimum number of multisignature signers (min N)
14pub const MIN_SIGNERS: usize = 1;
15/// Maximum number of multisignature signers (max N)
16pub const MAX_SIGNERS: usize = 11;
17/// Serialized length of a `u64`, for unpacking
18const U64_BYTES: usize = 8;
19
20/// Instructions supported by the token program.
21#[repr(C, u8)]
22#[derive(Clone, Debug, PartialEq)]
23pub enum TokenInstruction<'a> {
24    /// Initializes a new mint and optionally deposits all the newly minted
25    /// tokens in an account.
26    ///
27    /// The `InitializeMint` instruction requires no signers and MUST be
28    /// included within the same Transaction as the system program's
29    /// `CreateAccount` instruction that creates the account being initialized.
30    /// Otherwise another party can acquire ownership of the uninitialized
31    /// account.
32    ///
33    /// Accounts expected by this instruction:
34    ///
35    ///   0. `[writable]` The mint to initialize.
36    ///   1. `[]` Rent sysvar
37    InitializeMint {
38        /// Number of base 10 digits to the right of the decimal place.
39        decimals: u8,
40        /// The authority/multisignature to mint tokens.
41        mint_authority: Pubkey,
42        /// The freeze authority/multisignature of the mint.
43        freeze_authority: COption<Pubkey>,
44    },
45    /// Initializes a new account to hold tokens.  If this account is associated
46    /// with the native mint then the token balance of the initialized account
47    /// will be equal to the amount of SOL in the account. If this account is
48    /// associated with another mint, that mint must be initialized before this
49    /// command can succeed.
50    ///
51    /// The `InitializeAccount` instruction requires no signers and MUST be
52    /// included within the same Transaction as the system program's
53    /// `CreateAccount` instruction that creates the account being initialized.
54    /// Otherwise another party can acquire ownership of the uninitialized
55    /// account.
56    ///
57    /// Accounts expected by this instruction:
58    ///
59    ///   0. `[writable]`  The account to initialize.
60    ///   1. `[]` The mint this account will be associated with.
61    ///   2. `[]` The new account's owner/multisignature.
62    ///   3. `[]` Rent sysvar
63    InitializeAccount,
64    /// Initializes a multisignature account with N provided signers.
65    ///
66    /// Multisignature accounts can used in place of any single owner/delegate
67    /// accounts in any token instruction that require an owner/delegate to be
68    /// present.  The variant field represents the number of signers (M)
69    /// required to validate this multisignature account.
70    ///
71    /// The `InitializeMultisig` instruction requires no signers and MUST be
72    /// included within the same Transaction as the system program's
73    /// `CreateAccount` instruction that creates the account being initialized.
74    /// Otherwise another party can acquire ownership of the uninitialized
75    /// account.
76    ///
77    /// Accounts expected by this instruction:
78    ///
79    ///   0. `[writable]` The multisignature account to initialize.
80    ///   1. `[]` Rent sysvar
81    ///   2. ..`2+N`. `[]` The signer accounts, must equal to N where `1 <= N <=
82    ///      11`.
83    InitializeMultisig {
84        /// The number of signers (M) required to validate this multisignature
85        /// account.
86        m: u8,
87    },
88    /// Transfers tokens from one account to another either directly or via a
89    /// delegate.  If this account is associated with the native mint then equal
90    /// amounts of SOL and Tokens will be transferred to the destination
91    /// account.
92    ///
93    /// Accounts expected by this instruction:
94    ///
95    ///   * Single owner/delegate
96    ///   0. `[writable]` The source account.
97    ///   1. `[writable]` The destination account.
98    ///   2. `[signer]` The source account's owner/delegate.
99    ///
100    ///   * Multisignature owner/delegate
101    ///   0. `[writable]` The source account.
102    ///   1. `[writable]` The destination account.
103    ///   2. `[]` The source account's multisignature owner/delegate.
104    ///   3. ..`3+M` `[signer]` M signer accounts.
105    Transfer {
106        /// The amount of tokens to transfer.
107        amount: u64,
108    },
109    /// Approves a delegate.  A delegate is given the authority over tokens on
110    /// behalf of the source account's owner.
111    ///
112    /// Accounts expected by this instruction:
113    ///
114    ///   * Single owner
115    ///   0. `[writable]` The source account.
116    ///   1. `[]` The delegate.
117    ///   2. `[signer]` The source account owner.
118    ///
119    ///   * Multisignature owner
120    ///   0. `[writable]` The source account.
121    ///   1. `[]` The delegate.
122    ///   2. `[]` The source account's multisignature owner.
123    ///   3. ..`3+M` `[signer]` M signer accounts
124    Approve {
125        /// The amount of tokens the delegate is approved for.
126        amount: u64,
127    },
128    /// Revokes the delegate's authority.
129    ///
130    /// Accounts expected by this instruction:
131    ///
132    ///   * Single owner
133    ///   0. `[writable]` The source account.
134    ///   1. `[signer]` The source account owner.
135    ///
136    ///   * Multisignature owner
137    ///   0. `[writable]` The source account.
138    ///   1. `[]` The source account's multisignature owner.
139    ///   2. ..`2+M` `[signer]` M signer accounts
140    Revoke,
141    /// Sets a new authority of a mint or account.
142    ///
143    /// Accounts expected by this instruction:
144    ///
145    ///   * Single authority
146    ///   0. `[writable]` The mint or account to change the authority of.
147    ///   1. `[signer]` The current authority of the mint or account.
148    ///
149    ///   * Multisignature authority
150    ///   0. `[writable]` The mint or account to change the authority of.
151    ///   1. `[]` The mint's or account's current multisignature authority.
152    ///   2. ..`2+M` `[signer]` M signer accounts
153    SetAuthority {
154        /// The type of authority to update.
155        authority_type: AuthorityType,
156        /// The new authority
157        new_authority: COption<Pubkey>,
158    },
159    /// Mints new tokens to an account.  The native mint does not support
160    /// minting.
161    ///
162    /// Accounts expected by this instruction:
163    ///
164    ///   * Single authority
165    ///   0. `[writable]` The mint.
166    ///   1. `[writable]` The account to mint tokens to.
167    ///   2. `[signer]` The mint's minting authority.
168    ///
169    ///   * Multisignature authority
170    ///   0. `[writable]` The mint.
171    ///   1. `[writable]` The account to mint tokens to.
172    ///   2. `[]` The mint's multisignature mint-tokens authority.
173    ///   3. ..`3+M` `[signer]` M signer accounts.
174    MintTo {
175        /// The amount of new tokens to mint.
176        amount: u64,
177    },
178    /// Burns tokens by removing them from an account.  `Burn` does not support
179    /// accounts associated with the native mint, use `CloseAccount` instead.
180    ///
181    /// Accounts expected by this instruction:
182    ///
183    ///   * Single owner/delegate
184    ///   0. `[writable]` The account to burn from.
185    ///   1. `[writable]` The token mint.
186    ///   2. `[signer]` The account's owner/delegate.
187    ///
188    ///   * Multisignature owner/delegate
189    ///   0. `[writable]` The account to burn from.
190    ///   1. `[writable]` The token mint.
191    ///   2. `[]` The account's multisignature owner/delegate.
192    ///   3. ..`3+M` `[signer]` M signer accounts.
193    Burn {
194        /// The amount of tokens to burn.
195        amount: u64,
196    },
197    /// Close an account by transferring all its SOL to the destination account.
198    /// Non-native accounts may only be closed if its token amount is zero.
199    ///
200    /// Accounts expected by this instruction:
201    ///
202    ///   * Single owner
203    ///   0. `[writable]` The account to close.
204    ///   1. `[writable]` The destination account.
205    ///   2. `[signer]` The account's owner.
206    ///
207    ///   * Multisignature owner
208    ///   0. `[writable]` The account to close.
209    ///   1. `[writable]` The destination account.
210    ///   2. `[]` The account's multisignature owner.
211    ///   3. ..`3+M` `[signer]` M signer accounts.
212    CloseAccount,
213    /// Freeze an Initialized account using the Mint's `freeze_authority` (if
214    /// set).
215    ///
216    /// Accounts expected by this instruction:
217    ///
218    ///   * Single owner
219    ///   0. `[writable]` The account to freeze.
220    ///   1. `[]` The token mint.
221    ///   2. `[signer]` The mint freeze authority.
222    ///
223    ///   * Multisignature owner
224    ///   0. `[writable]` The account to freeze.
225    ///   1. `[]` The token mint.
226    ///   2. `[]` The mint's multisignature freeze authority.
227    ///   3. ..`3+M` `[signer]` M signer accounts.
228    FreezeAccount,
229    /// Thaw a Frozen account using the Mint's `freeze_authority` (if set).
230    ///
231    /// Accounts expected by this instruction:
232    ///
233    ///   * Single owner
234    ///   0. `[writable]` The account to thaw.
235    ///   1. `[]` The token mint.
236    ///   2. `[signer]` The mint freeze authority.
237    ///
238    ///   * Multisignature owner
239    ///   0. `[writable]` The account to thaw.
240    ///   1. `[]` The token mint.
241    ///   2. `[]` The mint's multisignature freeze authority.
242    ///   3. ..`3+M` `[signer]` M signer accounts.
243    ThawAccount,
244
245    /// Transfers tokens from one account to another either directly or via a
246    /// delegate.  If this account is associated with the native mint then equal
247    /// amounts of SOL and Tokens will be transferred to the destination
248    /// account.
249    ///
250    /// This instruction differs from Transfer in that the token mint and
251    /// decimals value is checked by the caller.  This may be useful when
252    /// creating transactions offline or within a hardware wallet.
253    ///
254    /// Accounts expected by this instruction:
255    ///
256    ///   * Single owner/delegate
257    ///   0. `[writable]` The source account.
258    ///   1. `[]` The token mint.
259    ///   2. `[writable]` The destination account.
260    ///   3. `[signer]` The source account's owner/delegate.
261    ///
262    ///   * Multisignature owner/delegate
263    ///   0. `[writable]` The source account.
264    ///   1. `[]` The token mint.
265    ///   2. `[writable]` The destination account.
266    ///   3. `[]` The source account's multisignature owner/delegate.
267    ///   4. ..`4+M` `[signer]` M signer accounts.
268    TransferChecked {
269        /// The amount of tokens to transfer.
270        amount: u64,
271        /// Expected number of base 10 digits to the right of the decimal place.
272        decimals: u8,
273    },
274    /// Approves a delegate.  A delegate is given the authority over tokens on
275    /// behalf of the source account's owner.
276    ///
277    /// This instruction differs from Approve in that the token mint and
278    /// decimals value is checked by the caller.  This may be useful when
279    /// creating transactions offline or within a hardware wallet.
280    ///
281    /// Accounts expected by this instruction:
282    ///
283    ///   * Single owner
284    ///   0. `[writable]` The source account.
285    ///   1. `[]` The token mint.
286    ///   2. `[]` The delegate.
287    ///   3. `[signer]` The source account owner.
288    ///
289    ///   * Multisignature owner
290    ///   0. `[writable]` The source account.
291    ///   1. `[]` The token mint.
292    ///   2. `[]` The delegate.
293    ///   3. `[]` The source account's multisignature owner.
294    ///   4. ..`4+M` `[signer]` M signer accounts
295    ApproveChecked {
296        /// The amount of tokens the delegate is approved for.
297        amount: u64,
298        /// Expected number of base 10 digits to the right of the decimal place.
299        decimals: u8,
300    },
301    /// Mints new tokens to an account.  The native mint does not support
302    /// minting.
303    ///
304    /// This instruction differs from `MintTo` in that the decimals value is
305    /// checked by the caller. This may be useful when creating transactions
306    /// offline or within a hardware wallet.
307    ///
308    /// Accounts expected by this instruction:
309    ///
310    ///   * Single authority
311    ///   0. `[writable]` The mint.
312    ///   1. `[writable]` The account to mint tokens to.
313    ///   2. `[signer]` The mint's minting authority.
314    ///
315    ///   * Multisignature authority
316    ///   0. `[writable]` The mint.
317    ///   1. `[writable]` The account to mint tokens to.
318    ///   2. `[]` The mint's multisignature mint-tokens authority.
319    ///   3. ..`3+M` `[signer]` M signer accounts.
320    MintToChecked {
321        /// The amount of new tokens to mint.
322        amount: u64,
323        /// Expected number of base 10 digits to the right of the decimal place.
324        decimals: u8,
325    },
326    /// Burns tokens by removing them from an account.  `BurnChecked` does not
327    /// support accounts associated with the native mint, use `CloseAccount`
328    /// instead.
329    ///
330    /// This instruction differs from Burn in that the decimals value is checked
331    /// by the caller. This may be useful when creating transactions offline or
332    /// within a hardware wallet.
333    ///
334    /// Accounts expected by this instruction:
335    ///
336    ///   * Single owner/delegate
337    ///   0. `[writable]` The account to burn from.
338    ///   1. `[writable]` The token mint.
339    ///   2. `[signer]` The account's owner/delegate.
340    ///
341    ///   * Multisignature owner/delegate
342    ///   0. `[writable]` The account to burn from.
343    ///   1. `[writable]` The token mint.
344    ///   2. `[]` The account's multisignature owner/delegate.
345    ///   3. ..`3+M` `[signer]` M signer accounts.
346    BurnChecked {
347        /// The amount of tokens to burn.
348        amount: u64,
349        /// Expected number of base 10 digits to the right of the decimal place.
350        decimals: u8,
351    },
352    /// Like [`TokenInstruction::InitializeAccount`], but the owner pubkey is
353    /// passed via instruction data rather than the accounts list. This
354    /// variant may be preferable when using Cross Program Invocation from
355    /// an instruction that does not need the owner's `AccountInfo`
356    /// otherwise.
357    ///
358    /// Accounts expected by this instruction:
359    ///
360    ///   0. `[writable]`  The account to initialize.
361    ///   1. `[]` The mint this account will be associated with.
362    ///   3. `[]` Rent sysvar
363    InitializeAccount2 {
364        /// The new account's owner/multisignature.
365        owner: Pubkey,
366    },
367    /// Given a wrapped / native token account (a token account containing SOL)
368    /// updates its amount field based on the account's underlying `lamports`.
369    /// This is useful if a non-wrapped SOL account uses
370    /// `system_instruction::transfer` to move lamports to a wrapped token
371    /// account, and needs to have its token `amount` field updated.
372    ///
373    /// Accounts expected by this instruction:
374    ///
375    ///   * Using runtime Rent sysvar
376    ///   0. `[writable]`  The native token account to sync with its underlying
377    ///      lamports.
378    ///
379    ///   * Using Rent sysvar account
380    ///   0. `[writable]`  The native token account to sync with its underlying
381    ///      lamports.
382    ///   1. `[]` Rent sysvar.
383    SyncNative,
384    /// Like [`TokenInstruction::InitializeAccount2`], but does not require the
385    /// Rent sysvar to be provided
386    ///
387    /// Accounts expected by this instruction:
388    ///
389    ///   0. `[writable]`  The account to initialize.
390    ///   1. `[]` The mint this account will be associated with.
391    InitializeAccount3 {
392        /// The new account's owner/multisignature.
393        owner: Pubkey,
394    },
395    /// Like [`TokenInstruction::InitializeMultisig`], but does not require the
396    /// Rent sysvar to be provided
397    ///
398    /// Accounts expected by this instruction:
399    ///
400    ///   0. `[writable]` The multisignature account to initialize.
401    ///   1. ..`1+N` `[]` The signer accounts, must equal to N where `1 <= N <=
402    ///      11`.
403    InitializeMultisig2 {
404        /// The number of signers (M) required to validate this multisignature
405        /// account.
406        m: u8,
407    },
408    /// Like [`TokenInstruction::InitializeMint`], but does not require the Rent
409    /// sysvar to be provided
410    ///
411    /// Accounts expected by this instruction:
412    ///
413    ///   0. `[writable]` The mint to initialize.
414    InitializeMint2 {
415        /// Number of base 10 digits to the right of the decimal place.
416        decimals: u8,
417        /// The authority/multisignature to mint tokens.
418        mint_authority: Pubkey,
419        /// The freeze authority/multisignature of the mint.
420        freeze_authority: COption<Pubkey>,
421    },
422    /// Gets the required size of an account for the given mint as a
423    /// little-endian `u64`.
424    ///
425    /// Return data can be fetched using `sol_get_return_data` and deserializing
426    /// the return data as a little-endian `u64`.
427    ///
428    /// Accounts expected by this instruction:
429    ///
430    ///   0. `[]` The mint to calculate for
431    GetAccountDataSize, // typically, there's also data, but this program ignores it
432    /// Initialize the Immutable Owner extension for the given token account
433    ///
434    /// Fails if the account has already been initialized, so must be called
435    /// before `InitializeAccount`.
436    ///
437    /// No-ops in this version of the program, but is included for compatibility
438    /// with the Associated Token Account program.
439    ///
440    /// Accounts expected by this instruction:
441    ///
442    ///   0. `[writable]`  The account to initialize.
443    ///
444    /// Data expected by this instruction:
445    ///   None
446    InitializeImmutableOwner,
447    /// Convert an Amount of tokens to a `UiAmount` string, using the given
448    /// mint. In this version of the program, the mint can only specify the
449    /// number of decimals.
450    ///
451    /// Fails on an invalid mint.
452    ///
453    /// Return data can be fetched using `sol_get_return_data` and deserialized
454    /// with `String::from_utf8`.
455    ///
456    /// Accounts expected by this instruction:
457    ///
458    ///   0. `[]` The mint to calculate for
459    AmountToUiAmount {
460        /// The amount of tokens to reformat.
461        amount: u64,
462    },
463    /// Convert a `UiAmount` of tokens to a little-endian `u64` raw Amount,
464    /// using the given mint. In this version of the program, the mint can
465    /// only specify the number of decimals.
466    ///
467    /// Return data can be fetched using `sol_get_return_data` and deserializing
468    /// the return data as a little-endian `u64`.
469    ///
470    /// Accounts expected by this instruction:
471    ///
472    ///   0. `[]` The mint to calculate for
473    UiAmountToAmount {
474        /// The `ui_amount` of tokens to reformat.
475        ui_amount: &'a str,
476    },
477    /// This instruction is to be used to rescue SOL sent to any `TokenProgram`
478    /// owned account by sending them to any other account, leaving behind only
479    /// lamports for rent exemption.
480    ///
481    /// Accounts expected by this instruction:
482    ///
483    ///   * Single owner/delegate
484    ///   0. `[writable]` The source account.
485    ///   1. `[writable]` The destination account.
486    ///   2. `[signer]` The source account's owner/delegate.
487    ///
488    ///   * Multisignature owner/delegate
489    ///   0. `[writable]` The source account.
490    ///   1. `[writable]` The destination account.
491    ///   2. `[]` The source account's multisignature owner/delegate.
492    ///   3. `..+M` `[signer]` M signer accounts.
493    WithdrawExcessLamports = 38,
494    /// Transfer lamports from a native SOL account to a destination account.
495    ///
496    /// This is useful to unwrap lamports from a wrapped SOL account.
497    ///
498    /// Accounts expected by this instruction:
499    ///
500    ///   * Single owner/delegate
501    ///   0. `[writable]` The source account.
502    ///   1. `[writable]` The destination account.
503    ///   2. `[signer]` The source account's owner/delegate.
504    ///
505    ///   * Multisignature owner/delegate
506    ///   0. `[writable]` The source account.
507    ///   1. `[writable]` The destination account.
508    ///   2. `[]` The source account's multisignature owner/delegate.
509    ///   3. `..+M` `[signer]` M signer accounts.
510    ///
511    /// Data expected by this instruction:
512    ///
513    ///   - `Option<u64>` The amount of lamports to transfer. When an amount is
514    ///     not specified, the entire balance of the source account will be
515    ///     transferred.
516    UnwrapLamports {
517        /// The amount of lamports to transfer.
518        amount: COption<u64>,
519    } = 45,
520    /// Executes a batch of instructions. The instructions to be executed are
521    /// specified in sequence on the instruction data. Each instruction
522    /// provides:
523    ///   - `u8`: number of accounts
524    ///   - `u8`: instruction data length (includes the discriminator)
525    ///   - `u8`: instruction discriminator
526    ///   - `[u8]`: instruction data
527    ///
528    /// Accounts follow a similar pattern, where accounts for each instruction
529    /// are specified in sequence. Therefore, the number of accounts
530    /// expected by this instruction is variable, i.e., it depends on the
531    /// instructions provided.
532    ///
533    /// Both the number of accounts and instruction data length are used to
534    /// identify the slice of accounts and instruction data for each
535    /// instruction. Since the instruction data length is specified as a `u8`,
536    /// the maximum instruction data length is 255 bytes, which is sufficient
537    /// for all current instructions in the program.
538    ///
539    /// When one or more batched instructions write return data, the batch
540    /// instruction returns the data written by the last instruction that does
541    /// so. That instruction does not have to be the final instruction in the
542    /// batch: later instructions that do not write return data leave the last
543    /// written return data unchanged.
544    ///
545    /// Note that it is not sound to have a `batch` instruction that contains
546    /// other `batch` instruction; an error will be raised when this is
547    /// detected.
548    Batch = 255,
549    // Any new variants also need to be added to program-2022 `TokenInstruction`, so that the
550    // latter remains a superset of this instruction set. New variants also need to be added to
551    // token/js/src/instructions/types.ts to maintain @solana/spl-token compatibility
552}
553impl<'a> TokenInstruction<'a> {
554    /// Unpacks a byte buffer into a
555    /// [`TokenInstruction`](enum.TokenInstruction.html).
556    pub fn unpack(input: &'a [u8]) -> Result<Self, ProgramError> {
557        use TokenError::InvalidInstruction;
558
559        let (&tag, rest) = input.split_first().ok_or(InvalidInstruction)?;
560        Ok(match tag {
561            0 => {
562                let (&decimals, rest) = rest.split_first().ok_or(InvalidInstruction)?;
563                let (mint_authority, rest) = Self::unpack_pubkey(rest)?;
564                let (freeze_authority, _rest) = Self::unpack_pubkey_option(rest)?;
565                Self::InitializeMint {
566                    mint_authority,
567                    freeze_authority,
568                    decimals,
569                }
570            }
571            1 => Self::InitializeAccount,
572            2 => {
573                let &m = rest.first().ok_or(InvalidInstruction)?;
574                Self::InitializeMultisig { m }
575            }
576            3 | 4 | 7 | 8 => {
577                let amount = rest
578                    .get(..8)
579                    .and_then(|slice| slice.try_into().ok())
580                    .map(u64::from_le_bytes)
581                    .ok_or(InvalidInstruction)?;
582                match tag {
583                    3 => Self::Transfer { amount },
584                    4 => Self::Approve { amount },
585                    7 => Self::MintTo { amount },
586                    8 => Self::Burn { amount },
587                    _ => unreachable!(),
588                }
589            }
590            5 => Self::Revoke,
591            6 => {
592                let (authority_type, rest) = rest
593                    .split_first()
594                    .ok_or_else(|| ProgramError::from(InvalidInstruction))
595                    .and_then(|(&t, rest)| Ok((AuthorityType::from(t)?, rest)))?;
596                let (new_authority, _rest) = Self::unpack_pubkey_option(rest)?;
597
598                Self::SetAuthority {
599                    authority_type,
600                    new_authority,
601                }
602            }
603            9 => Self::CloseAccount,
604            10 => Self::FreezeAccount,
605            11 => Self::ThawAccount,
606            12 => {
607                let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
608                Self::TransferChecked { amount, decimals }
609            }
610            13 => {
611                let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
612                Self::ApproveChecked { amount, decimals }
613            }
614            14 => {
615                let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
616                Self::MintToChecked { amount, decimals }
617            }
618            15 => {
619                let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
620                Self::BurnChecked { amount, decimals }
621            }
622            16 => {
623                let (owner, _rest) = Self::unpack_pubkey(rest)?;
624                Self::InitializeAccount2 { owner }
625            }
626            17 => Self::SyncNative,
627            18 => {
628                let (owner, _rest) = Self::unpack_pubkey(rest)?;
629                Self::InitializeAccount3 { owner }
630            }
631            19 => {
632                let &m = rest.first().ok_or(InvalidInstruction)?;
633                Self::InitializeMultisig2 { m }
634            }
635            20 => {
636                let (&decimals, rest) = rest.split_first().ok_or(InvalidInstruction)?;
637                let (mint_authority, rest) = Self::unpack_pubkey(rest)?;
638                let (freeze_authority, _rest) = Self::unpack_pubkey_option(rest)?;
639                Self::InitializeMint2 {
640                    mint_authority,
641                    freeze_authority,
642                    decimals,
643                }
644            }
645            21 => Self::GetAccountDataSize,
646            22 => Self::InitializeImmutableOwner,
647            23 => {
648                let (amount, _rest) = Self::unpack_u64(rest)?;
649                Self::AmountToUiAmount { amount }
650            }
651            24 => {
652                let ui_amount = std::str::from_utf8(rest).map_err(|_| InvalidInstruction)?;
653                Self::UiAmountToAmount { ui_amount }
654            }
655            38 => Self::WithdrawExcessLamports,
656            45 => {
657                let (amount, _rest) = Self::unpack_u64_option(rest)?;
658                Self::UnwrapLamports { amount }
659            }
660            255 => Self::Batch,
661            _ => return Err(TokenError::InvalidInstruction.into()),
662        })
663    }
664
665    /// Packs a [`TokenInstruction`](enum.TokenInstruction.html) into a byte
666    /// buffer.
667    pub fn pack(&self) -> Vec<u8> {
668        let mut buf = Vec::with_capacity(size_of::<Self>());
669        match self {
670            &Self::InitializeMint {
671                ref mint_authority,
672                ref freeze_authority,
673                decimals,
674            } => {
675                buf.push(0);
676                buf.push(decimals);
677                buf.extend_from_slice(mint_authority.as_ref());
678                Self::pack_pubkey_option(freeze_authority, &mut buf);
679            }
680            Self::InitializeAccount => buf.push(1),
681            &Self::InitializeMultisig { m } => {
682                buf.push(2);
683                buf.push(m);
684            }
685            &Self::Transfer { amount } => {
686                buf.push(3);
687                buf.extend_from_slice(&amount.to_le_bytes());
688            }
689            &Self::Approve { amount } => {
690                buf.push(4);
691                buf.extend_from_slice(&amount.to_le_bytes());
692            }
693            &Self::MintTo { amount } => {
694                buf.push(7);
695                buf.extend_from_slice(&amount.to_le_bytes());
696            }
697            &Self::Burn { amount } => {
698                buf.push(8);
699                buf.extend_from_slice(&amount.to_le_bytes());
700            }
701            Self::Revoke => buf.push(5),
702            Self::SetAuthority {
703                authority_type,
704                ref new_authority,
705            } => {
706                buf.push(6);
707                buf.push(authority_type.into());
708                Self::pack_pubkey_option(new_authority, &mut buf);
709            }
710            Self::CloseAccount => buf.push(9),
711            Self::FreezeAccount => buf.push(10),
712            Self::ThawAccount => buf.push(11),
713            &Self::TransferChecked { amount, decimals } => {
714                buf.push(12);
715                buf.extend_from_slice(&amount.to_le_bytes());
716                buf.push(decimals);
717            }
718            &Self::ApproveChecked { amount, decimals } => {
719                buf.push(13);
720                buf.extend_from_slice(&amount.to_le_bytes());
721                buf.push(decimals);
722            }
723            &Self::MintToChecked { amount, decimals } => {
724                buf.push(14);
725                buf.extend_from_slice(&amount.to_le_bytes());
726                buf.push(decimals);
727            }
728            &Self::BurnChecked { amount, decimals } => {
729                buf.push(15);
730                buf.extend_from_slice(&amount.to_le_bytes());
731                buf.push(decimals);
732            }
733            &Self::InitializeAccount2 { owner } => {
734                buf.push(16);
735                buf.extend_from_slice(owner.as_ref());
736            }
737            &Self::SyncNative => {
738                buf.push(17);
739            }
740            &Self::InitializeAccount3 { owner } => {
741                buf.push(18);
742                buf.extend_from_slice(owner.as_ref());
743            }
744            &Self::InitializeMultisig2 { m } => {
745                buf.push(19);
746                buf.push(m);
747            }
748            &Self::InitializeMint2 {
749                ref mint_authority,
750                ref freeze_authority,
751                decimals,
752            } => {
753                buf.push(20);
754                buf.push(decimals);
755                buf.extend_from_slice(mint_authority.as_ref());
756                Self::pack_pubkey_option(freeze_authority, &mut buf);
757            }
758            &Self::GetAccountDataSize => {
759                buf.push(21);
760            }
761            &Self::InitializeImmutableOwner => {
762                buf.push(22);
763            }
764            &Self::AmountToUiAmount { amount } => {
765                buf.push(23);
766                buf.extend_from_slice(&amount.to_le_bytes());
767            }
768            Self::UiAmountToAmount { ui_amount } => {
769                buf.push(24);
770                buf.extend_from_slice(ui_amount.as_bytes());
771            }
772            &Self::WithdrawExcessLamports => {
773                buf.push(38);
774            }
775            Self::UnwrapLamports { ref amount } => {
776                buf.push(45);
777                Self::pack_u64_option(amount, &mut buf);
778            }
779            &Self::Batch => {
780                buf.push(255);
781            }
782        };
783        buf
784    }
785
786    fn unpack_pubkey(input: &[u8]) -> Result<(Pubkey, &[u8]), ProgramError> {
787        if input.len() >= 32 {
788            let (key, rest) = input.split_at(32);
789            let pk = Pubkey::try_from(key).map_err(|_| TokenError::InvalidInstruction)?;
790            Ok((pk, rest))
791        } else {
792            Err(TokenError::InvalidInstruction.into())
793        }
794    }
795
796    fn unpack_pubkey_option(input: &[u8]) -> Result<(COption<Pubkey>, &[u8]), ProgramError> {
797        match input.split_first() {
798            Option::Some((&0, rest)) => Ok((COption::None, rest)),
799            Option::Some((&1, rest)) if rest.len() >= 32 => {
800                let (key, rest) = rest.split_at(32);
801                let pk = Pubkey::try_from(key).map_err(|_| TokenError::InvalidInstruction)?;
802                Ok((COption::Some(pk), rest))
803            }
804            _ => Err(TokenError::InvalidInstruction.into()),
805        }
806    }
807
808    fn pack_pubkey_option(value: &COption<Pubkey>, buf: &mut Vec<u8>) {
809        match *value {
810            COption::Some(ref key) => {
811                buf.push(1);
812                buf.extend_from_slice(&key.to_bytes());
813            }
814            COption::None => buf.push(0),
815        }
816    }
817
818    fn unpack_u64(input: &[u8]) -> Result<(u64, &[u8]), ProgramError> {
819        let value = input
820            .get(..U64_BYTES)
821            .and_then(|slice| slice.try_into().ok())
822            .map(u64::from_le_bytes)
823            .ok_or(TokenError::InvalidInstruction)?;
824        Ok((value, &input[U64_BYTES..]))
825    }
826
827    fn unpack_u64_option(input: &[u8]) -> Result<(COption<u64>, &[u8]), ProgramError> {
828        match input.split_first() {
829            Option::Some((&0, rest)) => Ok((COption::None, rest)),
830            Option::Some((&1, rest)) if rest.len() >= 8 => {
831                let (amount, rest) = rest
832                    .split_first_chunk::<8>()
833                    .ok_or(TokenError::InvalidInstruction)?;
834                let v = u64::from_le_bytes(*amount);
835                Ok((COption::Some(v), rest))
836            }
837            _ => Err(TokenError::InvalidInstruction.into()),
838        }
839    }
840
841    fn pack_u64_option(value: &COption<u64>, buf: &mut Vec<u8>) {
842        match *value {
843            COption::Some(ref amount) => {
844                buf.push(1);
845                buf.extend_from_slice(&amount.to_le_bytes());
846            }
847            COption::None => buf.push(0),
848        }
849    }
850
851    fn unpack_amount_decimals(input: &[u8]) -> Result<(u64, u8, &[u8]), ProgramError> {
852        let (amount, rest) = Self::unpack_u64(input)?;
853        let (&decimals, rest) = rest.split_first().ok_or(TokenError::InvalidInstruction)?;
854        Ok((amount, decimals, rest))
855    }
856}
857
858/// Specifies the authority type for `SetAuthority` instructions
859#[repr(u8)]
860#[derive(Clone, Debug, PartialEq)]
861pub enum AuthorityType {
862    /// Authority to mint new tokens
863    MintTokens,
864    /// Authority to freeze any account associated with the Mint
865    FreezeAccount,
866    /// Owner of a given token account
867    AccountOwner,
868    /// Authority to close a token account
869    CloseAccount,
870}
871
872impl AuthorityType {
873    fn into(&self) -> u8 {
874        match self {
875            AuthorityType::MintTokens => 0,
876            AuthorityType::FreezeAccount => 1,
877            AuthorityType::AccountOwner => 2,
878            AuthorityType::CloseAccount => 3,
879        }
880    }
881
882    fn from(index: u8) -> Result<Self, ProgramError> {
883        match index {
884            0 => Ok(AuthorityType::MintTokens),
885            1 => Ok(AuthorityType::FreezeAccount),
886            2 => Ok(AuthorityType::AccountOwner),
887            3 => Ok(AuthorityType::CloseAccount),
888            _ => Err(TokenError::InvalidInstruction.into()),
889        }
890    }
891}
892
893/// Creates a `InitializeMint` instruction.
894pub fn initialize_mint(
895    token_program_id: &Pubkey,
896    mint_pubkey: &Pubkey,
897    mint_authority_pubkey: &Pubkey,
898    freeze_authority_pubkey: Option<&Pubkey>,
899    decimals: u8,
900) -> Result<Instruction, ProgramError> {
901    check_program_account(token_program_id)?;
902    let freeze_authority = freeze_authority_pubkey.cloned().into();
903    let data = TokenInstruction::InitializeMint {
904        mint_authority: *mint_authority_pubkey,
905        freeze_authority,
906        decimals,
907    }
908    .pack();
909
910    let accounts = vec![
911        AccountMeta::new(*mint_pubkey, false),
912        AccountMeta::new_readonly(sysvar::rent::id(), false),
913    ];
914
915    Ok(Instruction {
916        program_id: *token_program_id,
917        accounts,
918        data,
919    })
920}
921
922/// Creates a `InitializeMint2` instruction.
923pub fn initialize_mint2(
924    token_program_id: &Pubkey,
925    mint_pubkey: &Pubkey,
926    mint_authority_pubkey: &Pubkey,
927    freeze_authority_pubkey: Option<&Pubkey>,
928    decimals: u8,
929) -> Result<Instruction, ProgramError> {
930    check_program_account(token_program_id)?;
931    let freeze_authority = freeze_authority_pubkey.cloned().into();
932    let data = TokenInstruction::InitializeMint2 {
933        mint_authority: *mint_authority_pubkey,
934        freeze_authority,
935        decimals,
936    }
937    .pack();
938
939    let accounts = vec![AccountMeta::new(*mint_pubkey, false)];
940
941    Ok(Instruction {
942        program_id: *token_program_id,
943        accounts,
944        data,
945    })
946}
947
948/// Creates a `InitializeAccount` instruction.
949pub fn initialize_account(
950    token_program_id: &Pubkey,
951    account_pubkey: &Pubkey,
952    mint_pubkey: &Pubkey,
953    owner_pubkey: &Pubkey,
954) -> Result<Instruction, ProgramError> {
955    check_program_account(token_program_id)?;
956    let data = TokenInstruction::InitializeAccount.pack();
957
958    let accounts = vec![
959        AccountMeta::new(*account_pubkey, false),
960        AccountMeta::new_readonly(*mint_pubkey, false),
961        AccountMeta::new_readonly(*owner_pubkey, false),
962        AccountMeta::new_readonly(sysvar::rent::id(), false),
963    ];
964
965    Ok(Instruction {
966        program_id: *token_program_id,
967        accounts,
968        data,
969    })
970}
971
972/// Creates a `InitializeAccount2` instruction.
973pub fn initialize_account2(
974    token_program_id: &Pubkey,
975    account_pubkey: &Pubkey,
976    mint_pubkey: &Pubkey,
977    owner_pubkey: &Pubkey,
978) -> Result<Instruction, ProgramError> {
979    check_program_account(token_program_id)?;
980    let data = TokenInstruction::InitializeAccount2 {
981        owner: *owner_pubkey,
982    }
983    .pack();
984
985    let accounts = vec![
986        AccountMeta::new(*account_pubkey, false),
987        AccountMeta::new_readonly(*mint_pubkey, false),
988        AccountMeta::new_readonly(sysvar::rent::id(), false),
989    ];
990
991    Ok(Instruction {
992        program_id: *token_program_id,
993        accounts,
994        data,
995    })
996}
997
998/// Creates a `InitializeAccount3` instruction.
999pub fn initialize_account3(
1000    token_program_id: &Pubkey,
1001    account_pubkey: &Pubkey,
1002    mint_pubkey: &Pubkey,
1003    owner_pubkey: &Pubkey,
1004) -> Result<Instruction, ProgramError> {
1005    check_program_account(token_program_id)?;
1006    let data = TokenInstruction::InitializeAccount3 {
1007        owner: *owner_pubkey,
1008    }
1009    .pack();
1010
1011    let accounts = vec![
1012        AccountMeta::new(*account_pubkey, false),
1013        AccountMeta::new_readonly(*mint_pubkey, false),
1014    ];
1015
1016    Ok(Instruction {
1017        program_id: *token_program_id,
1018        accounts,
1019        data,
1020    })
1021}
1022
1023/// Creates a `InitializeMultisig` instruction.
1024pub fn initialize_multisig(
1025    token_program_id: &Pubkey,
1026    multisig_pubkey: &Pubkey,
1027    signer_pubkeys: &[&Pubkey],
1028    m: u8,
1029) -> Result<Instruction, ProgramError> {
1030    check_program_account(token_program_id)?;
1031    if !is_valid_signer_index(m as usize)
1032        || !is_valid_signer_index(signer_pubkeys.len())
1033        || m as usize > signer_pubkeys.len()
1034    {
1035        return Err(ProgramError::MissingRequiredSignature);
1036    }
1037    let data = TokenInstruction::InitializeMultisig { m }.pack();
1038
1039    let mut accounts = Vec::with_capacity(1 + 1 + signer_pubkeys.len());
1040    accounts.push(AccountMeta::new(*multisig_pubkey, false));
1041    accounts.push(AccountMeta::new_readonly(sysvar::rent::id(), false));
1042    for signer_pubkey in signer_pubkeys.iter() {
1043        accounts.push(AccountMeta::new_readonly(**signer_pubkey, false));
1044    }
1045
1046    Ok(Instruction {
1047        program_id: *token_program_id,
1048        accounts,
1049        data,
1050    })
1051}
1052
1053/// Creates a `InitializeMultisig2` instruction.
1054pub fn initialize_multisig2(
1055    token_program_id: &Pubkey,
1056    multisig_pubkey: &Pubkey,
1057    signer_pubkeys: &[&Pubkey],
1058    m: u8,
1059) -> Result<Instruction, ProgramError> {
1060    check_program_account(token_program_id)?;
1061    if !is_valid_signer_index(m as usize)
1062        || !is_valid_signer_index(signer_pubkeys.len())
1063        || m as usize > signer_pubkeys.len()
1064    {
1065        return Err(ProgramError::MissingRequiredSignature);
1066    }
1067    let data = TokenInstruction::InitializeMultisig2 { m }.pack();
1068
1069    let mut accounts = Vec::with_capacity(1 + 1 + signer_pubkeys.len());
1070    accounts.push(AccountMeta::new(*multisig_pubkey, false));
1071    for signer_pubkey in signer_pubkeys.iter() {
1072        accounts.push(AccountMeta::new_readonly(**signer_pubkey, false));
1073    }
1074
1075    Ok(Instruction {
1076        program_id: *token_program_id,
1077        accounts,
1078        data,
1079    })
1080}
1081
1082/// Creates a `Transfer` instruction.
1083pub fn transfer(
1084    token_program_id: &Pubkey,
1085    source_pubkey: &Pubkey,
1086    destination_pubkey: &Pubkey,
1087    authority_pubkey: &Pubkey,
1088    signer_pubkeys: &[&Pubkey],
1089    amount: u64,
1090) -> Result<Instruction, ProgramError> {
1091    check_program_account(token_program_id)?;
1092    let data = TokenInstruction::Transfer { amount }.pack();
1093
1094    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1095    accounts.push(AccountMeta::new(*source_pubkey, false));
1096    accounts.push(AccountMeta::new(*destination_pubkey, false));
1097    accounts.push(AccountMeta::new_readonly(
1098        *authority_pubkey,
1099        signer_pubkeys.is_empty(),
1100    ));
1101    for signer_pubkey in signer_pubkeys.iter() {
1102        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1103    }
1104
1105    Ok(Instruction {
1106        program_id: *token_program_id,
1107        accounts,
1108        data,
1109    })
1110}
1111
1112/// Creates an `Approve` instruction.
1113pub fn approve(
1114    token_program_id: &Pubkey,
1115    source_pubkey: &Pubkey,
1116    delegate_pubkey: &Pubkey,
1117    owner_pubkey: &Pubkey,
1118    signer_pubkeys: &[&Pubkey],
1119    amount: u64,
1120) -> Result<Instruction, ProgramError> {
1121    check_program_account(token_program_id)?;
1122    let data = TokenInstruction::Approve { amount }.pack();
1123
1124    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1125    accounts.push(AccountMeta::new(*source_pubkey, false));
1126    accounts.push(AccountMeta::new_readonly(*delegate_pubkey, false));
1127    accounts.push(AccountMeta::new_readonly(
1128        *owner_pubkey,
1129        signer_pubkeys.is_empty(),
1130    ));
1131    for signer_pubkey in signer_pubkeys.iter() {
1132        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1133    }
1134
1135    Ok(Instruction {
1136        program_id: *token_program_id,
1137        accounts,
1138        data,
1139    })
1140}
1141
1142/// Creates a `Revoke` instruction.
1143pub fn revoke(
1144    token_program_id: &Pubkey,
1145    source_pubkey: &Pubkey,
1146    owner_pubkey: &Pubkey,
1147    signer_pubkeys: &[&Pubkey],
1148) -> Result<Instruction, ProgramError> {
1149    check_program_account(token_program_id)?;
1150    let data = TokenInstruction::Revoke.pack();
1151
1152    let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len());
1153    accounts.push(AccountMeta::new(*source_pubkey, false));
1154    accounts.push(AccountMeta::new_readonly(
1155        *owner_pubkey,
1156        signer_pubkeys.is_empty(),
1157    ));
1158    for signer_pubkey in signer_pubkeys.iter() {
1159        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1160    }
1161
1162    Ok(Instruction {
1163        program_id: *token_program_id,
1164        accounts,
1165        data,
1166    })
1167}
1168
1169/// Creates a `SetAuthority` instruction.
1170pub fn set_authority(
1171    token_program_id: &Pubkey,
1172    owned_pubkey: &Pubkey,
1173    new_authority_pubkey: Option<&Pubkey>,
1174    authority_type: AuthorityType,
1175    owner_pubkey: &Pubkey,
1176    signer_pubkeys: &[&Pubkey],
1177) -> Result<Instruction, ProgramError> {
1178    check_program_account(token_program_id)?;
1179    let new_authority = new_authority_pubkey.cloned().into();
1180    let data = TokenInstruction::SetAuthority {
1181        authority_type,
1182        new_authority,
1183    }
1184    .pack();
1185
1186    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1187    accounts.push(AccountMeta::new(*owned_pubkey, false));
1188    accounts.push(AccountMeta::new_readonly(
1189        *owner_pubkey,
1190        signer_pubkeys.is_empty(),
1191    ));
1192    for signer_pubkey in signer_pubkeys.iter() {
1193        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1194    }
1195
1196    Ok(Instruction {
1197        program_id: *token_program_id,
1198        accounts,
1199        data,
1200    })
1201}
1202
1203/// Creates a `MintTo` instruction.
1204pub fn mint_to(
1205    token_program_id: &Pubkey,
1206    mint_pubkey: &Pubkey,
1207    account_pubkey: &Pubkey,
1208    owner_pubkey: &Pubkey,
1209    signer_pubkeys: &[&Pubkey],
1210    amount: u64,
1211) -> Result<Instruction, ProgramError> {
1212    check_program_account(token_program_id)?;
1213    let data = TokenInstruction::MintTo { amount }.pack();
1214
1215    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1216    accounts.push(AccountMeta::new(*mint_pubkey, false));
1217    accounts.push(AccountMeta::new(*account_pubkey, false));
1218    accounts.push(AccountMeta::new_readonly(
1219        *owner_pubkey,
1220        signer_pubkeys.is_empty(),
1221    ));
1222    for signer_pubkey in signer_pubkeys.iter() {
1223        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1224    }
1225
1226    Ok(Instruction {
1227        program_id: *token_program_id,
1228        accounts,
1229        data,
1230    })
1231}
1232
1233/// Creates a `Burn` instruction.
1234pub fn burn(
1235    token_program_id: &Pubkey,
1236    account_pubkey: &Pubkey,
1237    mint_pubkey: &Pubkey,
1238    authority_pubkey: &Pubkey,
1239    signer_pubkeys: &[&Pubkey],
1240    amount: u64,
1241) -> Result<Instruction, ProgramError> {
1242    check_program_account(token_program_id)?;
1243    let data = TokenInstruction::Burn { amount }.pack();
1244
1245    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1246    accounts.push(AccountMeta::new(*account_pubkey, false));
1247    accounts.push(AccountMeta::new(*mint_pubkey, false));
1248    accounts.push(AccountMeta::new_readonly(
1249        *authority_pubkey,
1250        signer_pubkeys.is_empty(),
1251    ));
1252    for signer_pubkey in signer_pubkeys.iter() {
1253        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1254    }
1255
1256    Ok(Instruction {
1257        program_id: *token_program_id,
1258        accounts,
1259        data,
1260    })
1261}
1262
1263/// Creates a `CloseAccount` instruction.
1264pub fn close_account(
1265    token_program_id: &Pubkey,
1266    account_pubkey: &Pubkey,
1267    destination_pubkey: &Pubkey,
1268    owner_pubkey: &Pubkey,
1269    signer_pubkeys: &[&Pubkey],
1270) -> Result<Instruction, ProgramError> {
1271    check_program_account(token_program_id)?;
1272    let data = TokenInstruction::CloseAccount.pack();
1273
1274    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1275    accounts.push(AccountMeta::new(*account_pubkey, false));
1276    accounts.push(AccountMeta::new(*destination_pubkey, false));
1277    accounts.push(AccountMeta::new_readonly(
1278        *owner_pubkey,
1279        signer_pubkeys.is_empty(),
1280    ));
1281    for signer_pubkey in signer_pubkeys.iter() {
1282        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1283    }
1284
1285    Ok(Instruction {
1286        program_id: *token_program_id,
1287        accounts,
1288        data,
1289    })
1290}
1291
1292/// Creates a `FreezeAccount` instruction.
1293pub fn freeze_account(
1294    token_program_id: &Pubkey,
1295    account_pubkey: &Pubkey,
1296    mint_pubkey: &Pubkey,
1297    owner_pubkey: &Pubkey,
1298    signer_pubkeys: &[&Pubkey],
1299) -> Result<Instruction, ProgramError> {
1300    check_program_account(token_program_id)?;
1301    let data = TokenInstruction::FreezeAccount.pack();
1302
1303    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1304    accounts.push(AccountMeta::new(*account_pubkey, false));
1305    accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
1306    accounts.push(AccountMeta::new_readonly(
1307        *owner_pubkey,
1308        signer_pubkeys.is_empty(),
1309    ));
1310    for signer_pubkey in signer_pubkeys.iter() {
1311        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1312    }
1313
1314    Ok(Instruction {
1315        program_id: *token_program_id,
1316        accounts,
1317        data,
1318    })
1319}
1320
1321/// Creates a `ThawAccount` instruction.
1322pub fn thaw_account(
1323    token_program_id: &Pubkey,
1324    account_pubkey: &Pubkey,
1325    mint_pubkey: &Pubkey,
1326    owner_pubkey: &Pubkey,
1327    signer_pubkeys: &[&Pubkey],
1328) -> Result<Instruction, ProgramError> {
1329    check_program_account(token_program_id)?;
1330    let data = TokenInstruction::ThawAccount.pack();
1331
1332    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1333    accounts.push(AccountMeta::new(*account_pubkey, false));
1334    accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
1335    accounts.push(AccountMeta::new_readonly(
1336        *owner_pubkey,
1337        signer_pubkeys.is_empty(),
1338    ));
1339    for signer_pubkey in signer_pubkeys.iter() {
1340        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1341    }
1342
1343    Ok(Instruction {
1344        program_id: *token_program_id,
1345        accounts,
1346        data,
1347    })
1348}
1349
1350/// Creates a `TransferChecked` instruction.
1351#[allow(clippy::too_many_arguments)]
1352pub fn transfer_checked(
1353    token_program_id: &Pubkey,
1354    source_pubkey: &Pubkey,
1355    mint_pubkey: &Pubkey,
1356    destination_pubkey: &Pubkey,
1357    authority_pubkey: &Pubkey,
1358    signer_pubkeys: &[&Pubkey],
1359    amount: u64,
1360    decimals: u8,
1361) -> Result<Instruction, ProgramError> {
1362    check_program_account(token_program_id)?;
1363    let data = TokenInstruction::TransferChecked { amount, decimals }.pack();
1364
1365    let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len());
1366    accounts.push(AccountMeta::new(*source_pubkey, false));
1367    accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
1368    accounts.push(AccountMeta::new(*destination_pubkey, false));
1369    accounts.push(AccountMeta::new_readonly(
1370        *authority_pubkey,
1371        signer_pubkeys.is_empty(),
1372    ));
1373    for signer_pubkey in signer_pubkeys.iter() {
1374        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1375    }
1376
1377    Ok(Instruction {
1378        program_id: *token_program_id,
1379        accounts,
1380        data,
1381    })
1382}
1383
1384/// Creates an `ApproveChecked` instruction.
1385#[allow(clippy::too_many_arguments)]
1386pub fn approve_checked(
1387    token_program_id: &Pubkey,
1388    source_pubkey: &Pubkey,
1389    mint_pubkey: &Pubkey,
1390    delegate_pubkey: &Pubkey,
1391    owner_pubkey: &Pubkey,
1392    signer_pubkeys: &[&Pubkey],
1393    amount: u64,
1394    decimals: u8,
1395) -> Result<Instruction, ProgramError> {
1396    check_program_account(token_program_id)?;
1397    let data = TokenInstruction::ApproveChecked { amount, decimals }.pack();
1398
1399    let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len());
1400    accounts.push(AccountMeta::new(*source_pubkey, false));
1401    accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
1402    accounts.push(AccountMeta::new_readonly(*delegate_pubkey, false));
1403    accounts.push(AccountMeta::new_readonly(
1404        *owner_pubkey,
1405        signer_pubkeys.is_empty(),
1406    ));
1407    for signer_pubkey in signer_pubkeys.iter() {
1408        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1409    }
1410
1411    Ok(Instruction {
1412        program_id: *token_program_id,
1413        accounts,
1414        data,
1415    })
1416}
1417
1418/// Creates a `MintToChecked` instruction.
1419pub fn mint_to_checked(
1420    token_program_id: &Pubkey,
1421    mint_pubkey: &Pubkey,
1422    account_pubkey: &Pubkey,
1423    owner_pubkey: &Pubkey,
1424    signer_pubkeys: &[&Pubkey],
1425    amount: u64,
1426    decimals: u8,
1427) -> Result<Instruction, ProgramError> {
1428    check_program_account(token_program_id)?;
1429    let data = TokenInstruction::MintToChecked { amount, decimals }.pack();
1430
1431    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1432    accounts.push(AccountMeta::new(*mint_pubkey, false));
1433    accounts.push(AccountMeta::new(*account_pubkey, false));
1434    accounts.push(AccountMeta::new_readonly(
1435        *owner_pubkey,
1436        signer_pubkeys.is_empty(),
1437    ));
1438    for signer_pubkey in signer_pubkeys.iter() {
1439        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1440    }
1441
1442    Ok(Instruction {
1443        program_id: *token_program_id,
1444        accounts,
1445        data,
1446    })
1447}
1448
1449/// Creates a `BurnChecked` instruction.
1450pub fn burn_checked(
1451    token_program_id: &Pubkey,
1452    account_pubkey: &Pubkey,
1453    mint_pubkey: &Pubkey,
1454    authority_pubkey: &Pubkey,
1455    signer_pubkeys: &[&Pubkey],
1456    amount: u64,
1457    decimals: u8,
1458) -> Result<Instruction, ProgramError> {
1459    check_program_account(token_program_id)?;
1460    let data = TokenInstruction::BurnChecked { amount, decimals }.pack();
1461
1462    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1463    accounts.push(AccountMeta::new(*account_pubkey, false));
1464    accounts.push(AccountMeta::new(*mint_pubkey, false));
1465    accounts.push(AccountMeta::new_readonly(
1466        *authority_pubkey,
1467        signer_pubkeys.is_empty(),
1468    ));
1469    for signer_pubkey in signer_pubkeys.iter() {
1470        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1471    }
1472
1473    Ok(Instruction {
1474        program_id: *token_program_id,
1475        accounts,
1476        data,
1477    })
1478}
1479
1480/// Creates a `SyncNative` instruction
1481pub fn sync_native(
1482    token_program_id: &Pubkey,
1483    account_pubkey: &Pubkey,
1484) -> Result<Instruction, ProgramError> {
1485    check_program_account(token_program_id)?;
1486
1487    Ok(Instruction {
1488        program_id: *token_program_id,
1489        accounts: vec![AccountMeta::new(*account_pubkey, false)],
1490        data: TokenInstruction::SyncNative.pack(),
1491    })
1492}
1493
1494/// Creates a `SyncNative` instruction with the Rent sysvar account
1495/// added to the accounts list.
1496pub fn sync_native_with_rent_sysvar(
1497    token_program_id: &Pubkey,
1498    account_pubkey: &Pubkey,
1499) -> Result<Instruction, ProgramError> {
1500    let mut instruction = sync_native(token_program_id, account_pubkey)?;
1501    instruction
1502        .accounts
1503        .push(AccountMeta::new_readonly(sysvar::rent::id(), false));
1504
1505    Ok(instruction)
1506}
1507
1508/// Creates a `GetAccountDataSize` instruction
1509pub fn get_account_data_size(
1510    token_program_id: &Pubkey,
1511    mint_pubkey: &Pubkey,
1512) -> Result<Instruction, ProgramError> {
1513    check_program_account(token_program_id)?;
1514
1515    Ok(Instruction {
1516        program_id: *token_program_id,
1517        accounts: vec![AccountMeta::new_readonly(*mint_pubkey, false)],
1518        data: TokenInstruction::GetAccountDataSize.pack(),
1519    })
1520}
1521
1522/// Creates a `InitializeImmutableOwner` instruction
1523pub fn initialize_immutable_owner(
1524    token_program_id: &Pubkey,
1525    account_pubkey: &Pubkey,
1526) -> Result<Instruction, ProgramError> {
1527    check_program_account(token_program_id)?;
1528    Ok(Instruction {
1529        program_id: *token_program_id,
1530        accounts: vec![AccountMeta::new(*account_pubkey, false)],
1531        data: TokenInstruction::InitializeImmutableOwner.pack(),
1532    })
1533}
1534
1535/// Creates an `AmountToUiAmount` instruction
1536pub fn amount_to_ui_amount(
1537    token_program_id: &Pubkey,
1538    mint_pubkey: &Pubkey,
1539    amount: u64,
1540) -> Result<Instruction, ProgramError> {
1541    check_program_account(token_program_id)?;
1542
1543    Ok(Instruction {
1544        program_id: *token_program_id,
1545        accounts: vec![AccountMeta::new_readonly(*mint_pubkey, false)],
1546        data: TokenInstruction::AmountToUiAmount { amount }.pack(),
1547    })
1548}
1549
1550/// Creates a `UiAmountToAmount` instruction
1551pub fn ui_amount_to_amount(
1552    token_program_id: &Pubkey,
1553    mint_pubkey: &Pubkey,
1554    ui_amount: &str,
1555) -> Result<Instruction, ProgramError> {
1556    check_program_account(token_program_id)?;
1557
1558    Ok(Instruction {
1559        program_id: *token_program_id,
1560        accounts: vec![AccountMeta::new_readonly(*mint_pubkey, false)],
1561        data: TokenInstruction::UiAmountToAmount { ui_amount }.pack(),
1562    })
1563}
1564
1565/// Creates a `WithdrawExcessLamports` instruction
1566pub fn withdraw_excess_lamports(
1567    token_program_id: &Pubkey,
1568    account_pubkey: &Pubkey,
1569    destination_pubkey: &Pubkey,
1570    authority_pubkey: &Pubkey,
1571    signer_pubkeys: &[&Pubkey],
1572) -> Result<Instruction, ProgramError> {
1573    check_program_account(token_program_id)?;
1574    let data = TokenInstruction::WithdrawExcessLamports.pack();
1575
1576    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1577    accounts.push(AccountMeta::new(*account_pubkey, false));
1578    accounts.push(AccountMeta::new(*destination_pubkey, false));
1579    accounts.push(AccountMeta::new_readonly(
1580        *authority_pubkey,
1581        signer_pubkeys.is_empty(),
1582    ));
1583    for signer_pubkey in signer_pubkeys.iter() {
1584        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1585    }
1586
1587    Ok(Instruction {
1588        program_id: *token_program_id,
1589        accounts,
1590        data,
1591    })
1592}
1593
1594/// Creates a `UnwrapLamports` instruction
1595pub fn unwrap_lamports(
1596    token_program_id: &Pubkey,
1597    account_pubkey: &Pubkey,
1598    destination_pubkey: &Pubkey,
1599    authority_pubkey: &Pubkey,
1600    signer_pubkeys: &[&Pubkey],
1601    amount: Option<u64>,
1602) -> Result<Instruction, ProgramError> {
1603    check_program_account(token_program_id)?;
1604
1605    let amount = amount.into();
1606    let data = TokenInstruction::UnwrapLamports { amount }.pack();
1607
1608    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1609    accounts.push(AccountMeta::new(*account_pubkey, false));
1610    accounts.push(AccountMeta::new(*destination_pubkey, false));
1611    accounts.push(AccountMeta::new_readonly(
1612        *authority_pubkey,
1613        signer_pubkeys.is_empty(),
1614    ));
1615    for signer_pubkey in signer_pubkeys.iter() {
1616        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1617    }
1618
1619    Ok(Instruction {
1620        program_id: *token_program_id,
1621        accounts,
1622        data,
1623    })
1624}
1625
1626/// Creates a `Batch` instruction
1627pub fn batch(
1628    token_program_id: &Pubkey,
1629    instructions: &[Instruction],
1630) -> Result<Instruction, ProgramError> {
1631    check_program_account(token_program_id)?;
1632
1633    let mut data: Vec<u8> = TokenInstruction::Batch.pack();
1634    let mut accounts: Vec<AccountMeta> = vec![];
1635
1636    for instruction in instructions {
1637        if token_program_id != &instruction.program_id {
1638            return Err(ProgramError::IncorrectProgramId);
1639        }
1640
1641        data.push(instruction.accounts.len() as u8);
1642
1643        if instruction.data.len() > u8::MAX as usize {
1644            return Err(ProgramError::InvalidInstructionData);
1645        }
1646
1647        data.push(instruction.data.len() as u8);
1648
1649        data.extend_from_slice(&instruction.data);
1650        accounts.extend_from_slice(&instruction.accounts);
1651    }
1652
1653    Ok(Instruction {
1654        program_id: *token_program_id,
1655        data,
1656        accounts,
1657    })
1658}
1659
1660/// Utility function that checks index is between `MIN_SIGNERS` and
1661/// `MAX_SIGNERS`
1662pub fn is_valid_signer_index(index: usize) -> bool {
1663    (MIN_SIGNERS..=MAX_SIGNERS).contains(&index)
1664}
1665
1666#[cfg(test)]
1667mod test {
1668    use {super::*, proptest::prelude::*};
1669
1670    #[test]
1671    fn test_instruction_packing() {
1672        let check = TokenInstruction::InitializeMint {
1673            decimals: 2,
1674            mint_authority: Pubkey::new_from_array([1u8; 32]),
1675            freeze_authority: COption::None,
1676        };
1677        let packed = check.pack();
1678        let mut expect = Vec::from([0u8, 2]);
1679        expect.extend_from_slice(&[1u8; 32]);
1680        expect.extend_from_slice(&[0]);
1681        assert_eq!(packed, expect);
1682        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1683        assert_eq!(unpacked, check);
1684
1685        let check = TokenInstruction::InitializeMint {
1686            decimals: 2,
1687            mint_authority: Pubkey::new_from_array([2u8; 32]),
1688            freeze_authority: COption::Some(Pubkey::new_from_array([3u8; 32])),
1689        };
1690        let packed = check.pack();
1691        let mut expect = vec![0u8, 2];
1692        expect.extend_from_slice(&[2u8; 32]);
1693        expect.extend_from_slice(&[1]);
1694        expect.extend_from_slice(&[3u8; 32]);
1695        assert_eq!(packed, expect);
1696        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1697        assert_eq!(unpacked, check);
1698
1699        let check = TokenInstruction::InitializeAccount;
1700        let packed = check.pack();
1701        let expect = Vec::from([1u8]);
1702        assert_eq!(packed, expect);
1703        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1704        assert_eq!(unpacked, check);
1705
1706        let check = TokenInstruction::InitializeMultisig { m: 1 };
1707        let packed = check.pack();
1708        let expect = Vec::from([2u8, 1]);
1709        assert_eq!(packed, expect);
1710        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1711        assert_eq!(unpacked, check);
1712
1713        let check = TokenInstruction::Transfer { amount: 1 };
1714        let packed = check.pack();
1715        let expect = Vec::from([3u8, 1, 0, 0, 0, 0, 0, 0, 0]);
1716        assert_eq!(packed, expect);
1717        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1718        assert_eq!(unpacked, check);
1719
1720        let check = TokenInstruction::Approve { amount: 1 };
1721        let packed = check.pack();
1722        let expect = Vec::from([4u8, 1, 0, 0, 0, 0, 0, 0, 0]);
1723        assert_eq!(packed, expect);
1724        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1725        assert_eq!(unpacked, check);
1726
1727        let check = TokenInstruction::Revoke;
1728        let packed = check.pack();
1729        let expect = Vec::from([5u8]);
1730        assert_eq!(packed, expect);
1731        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1732        assert_eq!(unpacked, check);
1733
1734        let check = TokenInstruction::SetAuthority {
1735            authority_type: AuthorityType::FreezeAccount,
1736            new_authority: COption::Some(Pubkey::new_from_array([4u8; 32])),
1737        };
1738        let packed = check.pack();
1739        let mut expect = Vec::from([6u8, 1]);
1740        expect.extend_from_slice(&[1]);
1741        expect.extend_from_slice(&[4u8; 32]);
1742        assert_eq!(packed, expect);
1743        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1744        assert_eq!(unpacked, check);
1745
1746        let check = TokenInstruction::MintTo { amount: 1 };
1747        let packed = check.pack();
1748        let expect = Vec::from([7u8, 1, 0, 0, 0, 0, 0, 0, 0]);
1749        assert_eq!(packed, expect);
1750        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1751        assert_eq!(unpacked, check);
1752
1753        let check = TokenInstruction::Burn { amount: 1 };
1754        let packed = check.pack();
1755        let expect = Vec::from([8u8, 1, 0, 0, 0, 0, 0, 0, 0]);
1756        assert_eq!(packed, expect);
1757        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1758        assert_eq!(unpacked, check);
1759
1760        let check = TokenInstruction::CloseAccount;
1761        let packed = check.pack();
1762        let expect = Vec::from([9u8]);
1763        assert_eq!(packed, expect);
1764        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1765        assert_eq!(unpacked, check);
1766
1767        let check = TokenInstruction::FreezeAccount;
1768        let packed = check.pack();
1769        let expect = Vec::from([10u8]);
1770        assert_eq!(packed, expect);
1771        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1772        assert_eq!(unpacked, check);
1773
1774        let check = TokenInstruction::ThawAccount;
1775        let packed = check.pack();
1776        let expect = Vec::from([11u8]);
1777        assert_eq!(packed, expect);
1778        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1779        assert_eq!(unpacked, check);
1780
1781        let check = TokenInstruction::TransferChecked {
1782            amount: 1,
1783            decimals: 2,
1784        };
1785        let packed = check.pack();
1786        let expect = Vec::from([12u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
1787        assert_eq!(packed, expect);
1788        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1789        assert_eq!(unpacked, check);
1790
1791        let check = TokenInstruction::ApproveChecked {
1792            amount: 1,
1793            decimals: 2,
1794        };
1795        let packed = check.pack();
1796        let expect = Vec::from([13u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
1797        assert_eq!(packed, expect);
1798        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1799        assert_eq!(unpacked, check);
1800
1801        let check = TokenInstruction::MintToChecked {
1802            amount: 1,
1803            decimals: 2,
1804        };
1805        let packed = check.pack();
1806        let expect = Vec::from([14u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
1807        assert_eq!(packed, expect);
1808        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1809        assert_eq!(unpacked, check);
1810
1811        let check = TokenInstruction::BurnChecked {
1812            amount: 1,
1813            decimals: 2,
1814        };
1815        let packed = check.pack();
1816        let expect = Vec::from([15u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
1817        assert_eq!(packed, expect);
1818        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1819        assert_eq!(unpacked, check);
1820
1821        let check = TokenInstruction::InitializeAccount2 {
1822            owner: Pubkey::new_from_array([2u8; 32]),
1823        };
1824        let packed = check.pack();
1825        let mut expect = vec![16u8];
1826        expect.extend_from_slice(&[2u8; 32]);
1827        assert_eq!(packed, expect);
1828        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1829        assert_eq!(unpacked, check);
1830
1831        let check = TokenInstruction::SyncNative;
1832        let packed = check.pack();
1833        let expect = vec![17u8];
1834        assert_eq!(packed, expect);
1835        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1836        assert_eq!(unpacked, check);
1837
1838        let check = TokenInstruction::InitializeAccount3 {
1839            owner: Pubkey::new_from_array([2u8; 32]),
1840        };
1841        let packed = check.pack();
1842        let mut expect = vec![18u8];
1843        expect.extend_from_slice(&[2u8; 32]);
1844        assert_eq!(packed, expect);
1845        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1846        assert_eq!(unpacked, check);
1847
1848        let check = TokenInstruction::InitializeMultisig2 { m: 1 };
1849        let packed = check.pack();
1850        let expect = Vec::from([19u8, 1]);
1851        assert_eq!(packed, expect);
1852        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1853        assert_eq!(unpacked, check);
1854
1855        let check = TokenInstruction::InitializeMint2 {
1856            decimals: 2,
1857            mint_authority: Pubkey::new_from_array([1u8; 32]),
1858            freeze_authority: COption::None,
1859        };
1860        let packed = check.pack();
1861        let mut expect = Vec::from([20u8, 2]);
1862        expect.extend_from_slice(&[1u8; 32]);
1863        expect.extend_from_slice(&[0]);
1864        assert_eq!(packed, expect);
1865        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1866        assert_eq!(unpacked, check);
1867
1868        let check = TokenInstruction::InitializeMint2 {
1869            decimals: 2,
1870            mint_authority: Pubkey::new_from_array([2u8; 32]),
1871            freeze_authority: COption::Some(Pubkey::new_from_array([3u8; 32])),
1872        };
1873        let packed = check.pack();
1874        let mut expect = vec![20u8, 2];
1875        expect.extend_from_slice(&[2u8; 32]);
1876        expect.extend_from_slice(&[1]);
1877        expect.extend_from_slice(&[3u8; 32]);
1878        assert_eq!(packed, expect);
1879        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1880        assert_eq!(unpacked, check);
1881
1882        let check = TokenInstruction::GetAccountDataSize;
1883        let packed = check.pack();
1884        let expect = vec![21u8];
1885        assert_eq!(packed, expect);
1886        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1887        assert_eq!(unpacked, check);
1888
1889        let check = TokenInstruction::InitializeImmutableOwner;
1890        let packed = check.pack();
1891        let expect = vec![22u8];
1892        assert_eq!(packed, expect);
1893        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1894        assert_eq!(unpacked, check);
1895
1896        let check = TokenInstruction::AmountToUiAmount { amount: 42 };
1897        let packed = check.pack();
1898        let expect = vec![23u8, 42, 0, 0, 0, 0, 0, 0, 0];
1899        assert_eq!(packed, expect);
1900        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1901        assert_eq!(unpacked, check);
1902
1903        let check = TokenInstruction::UiAmountToAmount { ui_amount: "0.42" };
1904        let packed = check.pack();
1905        let expect = vec![24u8, 48, 46, 52, 50];
1906        assert_eq!(packed, expect);
1907        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1908        assert_eq!(unpacked, check);
1909
1910        let check = TokenInstruction::WithdrawExcessLamports;
1911        let packed = check.pack();
1912        let expect = vec![38u8];
1913        assert_eq!(packed, expect);
1914        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1915        assert_eq!(unpacked, check);
1916
1917        let check = TokenInstruction::UnwrapLamports {
1918            amount: COption::Some(42),
1919        };
1920        let packed = check.pack();
1921        let expect = vec![45u8, 1, 42, 0, 0, 0, 0, 0, 0, 0];
1922        assert_eq!(packed, expect);
1923        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1924        assert_eq!(unpacked, check);
1925
1926        let check = TokenInstruction::UnwrapLamports {
1927            amount: COption::None,
1928        };
1929        let packed = check.pack();
1930        let expect = vec![45u8, 0];
1931        assert_eq!(packed, expect);
1932        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1933        assert_eq!(unpacked, check);
1934
1935        let check = TokenInstruction::Batch;
1936        let packed = check.pack();
1937        let expect = vec![255u8];
1938        assert_eq!(packed, expect);
1939        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1940        assert_eq!(unpacked, check);
1941    }
1942
1943    #[test]
1944    fn test_instruction_unpack_panic() {
1945        for i in 0..255u8 {
1946            for j in 1..10 {
1947                let mut data = vec![0; j];
1948                data[0] = i;
1949                let _no_panic = TokenInstruction::unpack(&data);
1950            }
1951        }
1952    }
1953
1954    proptest! {
1955        #![proptest_config(ProptestConfig::with_cases(1024))]
1956        #[test]
1957        fn test_instruction_unpack_proptest(
1958            data in prop::collection::vec(any::<u8>(), 0..255)
1959        ) {
1960            let _no_panic = TokenInstruction::unpack(&data);
1961        }
1962    }
1963}