safe_token/
instruction.rs

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