Skip to main content

apl_token/
instruction.rs

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