gorba_token/
instruction.rs

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