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)]
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 freeze.
235    ///   1. `[]` The token mint.
236    ///   2. `[signer]` The mint freeze authority.
237    ///
238    ///   * Multisignature owner
239    ///   0. `[writable]` The account to freeze.
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 [`InitializeAccount`], but the owner pubkey is passed via
353    /// instruction data rather than the accounts list. This variant may be
354    /// preferable when using Cross Program Invocation from an instruction
355    /// that does not need the owner's `AccountInfo` otherwise.
356    ///
357    /// Accounts expected by this instruction:
358    ///
359    ///   0. `[writable]`  The account to initialize.
360    ///   1. `[]` The mint this account will be associated with.
361    ///   3. `[]` Rent sysvar
362    InitializeAccount2 {
363        /// The new account's owner/multisignature.
364        owner: Pubkey,
365    },
366    /// Given a wrapped / native token account (a token account containing SOL)
367    /// updates its amount field based on the account's underlying `lamports`.
368    /// This is useful if a non-wrapped SOL account uses
369    /// `system_instruction::transfer` to move lamports to a wrapped token
370    /// account, and needs to have its token `amount` field updated.
371    ///
372    /// Accounts expected by this instruction:
373    ///
374    ///   0. `[writable]`  The native token account to sync with its underlying
375    ///      lamports.
376    SyncNative,
377    /// Like [`InitializeAccount2`], but does not require the Rent sysvar to be
378    /// provided
379    ///
380    /// Accounts expected by this instruction:
381    ///
382    ///   0. `[writable]`  The account to initialize.
383    ///   1. `[]` The mint this account will be associated with.
384    InitializeAccount3 {
385        /// The new account's owner/multisignature.
386        owner: Pubkey,
387    },
388    /// Like [`InitializeMultisig`], but does not require the Rent sysvar to be
389    /// provided
390    ///
391    /// Accounts expected by this instruction:
392    ///
393    ///   0. `[writable]` The multisignature account to initialize.
394    ///   1. ..`1+N` `[]` The signer accounts, must equal to N where `1 <= N <=
395    ///      11`.
396    InitializeMultisig2 {
397        /// The number of signers (M) required to validate this multisignature
398        /// account.
399        m: u8,
400    },
401    /// Like [`InitializeMint`], but does not require the Rent sysvar to be
402    /// provided
403    ///
404    /// Accounts expected by this instruction:
405    ///
406    ///   0. `[writable]` The mint to initialize.
407    InitializeMint2 {
408        /// Number of base 10 digits to the right of the decimal place.
409        decimals: u8,
410        /// The authority/multisignature to mint tokens.
411        mint_authority: Pubkey,
412        /// The freeze authority/multisignature of the mint.
413        freeze_authority: COption<Pubkey>,
414    },
415    /// Gets the required size of an account for the given mint as a
416    /// little-endian `u64`.
417    ///
418    /// Return data can be fetched using `sol_get_return_data` and deserializing
419    /// the return data as a little-endian `u64`.
420    ///
421    /// Accounts expected by this instruction:
422    ///
423    ///   0. `[]` The mint to calculate for
424    GetAccountDataSize, // typically, there's also data, but this program ignores it
425    /// Initialize the Immutable Owner extension for the given token account
426    ///
427    /// Fails if the account has already been initialized, so must be called
428    /// before `InitializeAccount`.
429    ///
430    /// No-ops in this version of the program, but is included for compatibility
431    /// with the Associated Token Account program.
432    ///
433    /// Accounts expected by this instruction:
434    ///
435    ///   0. `[writable]`  The account to initialize.
436    ///
437    /// Data expected by this instruction:
438    ///   None
439    InitializeImmutableOwner,
440    /// Convert an Amount of tokens to a `UiAmount` string, using the given
441    /// mint. In this version of the program, the mint can only specify the
442    /// number of decimals.
443    ///
444    /// Fails on an invalid mint.
445    ///
446    /// Return data can be fetched using `sol_get_return_data` and deserialized
447    /// with `String::from_utf8`.
448    ///
449    /// Accounts expected by this instruction:
450    ///
451    ///   0. `[]` The mint to calculate for
452    AmountToUiAmount {
453        /// The amount of tokens to reformat.
454        amount: u64,
455    },
456    /// Convert a `UiAmount` of tokens to a little-endian `u64` raw Amount,
457    /// using the given mint. In this version of the program, the mint can
458    /// only specify the number of decimals.
459    ///
460    /// Return data can be fetched using `sol_get_return_data` and deserializing
461    /// the return data as a little-endian `u64`.
462    ///
463    /// Accounts expected by this instruction:
464    ///
465    ///   0. `[]` The mint to calculate for
466    UiAmountToAmount {
467        /// The `ui_amount` of tokens to reformat.
468        ui_amount: &'a str,
469    },
470    // Any new variants also need to be added to program-2022 `TokenInstruction`, so that the
471    // latter remains a superset of this instruction set. New variants also need to be added to
472    // token/js/src/instructions/types.ts to maintain @solana/spl-token compatibility
473}
474impl<'a> TokenInstruction<'a> {
475    /// Unpacks a byte buffer into a
476    /// [`TokenInstruction`](enum.TokenInstruction.html).
477    pub fn unpack(input: &'a [u8]) -> Result<Self, ProgramError> {
478        use TokenError::InvalidInstruction;
479
480        let (&tag, rest) = input.split_first().ok_or(InvalidInstruction)?;
481        Ok(match tag {
482            0 => {
483                let (&decimals, rest) = rest.split_first().ok_or(InvalidInstruction)?;
484                let (mint_authority, rest) = Self::unpack_pubkey(rest)?;
485                let (freeze_authority, _rest) = Self::unpack_pubkey_option(rest)?;
486                Self::InitializeMint {
487                    mint_authority,
488                    freeze_authority,
489                    decimals,
490                }
491            }
492            1 => Self::InitializeAccount,
493            2 => {
494                let &m = rest.first().ok_or(InvalidInstruction)?;
495                Self::InitializeMultisig { m }
496            }
497            3 | 4 | 7 | 8 => {
498                let amount = rest
499                    .get(..8)
500                    .and_then(|slice| slice.try_into().ok())
501                    .map(u64::from_le_bytes)
502                    .ok_or(InvalidInstruction)?;
503                match tag {
504                    3 => Self::Transfer { amount },
505                    4 => Self::Approve { amount },
506                    7 => Self::MintTo { amount },
507                    8 => Self::Burn { amount },
508                    _ => unreachable!(),
509                }
510            }
511            5 => Self::Revoke,
512            6 => {
513                let (authority_type, rest) = rest
514                    .split_first()
515                    .ok_or_else(|| ProgramError::from(InvalidInstruction))
516                    .and_then(|(&t, rest)| Ok((AuthorityType::from(t)?, rest)))?;
517                let (new_authority, _rest) = Self::unpack_pubkey_option(rest)?;
518
519                Self::SetAuthority {
520                    authority_type,
521                    new_authority,
522                }
523            }
524            9 => Self::CloseAccount,
525            10 => Self::FreezeAccount,
526            11 => Self::ThawAccount,
527            12 => {
528                let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
529                Self::TransferChecked { amount, decimals }
530            }
531            13 => {
532                let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
533                Self::ApproveChecked { amount, decimals }
534            }
535            14 => {
536                let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
537                Self::MintToChecked { amount, decimals }
538            }
539            15 => {
540                let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
541                Self::BurnChecked { amount, decimals }
542            }
543            16 => {
544                let (owner, _rest) = Self::unpack_pubkey(rest)?;
545                Self::InitializeAccount2 { owner }
546            }
547            17 => Self::SyncNative,
548            18 => {
549                let (owner, _rest) = Self::unpack_pubkey(rest)?;
550                Self::InitializeAccount3 { owner }
551            }
552            19 => {
553                let &m = rest.first().ok_or(InvalidInstruction)?;
554                Self::InitializeMultisig2 { m }
555            }
556            20 => {
557                let (&decimals, rest) = rest.split_first().ok_or(InvalidInstruction)?;
558                let (mint_authority, rest) = Self::unpack_pubkey(rest)?;
559                let (freeze_authority, _rest) = Self::unpack_pubkey_option(rest)?;
560                Self::InitializeMint2 {
561                    mint_authority,
562                    freeze_authority,
563                    decimals,
564                }
565            }
566            21 => Self::GetAccountDataSize,
567            22 => Self::InitializeImmutableOwner,
568            23 => {
569                let (amount, _rest) = Self::unpack_u64(rest)?;
570                Self::AmountToUiAmount { amount }
571            }
572            24 => {
573                let ui_amount = std::str::from_utf8(rest).map_err(|_| InvalidInstruction)?;
574                Self::UiAmountToAmount { ui_amount }
575            }
576            _ => return Err(TokenError::InvalidInstruction.into()),
577        })
578    }
579
580    /// Packs a [`TokenInstruction`](enum.TokenInstruction.html) into a byte
581    /// buffer.
582    pub fn pack(&self) -> Vec<u8> {
583        let mut buf = Vec::with_capacity(size_of::<Self>());
584        match self {
585            &Self::InitializeMint {
586                ref mint_authority,
587                ref freeze_authority,
588                decimals,
589            } => {
590                buf.push(0);
591                buf.push(decimals);
592                buf.extend_from_slice(mint_authority.as_ref());
593                Self::pack_pubkey_option(freeze_authority, &mut buf);
594            }
595            Self::InitializeAccount => buf.push(1),
596            &Self::InitializeMultisig { m } => {
597                buf.push(2);
598                buf.push(m);
599            }
600            &Self::Transfer { amount } => {
601                buf.push(3);
602                buf.extend_from_slice(&amount.to_le_bytes());
603            }
604            &Self::Approve { amount } => {
605                buf.push(4);
606                buf.extend_from_slice(&amount.to_le_bytes());
607            }
608            &Self::MintTo { amount } => {
609                buf.push(7);
610                buf.extend_from_slice(&amount.to_le_bytes());
611            }
612            &Self::Burn { amount } => {
613                buf.push(8);
614                buf.extend_from_slice(&amount.to_le_bytes());
615            }
616            Self::Revoke => buf.push(5),
617            Self::SetAuthority {
618                authority_type,
619                ref new_authority,
620            } => {
621                buf.push(6);
622                buf.push(authority_type.into());
623                Self::pack_pubkey_option(new_authority, &mut buf);
624            }
625            Self::CloseAccount => buf.push(9),
626            Self::FreezeAccount => buf.push(10),
627            Self::ThawAccount => buf.push(11),
628            &Self::TransferChecked { amount, decimals } => {
629                buf.push(12);
630                buf.extend_from_slice(&amount.to_le_bytes());
631                buf.push(decimals);
632            }
633            &Self::ApproveChecked { amount, decimals } => {
634                buf.push(13);
635                buf.extend_from_slice(&amount.to_le_bytes());
636                buf.push(decimals);
637            }
638            &Self::MintToChecked { amount, decimals } => {
639                buf.push(14);
640                buf.extend_from_slice(&amount.to_le_bytes());
641                buf.push(decimals);
642            }
643            &Self::BurnChecked { amount, decimals } => {
644                buf.push(15);
645                buf.extend_from_slice(&amount.to_le_bytes());
646                buf.push(decimals);
647            }
648            &Self::InitializeAccount2 { owner } => {
649                buf.push(16);
650                buf.extend_from_slice(owner.as_ref());
651            }
652            &Self::SyncNative => {
653                buf.push(17);
654            }
655            &Self::InitializeAccount3 { owner } => {
656                buf.push(18);
657                buf.extend_from_slice(owner.as_ref());
658            }
659            &Self::InitializeMultisig2 { m } => {
660                buf.push(19);
661                buf.push(m);
662            }
663            &Self::InitializeMint2 {
664                ref mint_authority,
665                ref freeze_authority,
666                decimals,
667            } => {
668                buf.push(20);
669                buf.push(decimals);
670                buf.extend_from_slice(mint_authority.as_ref());
671                Self::pack_pubkey_option(freeze_authority, &mut buf);
672            }
673            &Self::GetAccountDataSize => {
674                buf.push(21);
675            }
676            &Self::InitializeImmutableOwner => {
677                buf.push(22);
678            }
679            &Self::AmountToUiAmount { amount } => {
680                buf.push(23);
681                buf.extend_from_slice(&amount.to_le_bytes());
682            }
683            Self::UiAmountToAmount { ui_amount } => {
684                buf.push(24);
685                buf.extend_from_slice(ui_amount.as_bytes());
686            }
687        };
688        buf
689    }
690
691    fn unpack_pubkey(input: &[u8]) -> Result<(Pubkey, &[u8]), ProgramError> {
692        if input.len() >= 32 {
693            let (key, rest) = input.split_at(32);
694            let pk = Pubkey::try_from(key).map_err(|_| TokenError::InvalidInstruction)?;
695            Ok((pk, rest))
696        } else {
697            Err(TokenError::InvalidInstruction.into())
698        }
699    }
700
701    fn unpack_pubkey_option(input: &[u8]) -> Result<(COption<Pubkey>, &[u8]), ProgramError> {
702        match input.split_first() {
703            Option::Some((&0, rest)) => Ok((COption::None, rest)),
704            Option::Some((&1, rest)) if rest.len() >= 32 => {
705                let (key, rest) = rest.split_at(32);
706                let pk = Pubkey::try_from(key).map_err(|_| TokenError::InvalidInstruction)?;
707                Ok((COption::Some(pk), rest))
708            }
709            _ => Err(TokenError::InvalidInstruction.into()),
710        }
711    }
712
713    fn pack_pubkey_option(value: &COption<Pubkey>, buf: &mut Vec<u8>) {
714        match *value {
715            COption::Some(ref key) => {
716                buf.push(1);
717                buf.extend_from_slice(&key.to_bytes());
718            }
719            COption::None => buf.push(0),
720        }
721    }
722
723    fn unpack_u64(input: &[u8]) -> Result<(u64, &[u8]), ProgramError> {
724        let value = input
725            .get(..U64_BYTES)
726            .and_then(|slice| slice.try_into().ok())
727            .map(u64::from_le_bytes)
728            .ok_or(TokenError::InvalidInstruction)?;
729        Ok((value, &input[U64_BYTES..]))
730    }
731
732    fn unpack_amount_decimals(input: &[u8]) -> Result<(u64, u8, &[u8]), ProgramError> {
733        let (amount, rest) = Self::unpack_u64(input)?;
734        let (&decimals, rest) = rest.split_first().ok_or(TokenError::InvalidInstruction)?;
735        Ok((amount, decimals, rest))
736    }
737}
738
739/// Specifies the authority type for `SetAuthority` instructions
740#[repr(u8)]
741#[derive(Clone, Debug, PartialEq)]
742pub enum AuthorityType {
743    /// Authority to mint new tokens
744    MintTokens,
745    /// Authority to freeze any account associated with the Mint
746    FreezeAccount,
747    /// Owner of a given token account
748    AccountOwner,
749    /// Authority to close a token account
750    CloseAccount,
751}
752
753impl AuthorityType {
754    fn into(&self) -> u8 {
755        match self {
756            AuthorityType::MintTokens => 0,
757            AuthorityType::FreezeAccount => 1,
758            AuthorityType::AccountOwner => 2,
759            AuthorityType::CloseAccount => 3,
760        }
761    }
762
763    fn from(index: u8) -> Result<Self, ProgramError> {
764        match index {
765            0 => Ok(AuthorityType::MintTokens),
766            1 => Ok(AuthorityType::FreezeAccount),
767            2 => Ok(AuthorityType::AccountOwner),
768            3 => Ok(AuthorityType::CloseAccount),
769            _ => Err(TokenError::InvalidInstruction.into()),
770        }
771    }
772}
773
774/// Creates a `InitializeMint` instruction.
775pub fn initialize_mint(
776    token_program_id: &Pubkey,
777    mint_pubkey: &Pubkey,
778    mint_authority_pubkey: &Pubkey,
779    freeze_authority_pubkey: Option<&Pubkey>,
780    decimals: u8,
781) -> Result<Instruction, ProgramError> {
782    check_program_account(token_program_id)?;
783    let freeze_authority = freeze_authority_pubkey.cloned().into();
784    let data = TokenInstruction::InitializeMint {
785        mint_authority: *mint_authority_pubkey,
786        freeze_authority,
787        decimals,
788    }
789    .pack();
790
791    let accounts = vec![
792        AccountMeta::new(*mint_pubkey, false),
793        AccountMeta::new_readonly(sysvar::rent::id(), false),
794    ];
795
796    Ok(Instruction {
797        program_id: *token_program_id,
798        accounts,
799        data,
800    })
801}
802
803/// Creates a `InitializeMint2` instruction.
804pub fn initialize_mint2(
805    token_program_id: &Pubkey,
806    mint_pubkey: &Pubkey,
807    mint_authority_pubkey: &Pubkey,
808    freeze_authority_pubkey: Option<&Pubkey>,
809    decimals: u8,
810) -> Result<Instruction, ProgramError> {
811    check_program_account(token_program_id)?;
812    let freeze_authority = freeze_authority_pubkey.cloned().into();
813    let data = TokenInstruction::InitializeMint2 {
814        mint_authority: *mint_authority_pubkey,
815        freeze_authority,
816        decimals,
817    }
818    .pack();
819
820    let accounts = vec![AccountMeta::new(*mint_pubkey, false)];
821
822    Ok(Instruction {
823        program_id: *token_program_id,
824        accounts,
825        data,
826    })
827}
828
829/// Creates a `InitializeAccount` instruction.
830pub fn initialize_account(
831    token_program_id: &Pubkey,
832    account_pubkey: &Pubkey,
833    mint_pubkey: &Pubkey,
834    owner_pubkey: &Pubkey,
835) -> Result<Instruction, ProgramError> {
836    check_program_account(token_program_id)?;
837    let data = TokenInstruction::InitializeAccount.pack();
838
839    let accounts = vec![
840        AccountMeta::new(*account_pubkey, false),
841        AccountMeta::new_readonly(*mint_pubkey, false),
842        AccountMeta::new_readonly(*owner_pubkey, false),
843        AccountMeta::new_readonly(sysvar::rent::id(), false),
844    ];
845
846    Ok(Instruction {
847        program_id: *token_program_id,
848        accounts,
849        data,
850    })
851}
852
853/// Creates a `InitializeAccount2` instruction.
854pub fn initialize_account2(
855    token_program_id: &Pubkey,
856    account_pubkey: &Pubkey,
857    mint_pubkey: &Pubkey,
858    owner_pubkey: &Pubkey,
859) -> Result<Instruction, ProgramError> {
860    check_program_account(token_program_id)?;
861    let data = TokenInstruction::InitializeAccount2 {
862        owner: *owner_pubkey,
863    }
864    .pack();
865
866    let accounts = vec![
867        AccountMeta::new(*account_pubkey, false),
868        AccountMeta::new_readonly(*mint_pubkey, false),
869        AccountMeta::new_readonly(sysvar::rent::id(), false),
870    ];
871
872    Ok(Instruction {
873        program_id: *token_program_id,
874        accounts,
875        data,
876    })
877}
878
879/// Creates a `InitializeAccount3` instruction.
880pub fn initialize_account3(
881    token_program_id: &Pubkey,
882    account_pubkey: &Pubkey,
883    mint_pubkey: &Pubkey,
884    owner_pubkey: &Pubkey,
885) -> Result<Instruction, ProgramError> {
886    check_program_account(token_program_id)?;
887    let data = TokenInstruction::InitializeAccount3 {
888        owner: *owner_pubkey,
889    }
890    .pack();
891
892    let accounts = vec![
893        AccountMeta::new(*account_pubkey, false),
894        AccountMeta::new_readonly(*mint_pubkey, false),
895    ];
896
897    Ok(Instruction {
898        program_id: *token_program_id,
899        accounts,
900        data,
901    })
902}
903
904/// Creates a `InitializeMultisig` instruction.
905pub fn initialize_multisig(
906    token_program_id: &Pubkey,
907    multisig_pubkey: &Pubkey,
908    signer_pubkeys: &[&Pubkey],
909    m: u8,
910) -> Result<Instruction, ProgramError> {
911    check_program_account(token_program_id)?;
912    if !is_valid_signer_index(m as usize)
913        || !is_valid_signer_index(signer_pubkeys.len())
914        || m as usize > signer_pubkeys.len()
915    {
916        return Err(ProgramError::MissingRequiredSignature);
917    }
918    let data = TokenInstruction::InitializeMultisig { m }.pack();
919
920    let mut accounts = Vec::with_capacity(1 + 1 + signer_pubkeys.len());
921    accounts.push(AccountMeta::new(*multisig_pubkey, false));
922    accounts.push(AccountMeta::new_readonly(sysvar::rent::id(), false));
923    for signer_pubkey in signer_pubkeys.iter() {
924        accounts.push(AccountMeta::new_readonly(**signer_pubkey, false));
925    }
926
927    Ok(Instruction {
928        program_id: *token_program_id,
929        accounts,
930        data,
931    })
932}
933
934/// Creates a `InitializeMultisig2` instruction.
935pub fn initialize_multisig2(
936    token_program_id: &Pubkey,
937    multisig_pubkey: &Pubkey,
938    signer_pubkeys: &[&Pubkey],
939    m: u8,
940) -> Result<Instruction, ProgramError> {
941    check_program_account(token_program_id)?;
942    if !is_valid_signer_index(m as usize)
943        || !is_valid_signer_index(signer_pubkeys.len())
944        || m as usize > signer_pubkeys.len()
945    {
946        return Err(ProgramError::MissingRequiredSignature);
947    }
948    let data = TokenInstruction::InitializeMultisig2 { m }.pack();
949
950    let mut accounts = Vec::with_capacity(1 + 1 + signer_pubkeys.len());
951    accounts.push(AccountMeta::new(*multisig_pubkey, false));
952    for signer_pubkey in signer_pubkeys.iter() {
953        accounts.push(AccountMeta::new_readonly(**signer_pubkey, false));
954    }
955
956    Ok(Instruction {
957        program_id: *token_program_id,
958        accounts,
959        data,
960    })
961}
962
963/// Creates a `Transfer` instruction.
964pub fn transfer(
965    token_program_id: &Pubkey,
966    source_pubkey: &Pubkey,
967    destination_pubkey: &Pubkey,
968    authority_pubkey: &Pubkey,
969    signer_pubkeys: &[&Pubkey],
970    amount: u64,
971) -> Result<Instruction, ProgramError> {
972    check_program_account(token_program_id)?;
973    let data = TokenInstruction::Transfer { amount }.pack();
974
975    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
976    accounts.push(AccountMeta::new(*source_pubkey, false));
977    accounts.push(AccountMeta::new(*destination_pubkey, false));
978    accounts.push(AccountMeta::new_readonly(
979        *authority_pubkey,
980        signer_pubkeys.is_empty(),
981    ));
982    for signer_pubkey in signer_pubkeys.iter() {
983        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
984    }
985
986    Ok(Instruction {
987        program_id: *token_program_id,
988        accounts,
989        data,
990    })
991}
992
993/// Creates an `Approve` instruction.
994pub fn approve(
995    token_program_id: &Pubkey,
996    source_pubkey: &Pubkey,
997    delegate_pubkey: &Pubkey,
998    owner_pubkey: &Pubkey,
999    signer_pubkeys: &[&Pubkey],
1000    amount: u64,
1001) -> Result<Instruction, ProgramError> {
1002    check_program_account(token_program_id)?;
1003    let data = TokenInstruction::Approve { amount }.pack();
1004
1005    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1006    accounts.push(AccountMeta::new(*source_pubkey, false));
1007    accounts.push(AccountMeta::new_readonly(*delegate_pubkey, false));
1008    accounts.push(AccountMeta::new_readonly(
1009        *owner_pubkey,
1010        signer_pubkeys.is_empty(),
1011    ));
1012    for signer_pubkey in signer_pubkeys.iter() {
1013        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1014    }
1015
1016    Ok(Instruction {
1017        program_id: *token_program_id,
1018        accounts,
1019        data,
1020    })
1021}
1022
1023/// Creates a `Revoke` instruction.
1024pub fn revoke(
1025    token_program_id: &Pubkey,
1026    source_pubkey: &Pubkey,
1027    owner_pubkey: &Pubkey,
1028    signer_pubkeys: &[&Pubkey],
1029) -> Result<Instruction, ProgramError> {
1030    check_program_account(token_program_id)?;
1031    let data = TokenInstruction::Revoke.pack();
1032
1033    let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len());
1034    accounts.push(AccountMeta::new(*source_pubkey, false));
1035    accounts.push(AccountMeta::new_readonly(
1036        *owner_pubkey,
1037        signer_pubkeys.is_empty(),
1038    ));
1039    for signer_pubkey in signer_pubkeys.iter() {
1040        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1041    }
1042
1043    Ok(Instruction {
1044        program_id: *token_program_id,
1045        accounts,
1046        data,
1047    })
1048}
1049
1050/// Creates a `SetAuthority` instruction.
1051pub fn set_authority(
1052    token_program_id: &Pubkey,
1053    owned_pubkey: &Pubkey,
1054    new_authority_pubkey: Option<&Pubkey>,
1055    authority_type: AuthorityType,
1056    owner_pubkey: &Pubkey,
1057    signer_pubkeys: &[&Pubkey],
1058) -> Result<Instruction, ProgramError> {
1059    check_program_account(token_program_id)?;
1060    let new_authority = new_authority_pubkey.cloned().into();
1061    let data = TokenInstruction::SetAuthority {
1062        authority_type,
1063        new_authority,
1064    }
1065    .pack();
1066
1067    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1068    accounts.push(AccountMeta::new(*owned_pubkey, false));
1069    accounts.push(AccountMeta::new_readonly(
1070        *owner_pubkey,
1071        signer_pubkeys.is_empty(),
1072    ));
1073    for signer_pubkey in signer_pubkeys.iter() {
1074        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1075    }
1076
1077    Ok(Instruction {
1078        program_id: *token_program_id,
1079        accounts,
1080        data,
1081    })
1082}
1083
1084/// Creates a `MintTo` instruction.
1085pub fn mint_to(
1086    token_program_id: &Pubkey,
1087    mint_pubkey: &Pubkey,
1088    account_pubkey: &Pubkey,
1089    owner_pubkey: &Pubkey,
1090    signer_pubkeys: &[&Pubkey],
1091    amount: u64,
1092) -> Result<Instruction, ProgramError> {
1093    check_program_account(token_program_id)?;
1094    let data = TokenInstruction::MintTo { amount }.pack();
1095
1096    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1097    accounts.push(AccountMeta::new(*mint_pubkey, false));
1098    accounts.push(AccountMeta::new(*account_pubkey, false));
1099    accounts.push(AccountMeta::new_readonly(
1100        *owner_pubkey,
1101        signer_pubkeys.is_empty(),
1102    ));
1103    for signer_pubkey in signer_pubkeys.iter() {
1104        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1105    }
1106
1107    Ok(Instruction {
1108        program_id: *token_program_id,
1109        accounts,
1110        data,
1111    })
1112}
1113
1114/// Creates a `Burn` instruction.
1115pub fn burn(
1116    token_program_id: &Pubkey,
1117    account_pubkey: &Pubkey,
1118    mint_pubkey: &Pubkey,
1119    authority_pubkey: &Pubkey,
1120    signer_pubkeys: &[&Pubkey],
1121    amount: u64,
1122) -> Result<Instruction, ProgramError> {
1123    check_program_account(token_program_id)?;
1124    let data = TokenInstruction::Burn { amount }.pack();
1125
1126    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1127    accounts.push(AccountMeta::new(*account_pubkey, false));
1128    accounts.push(AccountMeta::new(*mint_pubkey, false));
1129    accounts.push(AccountMeta::new_readonly(
1130        *authority_pubkey,
1131        signer_pubkeys.is_empty(),
1132    ));
1133    for signer_pubkey in signer_pubkeys.iter() {
1134        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1135    }
1136
1137    Ok(Instruction {
1138        program_id: *token_program_id,
1139        accounts,
1140        data,
1141    })
1142}
1143
1144/// Creates a `CloseAccount` instruction.
1145pub fn close_account(
1146    token_program_id: &Pubkey,
1147    account_pubkey: &Pubkey,
1148    destination_pubkey: &Pubkey,
1149    owner_pubkey: &Pubkey,
1150    signer_pubkeys: &[&Pubkey],
1151) -> Result<Instruction, ProgramError> {
1152    check_program_account(token_program_id)?;
1153    let data = TokenInstruction::CloseAccount.pack();
1154
1155    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1156    accounts.push(AccountMeta::new(*account_pubkey, false));
1157    accounts.push(AccountMeta::new(*destination_pubkey, false));
1158    accounts.push(AccountMeta::new_readonly(
1159        *owner_pubkey,
1160        signer_pubkeys.is_empty(),
1161    ));
1162    for signer_pubkey in signer_pubkeys.iter() {
1163        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1164    }
1165
1166    Ok(Instruction {
1167        program_id: *token_program_id,
1168        accounts,
1169        data,
1170    })
1171}
1172
1173/// Creates a `FreezeAccount` instruction.
1174pub fn freeze_account(
1175    token_program_id: &Pubkey,
1176    account_pubkey: &Pubkey,
1177    mint_pubkey: &Pubkey,
1178    owner_pubkey: &Pubkey,
1179    signer_pubkeys: &[&Pubkey],
1180) -> Result<Instruction, ProgramError> {
1181    check_program_account(token_program_id)?;
1182    let data = TokenInstruction::FreezeAccount.pack();
1183
1184    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1185    accounts.push(AccountMeta::new(*account_pubkey, false));
1186    accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
1187    accounts.push(AccountMeta::new_readonly(
1188        *owner_pubkey,
1189        signer_pubkeys.is_empty(),
1190    ));
1191    for signer_pubkey in signer_pubkeys.iter() {
1192        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1193    }
1194
1195    Ok(Instruction {
1196        program_id: *token_program_id,
1197        accounts,
1198        data,
1199    })
1200}
1201
1202/// Creates a `ThawAccount` instruction.
1203pub fn thaw_account(
1204    token_program_id: &Pubkey,
1205    account_pubkey: &Pubkey,
1206    mint_pubkey: &Pubkey,
1207    owner_pubkey: &Pubkey,
1208    signer_pubkeys: &[&Pubkey],
1209) -> Result<Instruction, ProgramError> {
1210    check_program_account(token_program_id)?;
1211    let data = TokenInstruction::ThawAccount.pack();
1212
1213    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1214    accounts.push(AccountMeta::new(*account_pubkey, false));
1215    accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
1216    accounts.push(AccountMeta::new_readonly(
1217        *owner_pubkey,
1218        signer_pubkeys.is_empty(),
1219    ));
1220    for signer_pubkey in signer_pubkeys.iter() {
1221        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1222    }
1223
1224    Ok(Instruction {
1225        program_id: *token_program_id,
1226        accounts,
1227        data,
1228    })
1229}
1230
1231/// Creates a `TransferChecked` instruction.
1232#[allow(clippy::too_many_arguments)]
1233pub fn transfer_checked(
1234    token_program_id: &Pubkey,
1235    source_pubkey: &Pubkey,
1236    mint_pubkey: &Pubkey,
1237    destination_pubkey: &Pubkey,
1238    authority_pubkey: &Pubkey,
1239    signer_pubkeys: &[&Pubkey],
1240    amount: u64,
1241    decimals: u8,
1242) -> Result<Instruction, ProgramError> {
1243    check_program_account(token_program_id)?;
1244    let data = TokenInstruction::TransferChecked { amount, decimals }.pack();
1245
1246    let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len());
1247    accounts.push(AccountMeta::new(*source_pubkey, false));
1248    accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
1249    accounts.push(AccountMeta::new(*destination_pubkey, false));
1250    accounts.push(AccountMeta::new_readonly(
1251        *authority_pubkey,
1252        signer_pubkeys.is_empty(),
1253    ));
1254    for signer_pubkey in signer_pubkeys.iter() {
1255        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1256    }
1257
1258    Ok(Instruction {
1259        program_id: *token_program_id,
1260        accounts,
1261        data,
1262    })
1263}
1264
1265/// Creates an `ApproveChecked` instruction.
1266#[allow(clippy::too_many_arguments)]
1267pub fn approve_checked(
1268    token_program_id: &Pubkey,
1269    source_pubkey: &Pubkey,
1270    mint_pubkey: &Pubkey,
1271    delegate_pubkey: &Pubkey,
1272    owner_pubkey: &Pubkey,
1273    signer_pubkeys: &[&Pubkey],
1274    amount: u64,
1275    decimals: u8,
1276) -> Result<Instruction, ProgramError> {
1277    check_program_account(token_program_id)?;
1278    let data = TokenInstruction::ApproveChecked { amount, decimals }.pack();
1279
1280    let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len());
1281    accounts.push(AccountMeta::new(*source_pubkey, false));
1282    accounts.push(AccountMeta::new_readonly(*mint_pubkey, false));
1283    accounts.push(AccountMeta::new_readonly(*delegate_pubkey, false));
1284    accounts.push(AccountMeta::new_readonly(
1285        *owner_pubkey,
1286        signer_pubkeys.is_empty(),
1287    ));
1288    for signer_pubkey in signer_pubkeys.iter() {
1289        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1290    }
1291
1292    Ok(Instruction {
1293        program_id: *token_program_id,
1294        accounts,
1295        data,
1296    })
1297}
1298
1299/// Creates a `MintToChecked` instruction.
1300pub fn mint_to_checked(
1301    token_program_id: &Pubkey,
1302    mint_pubkey: &Pubkey,
1303    account_pubkey: &Pubkey,
1304    owner_pubkey: &Pubkey,
1305    signer_pubkeys: &[&Pubkey],
1306    amount: u64,
1307    decimals: u8,
1308) -> Result<Instruction, ProgramError> {
1309    check_program_account(token_program_id)?;
1310    let data = TokenInstruction::MintToChecked { amount, decimals }.pack();
1311
1312    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1313    accounts.push(AccountMeta::new(*mint_pubkey, false));
1314    accounts.push(AccountMeta::new(*account_pubkey, false));
1315    accounts.push(AccountMeta::new_readonly(
1316        *owner_pubkey,
1317        signer_pubkeys.is_empty(),
1318    ));
1319    for signer_pubkey in signer_pubkeys.iter() {
1320        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1321    }
1322
1323    Ok(Instruction {
1324        program_id: *token_program_id,
1325        accounts,
1326        data,
1327    })
1328}
1329
1330/// Creates a `BurnChecked` instruction.
1331pub fn burn_checked(
1332    token_program_id: &Pubkey,
1333    account_pubkey: &Pubkey,
1334    mint_pubkey: &Pubkey,
1335    authority_pubkey: &Pubkey,
1336    signer_pubkeys: &[&Pubkey],
1337    amount: u64,
1338    decimals: u8,
1339) -> Result<Instruction, ProgramError> {
1340    check_program_account(token_program_id)?;
1341    let data = TokenInstruction::BurnChecked { amount, decimals }.pack();
1342
1343    let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
1344    accounts.push(AccountMeta::new(*account_pubkey, false));
1345    accounts.push(AccountMeta::new(*mint_pubkey, false));
1346    accounts.push(AccountMeta::new_readonly(
1347        *authority_pubkey,
1348        signer_pubkeys.is_empty(),
1349    ));
1350    for signer_pubkey in signer_pubkeys.iter() {
1351        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
1352    }
1353
1354    Ok(Instruction {
1355        program_id: *token_program_id,
1356        accounts,
1357        data,
1358    })
1359}
1360
1361/// Creates a `SyncNative` instruction
1362pub fn sync_native(
1363    token_program_id: &Pubkey,
1364    account_pubkey: &Pubkey,
1365) -> Result<Instruction, ProgramError> {
1366    check_program_account(token_program_id)?;
1367
1368    Ok(Instruction {
1369        program_id: *token_program_id,
1370        accounts: vec![AccountMeta::new(*account_pubkey, false)],
1371        data: TokenInstruction::SyncNative.pack(),
1372    })
1373}
1374
1375/// Creates a `GetAccountDataSize` instruction
1376pub fn get_account_data_size(
1377    token_program_id: &Pubkey,
1378    mint_pubkey: &Pubkey,
1379) -> Result<Instruction, ProgramError> {
1380    check_program_account(token_program_id)?;
1381
1382    Ok(Instruction {
1383        program_id: *token_program_id,
1384        accounts: vec![AccountMeta::new_readonly(*mint_pubkey, false)],
1385        data: TokenInstruction::GetAccountDataSize.pack(),
1386    })
1387}
1388
1389/// Creates a `InitializeImmutableOwner` instruction
1390pub fn initialize_immutable_owner(
1391    token_program_id: &Pubkey,
1392    account_pubkey: &Pubkey,
1393) -> Result<Instruction, ProgramError> {
1394    check_program_account(token_program_id)?;
1395    Ok(Instruction {
1396        program_id: *token_program_id,
1397        accounts: vec![AccountMeta::new(*account_pubkey, false)],
1398        data: TokenInstruction::InitializeImmutableOwner.pack(),
1399    })
1400}
1401
1402/// Creates an `AmountToUiAmount` instruction
1403pub fn amount_to_ui_amount(
1404    token_program_id: &Pubkey,
1405    mint_pubkey: &Pubkey,
1406    amount: u64,
1407) -> Result<Instruction, ProgramError> {
1408    check_program_account(token_program_id)?;
1409
1410    Ok(Instruction {
1411        program_id: *token_program_id,
1412        accounts: vec![AccountMeta::new_readonly(*mint_pubkey, false)],
1413        data: TokenInstruction::AmountToUiAmount { amount }.pack(),
1414    })
1415}
1416
1417/// Creates a `UiAmountToAmount` instruction
1418pub fn ui_amount_to_amount(
1419    token_program_id: &Pubkey,
1420    mint_pubkey: &Pubkey,
1421    ui_amount: &str,
1422) -> Result<Instruction, ProgramError> {
1423    check_program_account(token_program_id)?;
1424
1425    Ok(Instruction {
1426        program_id: *token_program_id,
1427        accounts: vec![AccountMeta::new_readonly(*mint_pubkey, false)],
1428        data: TokenInstruction::UiAmountToAmount { ui_amount }.pack(),
1429    })
1430}
1431
1432/// Utility function that checks index is between `MIN_SIGNERS` and
1433/// `MAX_SIGNERS`
1434pub fn is_valid_signer_index(index: usize) -> bool {
1435    (MIN_SIGNERS..=MAX_SIGNERS).contains(&index)
1436}
1437
1438#[cfg(test)]
1439mod test {
1440    use {super::*, proptest::prelude::*};
1441
1442    #[test]
1443    fn test_instruction_packing() {
1444        let check = TokenInstruction::InitializeMint {
1445            decimals: 2,
1446            mint_authority: Pubkey::new_from_array([1u8; 32]),
1447            freeze_authority: COption::None,
1448        };
1449        let packed = check.pack();
1450        let mut expect = Vec::from([0u8, 2]);
1451        expect.extend_from_slice(&[1u8; 32]);
1452        expect.extend_from_slice(&[0]);
1453        assert_eq!(packed, expect);
1454        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1455        assert_eq!(unpacked, check);
1456
1457        let check = TokenInstruction::InitializeMint {
1458            decimals: 2,
1459            mint_authority: Pubkey::new_from_array([2u8; 32]),
1460            freeze_authority: COption::Some(Pubkey::new_from_array([3u8; 32])),
1461        };
1462        let packed = check.pack();
1463        let mut expect = vec![0u8, 2];
1464        expect.extend_from_slice(&[2u8; 32]);
1465        expect.extend_from_slice(&[1]);
1466        expect.extend_from_slice(&[3u8; 32]);
1467        assert_eq!(packed, expect);
1468        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1469        assert_eq!(unpacked, check);
1470
1471        let check = TokenInstruction::InitializeAccount;
1472        let packed = check.pack();
1473        let expect = Vec::from([1u8]);
1474        assert_eq!(packed, expect);
1475        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1476        assert_eq!(unpacked, check);
1477
1478        let check = TokenInstruction::InitializeMultisig { m: 1 };
1479        let packed = check.pack();
1480        let expect = Vec::from([2u8, 1]);
1481        assert_eq!(packed, expect);
1482        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1483        assert_eq!(unpacked, check);
1484
1485        let check = TokenInstruction::Transfer { amount: 1 };
1486        let packed = check.pack();
1487        let expect = Vec::from([3u8, 1, 0, 0, 0, 0, 0, 0, 0]);
1488        assert_eq!(packed, expect);
1489        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1490        assert_eq!(unpacked, check);
1491
1492        let check = TokenInstruction::Approve { amount: 1 };
1493        let packed = check.pack();
1494        let expect = Vec::from([4u8, 1, 0, 0, 0, 0, 0, 0, 0]);
1495        assert_eq!(packed, expect);
1496        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1497        assert_eq!(unpacked, check);
1498
1499        let check = TokenInstruction::Revoke;
1500        let packed = check.pack();
1501        let expect = Vec::from([5u8]);
1502        assert_eq!(packed, expect);
1503        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1504        assert_eq!(unpacked, check);
1505
1506        let check = TokenInstruction::SetAuthority {
1507            authority_type: AuthorityType::FreezeAccount,
1508            new_authority: COption::Some(Pubkey::new_from_array([4u8; 32])),
1509        };
1510        let packed = check.pack();
1511        let mut expect = Vec::from([6u8, 1]);
1512        expect.extend_from_slice(&[1]);
1513        expect.extend_from_slice(&[4u8; 32]);
1514        assert_eq!(packed, expect);
1515        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1516        assert_eq!(unpacked, check);
1517
1518        let check = TokenInstruction::MintTo { amount: 1 };
1519        let packed = check.pack();
1520        let expect = Vec::from([7u8, 1, 0, 0, 0, 0, 0, 0, 0]);
1521        assert_eq!(packed, expect);
1522        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1523        assert_eq!(unpacked, check);
1524
1525        let check = TokenInstruction::Burn { amount: 1 };
1526        let packed = check.pack();
1527        let expect = Vec::from([8u8, 1, 0, 0, 0, 0, 0, 0, 0]);
1528        assert_eq!(packed, expect);
1529        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1530        assert_eq!(unpacked, check);
1531
1532        let check = TokenInstruction::CloseAccount;
1533        let packed = check.pack();
1534        let expect = Vec::from([9u8]);
1535        assert_eq!(packed, expect);
1536        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1537        assert_eq!(unpacked, check);
1538
1539        let check = TokenInstruction::FreezeAccount;
1540        let packed = check.pack();
1541        let expect = Vec::from([10u8]);
1542        assert_eq!(packed, expect);
1543        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1544        assert_eq!(unpacked, check);
1545
1546        let check = TokenInstruction::ThawAccount;
1547        let packed = check.pack();
1548        let expect = Vec::from([11u8]);
1549        assert_eq!(packed, expect);
1550        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1551        assert_eq!(unpacked, check);
1552
1553        let check = TokenInstruction::TransferChecked {
1554            amount: 1,
1555            decimals: 2,
1556        };
1557        let packed = check.pack();
1558        let expect = Vec::from([12u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
1559        assert_eq!(packed, expect);
1560        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1561        assert_eq!(unpacked, check);
1562
1563        let check = TokenInstruction::ApproveChecked {
1564            amount: 1,
1565            decimals: 2,
1566        };
1567        let packed = check.pack();
1568        let expect = Vec::from([13u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
1569        assert_eq!(packed, expect);
1570        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1571        assert_eq!(unpacked, check);
1572
1573        let check = TokenInstruction::MintToChecked {
1574            amount: 1,
1575            decimals: 2,
1576        };
1577        let packed = check.pack();
1578        let expect = Vec::from([14u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
1579        assert_eq!(packed, expect);
1580        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1581        assert_eq!(unpacked, check);
1582
1583        let check = TokenInstruction::BurnChecked {
1584            amount: 1,
1585            decimals: 2,
1586        };
1587        let packed = check.pack();
1588        let expect = Vec::from([15u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
1589        assert_eq!(packed, expect);
1590        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1591        assert_eq!(unpacked, check);
1592
1593        let check = TokenInstruction::InitializeAccount2 {
1594            owner: Pubkey::new_from_array([2u8; 32]),
1595        };
1596        let packed = check.pack();
1597        let mut expect = vec![16u8];
1598        expect.extend_from_slice(&[2u8; 32]);
1599        assert_eq!(packed, expect);
1600        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1601        assert_eq!(unpacked, check);
1602
1603        let check = TokenInstruction::SyncNative;
1604        let packed = check.pack();
1605        let expect = vec![17u8];
1606        assert_eq!(packed, expect);
1607        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1608        assert_eq!(unpacked, check);
1609
1610        let check = TokenInstruction::InitializeAccount3 {
1611            owner: Pubkey::new_from_array([2u8; 32]),
1612        };
1613        let packed = check.pack();
1614        let mut expect = vec![18u8];
1615        expect.extend_from_slice(&[2u8; 32]);
1616        assert_eq!(packed, expect);
1617        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1618        assert_eq!(unpacked, check);
1619
1620        let check = TokenInstruction::InitializeMultisig2 { m: 1 };
1621        let packed = check.pack();
1622        let expect = Vec::from([19u8, 1]);
1623        assert_eq!(packed, expect);
1624        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1625        assert_eq!(unpacked, check);
1626
1627        let check = TokenInstruction::InitializeMint2 {
1628            decimals: 2,
1629            mint_authority: Pubkey::new_from_array([1u8; 32]),
1630            freeze_authority: COption::None,
1631        };
1632        let packed = check.pack();
1633        let mut expect = Vec::from([20u8, 2]);
1634        expect.extend_from_slice(&[1u8; 32]);
1635        expect.extend_from_slice(&[0]);
1636        assert_eq!(packed, expect);
1637        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1638        assert_eq!(unpacked, check);
1639
1640        let check = TokenInstruction::InitializeMint2 {
1641            decimals: 2,
1642            mint_authority: Pubkey::new_from_array([2u8; 32]),
1643            freeze_authority: COption::Some(Pubkey::new_from_array([3u8; 32])),
1644        };
1645        let packed = check.pack();
1646        let mut expect = vec![20u8, 2];
1647        expect.extend_from_slice(&[2u8; 32]);
1648        expect.extend_from_slice(&[1]);
1649        expect.extend_from_slice(&[3u8; 32]);
1650        assert_eq!(packed, expect);
1651        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1652        assert_eq!(unpacked, check);
1653
1654        let check = TokenInstruction::GetAccountDataSize;
1655        let packed = check.pack();
1656        let expect = vec![21u8];
1657        assert_eq!(packed, expect);
1658        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1659        assert_eq!(unpacked, check);
1660
1661        let check = TokenInstruction::InitializeImmutableOwner;
1662        let packed = check.pack();
1663        let expect = vec![22u8];
1664        assert_eq!(packed, expect);
1665        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1666        assert_eq!(unpacked, check);
1667
1668        let check = TokenInstruction::AmountToUiAmount { amount: 42 };
1669        let packed = check.pack();
1670        let expect = vec![23u8, 42, 0, 0, 0, 0, 0, 0, 0];
1671        assert_eq!(packed, expect);
1672        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1673        assert_eq!(unpacked, check);
1674
1675        let check = TokenInstruction::UiAmountToAmount { ui_amount: "0.42" };
1676        let packed = check.pack();
1677        let expect = vec![24u8, 48, 46, 52, 50];
1678        assert_eq!(packed, expect);
1679        let unpacked = TokenInstruction::unpack(&expect).unwrap();
1680        assert_eq!(unpacked, check);
1681    }
1682
1683    #[test]
1684    fn test_instruction_unpack_panic() {
1685        for i in 0..255u8 {
1686            for j in 1..10 {
1687                let mut data = vec![0; j];
1688                data[0] = i;
1689                let _no_panic = TokenInstruction::unpack(&data);
1690            }
1691        }
1692    }
1693
1694    proptest! {
1695        #![proptest_config(ProptestConfig::with_cases(1024))]
1696        #[test]
1697        fn test_instruction_unpack_proptest(
1698            data in prop::collection::vec(any::<u8>(), 0..255)
1699        ) {
1700            let _no_panic = TokenInstruction::unpack(&data);
1701        }
1702    }
1703}