apl_token/
instruction.rs

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