apl_token/
processor.rs

1//! Program state processor
2
3use {
4    crate::{
5        amount_to_ui_amount_string_trimmed,
6        error::TokenError,
7        instruction::{is_valid_signer_index, AuthorityType, TokenInstruction, MAX_SIGNERS},
8        state::{Account, AccountState, Mint, Multisig},
9        try_ui_amount_into_amount,
10    },
11    arch_program::{
12        account::{next_account_info, AccountInfo},
13        entrypoint::ProgramResult,
14        input_to_sign::InputToSign,
15        msg,
16        program::{set_input_to_sign, set_return_data},
17        program_error::ProgramError,
18        program_memory::sol_memcmp,
19        program_option::COption,
20        program_pack::{IsInitialized, Pack},
21        pubkey::{Pubkey, PUBKEY_BYTES},
22        system_program::SYSTEM_PROGRAM_ID,
23    },
24};
25
26/// Program state handler.
27pub struct Processor {}
28impl Processor {
29    fn _process_initialize_mint(
30        accounts: &[AccountInfo],
31        decimals: u8,
32        mint_authority: Pubkey,
33        freeze_authority: COption<Pubkey>,
34    ) -> ProgramResult {
35        let account_info_iter = &mut accounts.iter();
36        let mint_info = next_account_info(account_info_iter)?;
37
38        let mut mint = Mint::unpack_unchecked(&mint_info.data.borrow())?;
39        if mint.is_initialized {
40            return Err(TokenError::AlreadyInUse.into());
41        }
42
43        mint.mint_authority = COption::Some(mint_authority);
44        mint.decimals = decimals;
45        mint.is_initialized = true;
46        mint.freeze_authority = freeze_authority;
47
48        Mint::pack(mint, &mut mint_info.data.borrow_mut())?;
49
50        Ok(())
51    }
52
53    /// Processes an [`InitializeMint`](enum.TokenInstruction.html) instruction.
54    pub fn process_initialize_mint(
55        accounts: &[AccountInfo],
56        decimals: u8,
57        mint_authority: Pubkey,
58        freeze_authority: COption<Pubkey>,
59    ) -> ProgramResult {
60        Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority)
61    }
62
63    /// Processes an [`InitializeMint2`](enum.TokenInstruction.html)
64    /// instruction.
65    pub fn process_initialize_mint2(
66        accounts: &[AccountInfo],
67        decimals: u8,
68        mint_authority: Pubkey,
69        freeze_authority: COption<Pubkey>,
70    ) -> ProgramResult {
71        Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority)
72    }
73
74    fn _process_initialize_account(
75        program_id: &Pubkey,
76        accounts: &[AccountInfo],
77        owner: Option<&Pubkey>,
78    ) -> ProgramResult {
79        let account_info_iter = &mut accounts.iter();
80        let new_account_info = next_account_info(account_info_iter)?;
81        let mint_info = next_account_info(account_info_iter)?;
82        let owner = if let Some(owner) = owner {
83            owner
84        } else {
85            next_account_info(account_info_iter)?.key
86        };
87
88        let mut account = Account::unpack_unchecked(&new_account_info.data.borrow())?;
89        if account.is_initialized() {
90            return Err(TokenError::AlreadyInUse.into());
91        }
92
93        Self::check_account_owner(program_id, mint_info)?;
94        let _ = Mint::unpack(&mint_info.data.borrow_mut())
95            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
96
97        account.mint = *mint_info.key;
98        account.owner = *owner;
99        account.close_authority = COption::None;
100        account.delegate = COption::None;
101        account.delegated_amount = 0;
102        account.state = AccountState::Initialized;
103
104        Account::pack(account, &mut new_account_info.data.borrow_mut())?;
105
106        Ok(())
107    }
108
109    /// Processes an [`InitializeAccount`](enum.TokenInstruction.html)
110    /// instruction.
111    pub fn process_initialize_account(
112        program_id: &Pubkey,
113        accounts: &[AccountInfo],
114    ) -> ProgramResult {
115        Self::_process_initialize_account(program_id, accounts, None)
116    }
117
118    /// Processes an [`InitializeAccount2`](enum.TokenInstruction.html)
119    /// instruction.
120    pub fn process_initialize_account2(
121        program_id: &Pubkey,
122        accounts: &[AccountInfo],
123        owner: Pubkey,
124    ) -> ProgramResult {
125        Self::_process_initialize_account(program_id, accounts, Some(&owner))
126    }
127
128    /// Processes an [`InitializeAccount3`](enum.TokenInstruction.html)
129    /// instruction.
130    pub fn process_initialize_account3(
131        program_id: &Pubkey,
132        accounts: &[AccountInfo],
133        owner: Pubkey,
134    ) -> ProgramResult {
135        Self::_process_initialize_account(program_id, accounts, Some(&owner))
136    }
137
138    fn _process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult {
139        let account_info_iter = &mut accounts.iter();
140        let multisig_info = next_account_info(account_info_iter)?;
141
142        let mut multisig = Multisig::unpack_unchecked(&multisig_info.data.borrow())?;
143        if multisig.is_initialized {
144            return Err(TokenError::AlreadyInUse.into());
145        }
146
147        let signer_infos = account_info_iter.as_slice();
148        multisig.m = m;
149        multisig.n = signer_infos.len() as u8;
150        if !is_valid_signer_index(multisig.n as usize) {
151            return Err(TokenError::InvalidNumberOfProvidedSigners.into());
152        }
153        if !is_valid_signer_index(multisig.m as usize) {
154            return Err(TokenError::InvalidNumberOfRequiredSigners.into());
155        }
156        for (i, signer_info) in signer_infos.iter().enumerate() {
157            multisig.signers[i] = *signer_info.key;
158        }
159        multisig.is_initialized = true;
160
161        Multisig::pack(multisig, &mut multisig_info.data.borrow_mut())?;
162
163        Ok(())
164    }
165
166    /// Processes a [`InitializeMultisig`](enum.TokenInstruction.html)
167    /// instruction.
168    pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult {
169        Self::_process_initialize_multisig(accounts, m)
170    }
171
172    /// Processes a [`Transfer`](enum.TokenInstruction.html) instruction.
173    pub fn process_transfer(
174        program_id: &Pubkey,
175        accounts: &[AccountInfo],
176        amount: u64,
177        expected_decimals: Option<u8>,
178    ) -> ProgramResult {
179        let account_info_iter = &mut accounts.iter();
180
181        let source_account_info = next_account_info(account_info_iter)?;
182
183        let expected_mint_info = if let Some(expected_decimals) = expected_decimals {
184            Some((next_account_info(account_info_iter)?, expected_decimals))
185        } else {
186            None
187        };
188
189        let destination_account_info = next_account_info(account_info_iter)?;
190        let authority_info = next_account_info(account_info_iter)?;
191
192        let mut source_account = Account::unpack(&source_account_info.data.borrow())?;
193        let mut destination_account = Account::unpack(&destination_account_info.data.borrow())?;
194
195        if source_account.is_frozen() || destination_account.is_frozen() {
196            return Err(TokenError::AccountFrozen.into());
197        }
198        if source_account.amount < amount {
199            return Err(TokenError::InsufficientFunds.into());
200        }
201        if !Self::cmp_pubkeys(&source_account.mint, &destination_account.mint) {
202            return Err(TokenError::MintMismatch.into());
203        }
204
205        if let Some((mint_info, expected_decimals)) = expected_mint_info {
206            if !Self::cmp_pubkeys(mint_info.key, &source_account.mint) {
207                return Err(TokenError::MintMismatch.into());
208            }
209
210            let mint = Mint::unpack(&mint_info.data.borrow_mut())?;
211            if expected_decimals != mint.decimals {
212                return Err(TokenError::MintDecimalsMismatch.into());
213            }
214        }
215
216        let self_transfer =
217            Self::cmp_pubkeys(source_account_info.key, destination_account_info.key);
218
219        match source_account.delegate {
220            COption::Some(ref delegate) if Self::cmp_pubkeys(authority_info.key, delegate) => {
221                Self::validate_owner(
222                    program_id,
223                    delegate,
224                    authority_info,
225                    account_info_iter.as_slice(),
226                )?;
227                if source_account.delegated_amount < amount {
228                    return Err(TokenError::InsufficientFunds.into());
229                }
230                if !self_transfer {
231                    source_account.delegated_amount = source_account
232                        .delegated_amount
233                        .checked_sub(amount)
234                        .ok_or(TokenError::Overflow)?;
235                    if source_account.delegated_amount == 0 {
236                        source_account.delegate = COption::None;
237                    }
238                }
239            }
240            _ => Self::validate_owner(
241                program_id,
242                &source_account.owner,
243                authority_info,
244                account_info_iter.as_slice(),
245            )?,
246        };
247
248        if self_transfer || amount == 0 {
249            Self::check_account_owner(program_id, source_account_info)?;
250            Self::check_account_owner(program_id, destination_account_info)?;
251        }
252
253        // This check MUST occur just before the amounts are manipulated
254        // to ensure self-transfers are fully validated
255        if self_transfer {
256            return Ok(());
257        }
258
259        source_account.amount = source_account
260            .amount
261            .checked_sub(amount)
262            .ok_or(TokenError::Overflow)?;
263        destination_account.amount = destination_account
264            .amount
265            .checked_add(amount)
266            .ok_or(TokenError::Overflow)?;
267
268        Account::pack(source_account, &mut source_account_info.data.borrow_mut())?;
269        Account::pack(
270            destination_account,
271            &mut destination_account_info.data.borrow_mut(),
272        )?;
273
274        Ok(())
275    }
276
277    /// Processes an [`Approve`](enum.TokenInstruction.html) instruction.
278    pub fn process_approve(
279        program_id: &Pubkey,
280        accounts: &[AccountInfo],
281        amount: u64,
282        expected_decimals: Option<u8>,
283    ) -> ProgramResult {
284        let account_info_iter = &mut accounts.iter();
285
286        let source_account_info = next_account_info(account_info_iter)?;
287
288        let expected_mint_info = if let Some(expected_decimals) = expected_decimals {
289            Some((next_account_info(account_info_iter)?, expected_decimals))
290        } else {
291            None
292        };
293        let delegate_info = next_account_info(account_info_iter)?;
294        let owner_info = next_account_info(account_info_iter)?;
295
296        let mut source_account = Account::unpack(&source_account_info.data.borrow())?;
297
298        if source_account.is_frozen() {
299            return Err(TokenError::AccountFrozen.into());
300        }
301
302        if let Some((mint_info, expected_decimals)) = expected_mint_info {
303            if !Self::cmp_pubkeys(mint_info.key, &source_account.mint) {
304                return Err(TokenError::MintMismatch.into());
305            }
306
307            let mint = Mint::unpack(&mint_info.data.borrow_mut())?;
308            if expected_decimals != mint.decimals {
309                return Err(TokenError::MintDecimalsMismatch.into());
310            }
311        }
312
313        Self::validate_owner(
314            program_id,
315            &source_account.owner,
316            owner_info,
317            account_info_iter.as_slice(),
318        )?;
319
320        source_account.delegate = COption::Some(*delegate_info.key);
321        source_account.delegated_amount = amount;
322
323        Account::pack(source_account, &mut source_account_info.data.borrow_mut())?;
324
325        Ok(())
326    }
327
328    /// Processes an [`Revoke`](enum.TokenInstruction.html) instruction.
329    pub fn process_revoke(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
330        let account_info_iter = &mut accounts.iter();
331        let source_account_info = next_account_info(account_info_iter)?;
332
333        let mut source_account = Account::unpack(&source_account_info.data.borrow())?;
334
335        let owner_info = next_account_info(account_info_iter)?;
336
337        if source_account.is_frozen() {
338            return Err(TokenError::AccountFrozen.into());
339        }
340
341        Self::validate_owner(
342            program_id,
343            &source_account.owner,
344            owner_info,
345            account_info_iter.as_slice(),
346        )?;
347
348        source_account.delegate = COption::None;
349        source_account.delegated_amount = 0;
350
351        Account::pack(source_account, &mut source_account_info.data.borrow_mut())?;
352
353        Ok(())
354    }
355
356    /// Processes a [`SetAuthority`](enum.TokenInstruction.html) instruction.
357    pub fn process_set_authority(
358        program_id: &Pubkey,
359        accounts: &[AccountInfo],
360        authority_type: AuthorityType,
361        new_authority: COption<Pubkey>,
362    ) -> ProgramResult {
363        let account_info_iter = &mut accounts.iter();
364        let account_info = next_account_info(account_info_iter)?;
365        let authority_info = next_account_info(account_info_iter)?;
366
367        if account_info.data_len() == Account::get_packed_len() {
368            let mut account = Account::unpack(&account_info.data.borrow())?;
369
370            if account.is_frozen() {
371                return Err(TokenError::AccountFrozen.into());
372            }
373
374            match authority_type {
375                AuthorityType::AccountOwner => {
376                    Self::validate_owner(
377                        program_id,
378                        &account.owner,
379                        authority_info,
380                        account_info_iter.as_slice(),
381                    )?;
382
383                    if let COption::Some(authority) = new_authority {
384                        account.owner = authority;
385                    } else {
386                        return Err(TokenError::InvalidInstruction.into());
387                    }
388
389                    account.delegate = COption::None;
390                    account.delegated_amount = 0;
391                }
392                AuthorityType::CloseAccount => {
393                    let authority = account.close_authority.unwrap_or(account.owner);
394                    Self::validate_owner(
395                        program_id,
396                        &authority,
397                        authority_info,
398                        account_info_iter.as_slice(),
399                    )?;
400                    account.close_authority = new_authority;
401                }
402                _ => {
403                    return Err(TokenError::AuthorityTypeNotSupported.into());
404                }
405            }
406            Account::pack(account, &mut account_info.data.borrow_mut())?;
407        } else if account_info.data_len() == Mint::get_packed_len() {
408            let mut mint = Mint::unpack(&account_info.data.borrow())?;
409            match authority_type {
410                AuthorityType::MintTokens => {
411                    // Once a mint's supply is fixed, it cannot be undone by setting a new
412                    // mint_authority
413                    let mint_authority = mint
414                        .mint_authority
415                        .ok_or(Into::<ProgramError>::into(TokenError::FixedSupply))?;
416                    Self::validate_owner(
417                        program_id,
418                        &mint_authority,
419                        authority_info,
420                        account_info_iter.as_slice(),
421                    )?;
422                    mint.mint_authority = new_authority;
423                }
424                AuthorityType::FreezeAccount => {
425                    // Once a mint's freeze authority is disabled, it cannot be re-enabled by
426                    // setting a new freeze_authority
427                    let freeze_authority = mint
428                        .freeze_authority
429                        .ok_or(Into::<ProgramError>::into(TokenError::MintCannotFreeze))?;
430                    Self::validate_owner(
431                        program_id,
432                        &freeze_authority,
433                        authority_info,
434                        account_info_iter.as_slice(),
435                    )?;
436                    mint.freeze_authority = new_authority;
437                }
438                _ => {
439                    return Err(TokenError::AuthorityTypeNotSupported.into());
440                }
441            }
442            Mint::pack(mint, &mut account_info.data.borrow_mut())?;
443        } else {
444            return Err(ProgramError::InvalidArgument);
445        }
446
447        Ok(())
448    }
449
450    /// Processes a [`MintTo`](enum.TokenInstruction.html) instruction.
451    pub fn process_mint_to(
452        program_id: &Pubkey,
453        accounts: &[AccountInfo],
454        amount: u64,
455        expected_decimals: Option<u8>,
456    ) -> ProgramResult {
457        let account_info_iter = &mut accounts.iter();
458        let mint_info = next_account_info(account_info_iter)?;
459        let destination_account_info = next_account_info(account_info_iter)?;
460        let owner_info = next_account_info(account_info_iter)?;
461
462        let mut destination_account = Account::unpack(&destination_account_info.data.borrow())?;
463        if destination_account.is_frozen() {
464            return Err(TokenError::AccountFrozen.into());
465        }
466
467        if !Self::cmp_pubkeys(mint_info.key, &destination_account.mint) {
468            return Err(TokenError::MintMismatch.into());
469        }
470
471        let mut mint = Mint::unpack(&mint_info.data.borrow())?;
472        if let Some(expected_decimals) = expected_decimals {
473            if expected_decimals != mint.decimals {
474                return Err(TokenError::MintDecimalsMismatch.into());
475            }
476        }
477
478        match mint.mint_authority {
479            COption::Some(mint_authority) => Self::validate_owner(
480                program_id,
481                &mint_authority,
482                owner_info,
483                account_info_iter.as_slice(),
484            )?,
485            COption::None => return Err(TokenError::FixedSupply.into()),
486        }
487
488        if amount == 0 {
489            Self::check_account_owner(program_id, mint_info)?;
490            Self::check_account_owner(program_id, destination_account_info)?;
491        }
492
493        destination_account.amount = destination_account
494            .amount
495            .checked_add(amount)
496            .ok_or(TokenError::Overflow)?;
497
498        mint.supply = mint
499            .supply
500            .checked_add(amount)
501            .ok_or(TokenError::Overflow)?;
502
503        Account::pack(
504            destination_account,
505            &mut destination_account_info.data.borrow_mut(),
506        )?;
507        Mint::pack(mint, &mut mint_info.data.borrow_mut())?;
508
509        Ok(())
510    }
511
512    /// Processes a [`Burn`](enum.TokenInstruction.html) instruction.
513    pub fn process_burn(
514        program_id: &Pubkey,
515        accounts: &[AccountInfo],
516        amount: u64,
517        expected_decimals: Option<u8>,
518    ) -> ProgramResult {
519        let account_info_iter = &mut accounts.iter();
520
521        let source_account_info = next_account_info(account_info_iter)?;
522        let mint_info = next_account_info(account_info_iter)?;
523        let authority_info = next_account_info(account_info_iter)?;
524
525        let mut source_account = Account::unpack(&source_account_info.data.borrow())?;
526        let mut mint = Mint::unpack(&mint_info.data.borrow())?;
527
528        if source_account.is_frozen() {
529            return Err(TokenError::AccountFrozen.into());
530        }
531        if source_account.amount < amount {
532            return Err(TokenError::InsufficientFunds.into());
533        }
534        if !Self::cmp_pubkeys(mint_info.key, &source_account.mint) {
535            return Err(TokenError::MintMismatch.into());
536        }
537
538        if let Some(expected_decimals) = expected_decimals {
539            if expected_decimals != mint.decimals {
540                return Err(TokenError::MintDecimalsMismatch.into());
541            }
542        }
543
544        if source_account.owner != SYSTEM_PROGRAM_ID {
545            match source_account.delegate {
546                COption::Some(ref delegate) if Self::cmp_pubkeys(authority_info.key, delegate) => {
547                    Self::validate_owner(
548                        program_id,
549                        delegate,
550                        authority_info,
551                        account_info_iter.as_slice(),
552                    )?;
553
554                    if source_account.delegated_amount < amount {
555                        return Err(TokenError::InsufficientFunds.into());
556                    }
557                    source_account.delegated_amount = source_account
558                        .delegated_amount
559                        .checked_sub(amount)
560                        .ok_or(TokenError::Overflow)?;
561                    if source_account.delegated_amount == 0 {
562                        source_account.delegate = COption::None;
563                    }
564                }
565                _ => Self::validate_owner(
566                    program_id,
567                    &source_account.owner,
568                    authority_info,
569                    account_info_iter.as_slice(),
570                )?,
571            }
572        }
573
574        if amount == 0 {
575            Self::check_account_owner(program_id, source_account_info)?;
576            Self::check_account_owner(program_id, mint_info)?;
577        }
578
579        source_account.amount = source_account
580            .amount
581            .checked_sub(amount)
582            .ok_or(TokenError::Overflow)?;
583        mint.supply = mint
584            .supply
585            .checked_sub(amount)
586            .ok_or(TokenError::Overflow)?;
587
588        Account::pack(source_account, &mut source_account_info.data.borrow_mut())?;
589        Mint::pack(mint, &mut mint_info.data.borrow_mut())?;
590
591        Ok(())
592    }
593
594    /// Processes a [`CloseAccount`](enum.TokenInstruction.html) instruction.
595    pub fn process_close_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
596        let account_info_iter = &mut accounts.iter();
597        let source_account_info = next_account_info(account_info_iter)?;
598        let destination_account_info = next_account_info(account_info_iter)?;
599        let authority_info = next_account_info(account_info_iter)?;
600
601        if Self::cmp_pubkeys(source_account_info.key, destination_account_info.key) {
602            return Err(ProgramError::InvalidAccountData);
603        }
604
605        let source_account = Account::unpack(&source_account_info.data.borrow())?;
606        if source_account.amount != 0 {
607            return Err(TokenError::NonNativeHasBalance.into());
608        }
609
610        let authority = source_account
611            .close_authority
612            .unwrap_or(source_account.owner);
613
614        if source_account.owner != SYSTEM_PROGRAM_ID {
615            Self::validate_owner(
616                program_id,
617                &authority,
618                authority_info,
619                account_info_iter.as_slice(),
620            )?;
621        }
622
623        let destination_starting_lamports = destination_account_info.lamports();
624        **destination_account_info.lamports.borrow_mut() = destination_starting_lamports
625            .checked_add(source_account_info.lamports())
626            .ok_or(TokenError::Overflow)?;
627
628        **source_account_info.lamports.borrow_mut() = 0;
629
630        delete_account(source_account_info)?;
631
632        Ok(())
633    }
634
635    /// Processes a [`FreezeAccount`](enum.TokenInstruction.html) or a
636    /// [`ThawAccount`](enum.TokenInstruction.html) instruction.
637    pub fn process_toggle_freeze_account(
638        program_id: &Pubkey,
639        accounts: &[AccountInfo],
640        freeze: bool,
641    ) -> ProgramResult {
642        let account_info_iter = &mut accounts.iter();
643        let source_account_info = next_account_info(account_info_iter)?;
644        let mint_info = next_account_info(account_info_iter)?;
645        let authority_info = next_account_info(account_info_iter)?;
646
647        let mut source_account = Account::unpack(&source_account_info.data.borrow())?;
648        if freeze && source_account.is_frozen() || !freeze && !source_account.is_frozen() {
649            return Err(TokenError::InvalidState.into());
650        }
651        if !Self::cmp_pubkeys(mint_info.key, &source_account.mint) {
652            return Err(TokenError::MintMismatch.into());
653        }
654
655        let mint = Mint::unpack(&mint_info.data.borrow_mut())?;
656        match mint.freeze_authority {
657            COption::Some(authority) => Self::validate_owner(
658                program_id,
659                &authority,
660                authority_info,
661                account_info_iter.as_slice(),
662            ),
663            COption::None => Err(TokenError::MintCannotFreeze.into()),
664        }?;
665
666        source_account.state = if freeze {
667            AccountState::Frozen
668        } else {
669            AccountState::Initialized
670        };
671
672        Account::pack(source_account, &mut source_account_info.data.borrow_mut())?;
673
674        Ok(())
675    }
676
677    /// Processes a [`GetAccountDataSize`](enum.TokenInstruction.html)
678    /// instruction
679    pub fn process_get_account_data_size(
680        program_id: &Pubkey,
681        accounts: &[AccountInfo],
682    ) -> ProgramResult {
683        let account_info_iter = &mut accounts.iter();
684        // make sure the mint is valid
685        let mint_info = next_account_info(account_info_iter)?;
686        Self::check_account_owner(program_id, mint_info)?;
687        let _ = Mint::unpack(&mint_info.data.borrow())
688            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
689        set_return_data(&Account::LEN.to_le_bytes());
690        Ok(())
691    }
692
693    /// Processes an [`InitializeImmutableOwner`](enum.TokenInstruction.html)
694    /// instruction
695    pub fn process_initialize_immutable_owner(accounts: &[AccountInfo]) -> ProgramResult {
696        let account_info_iter = &mut accounts.iter();
697        let token_account_info = next_account_info(account_info_iter)?;
698        let account = Account::unpack_unchecked(&token_account_info.data.borrow())?;
699        if account.is_initialized() {
700            return Err(TokenError::AlreadyInUse.into());
701        }
702        msg!("Please upgrade to SPL Token 2022 for immutable owner support");
703        Ok(())
704    }
705
706    /// Processes an [`AmountToUiAmount`](enum.TokenInstruction.html)
707    /// instruction
708    pub fn process_amount_to_ui_amount(
709        program_id: &Pubkey,
710        accounts: &[AccountInfo],
711        amount: u64,
712    ) -> ProgramResult {
713        let account_info_iter = &mut accounts.iter();
714        let mint_info = next_account_info(account_info_iter)?;
715        Self::check_account_owner(program_id, mint_info)?;
716
717        let mint = Mint::unpack(&mint_info.data.borrow_mut())
718            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
719        let ui_amount = amount_to_ui_amount_string_trimmed(amount, mint.decimals);
720
721        set_return_data(&ui_amount.into_bytes());
722        Ok(())
723    }
724
725    /// Processes an [`AmountToUiAmount`](enum.TokenInstruction.html)
726    /// instruction
727    pub fn process_ui_amount_to_amount(
728        program_id: &Pubkey,
729        accounts: &[AccountInfo],
730        ui_amount: &str,
731    ) -> ProgramResult {
732        let account_info_iter = &mut accounts.iter();
733        let mint_info = next_account_info(account_info_iter)?;
734        Self::check_account_owner(program_id, mint_info)?;
735
736        let mint = Mint::unpack(&mint_info.data.borrow_mut())
737            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
738        let amount = try_ui_amount_into_amount(ui_amount.to_string(), mint.decimals)?;
739
740        set_return_data(&amount.to_le_bytes());
741        Ok(())
742    }
743
744    /// Processes an [`Anchor`](enum.TokenInstruction.html) instruction
745    pub fn process_anchor(
746        program_id: &Pubkey,
747        accounts: &[AccountInfo],
748        txid: [u8; 32],
749        input_to_sign: InputToSign,
750    ) -> ProgramResult {
751        let account_info_iter = &mut accounts.iter();
752        let account_info = next_account_info(account_info_iter)?;
753        let owner_info = next_account_info(account_info_iter)?;
754
755        let account = Account::unpack(&account_info.data.borrow())?;
756
757        if account.is_frozen() {
758            return Err(TokenError::AccountFrozen.into());
759        }
760
761        Self::validate_owner(
762            program_id,
763            &account.owner,
764            owner_info,
765            account_info_iter.as_slice(),
766        )?;
767
768        set_input_to_sign(account_info_iter.as_slice(), txid, &[input_to_sign])?;
769
770        Ok(())
771    }
772
773    /// Processes an [`Instruction`](enum.Instruction.html).
774    pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
775        let instruction = TokenInstruction::unpack(input)?;
776
777        match instruction {
778            TokenInstruction::InitializeMint {
779                decimals,
780                mint_authority,
781                freeze_authority,
782            } => {
783                msg!("Instruction: InitializeMint");
784                Self::process_initialize_mint(accounts, decimals, mint_authority, freeze_authority)
785            }
786            TokenInstruction::InitializeMint2 {
787                decimals,
788                mint_authority,
789                freeze_authority,
790            } => {
791                msg!("Instruction: InitializeMint2");
792                Self::process_initialize_mint2(accounts, decimals, mint_authority, freeze_authority)
793            }
794            TokenInstruction::InitializeAccount => {
795                msg!("Instruction: InitializeAccount");
796                Self::process_initialize_account(program_id, accounts)
797            }
798            TokenInstruction::InitializeAccount2 { owner } => {
799                msg!("Instruction: InitializeAccount2");
800                Self::process_initialize_account2(program_id, accounts, owner)
801            }
802            TokenInstruction::InitializeAccount3 { owner } => {
803                msg!("Instruction: InitializeAccount3");
804                Self::process_initialize_account3(program_id, accounts, owner)
805            }
806            TokenInstruction::InitializeMultisig { m } => {
807                msg!("Instruction: InitializeMultisig");
808                Self::process_initialize_multisig(accounts, m)
809            }
810            TokenInstruction::Transfer { amount } => {
811                msg!("Instruction: Transfer");
812                Self::process_transfer(program_id, accounts, amount, None)
813            }
814            TokenInstruction::Approve { amount } => {
815                msg!("Instruction: Approve");
816                Self::process_approve(program_id, accounts, amount, None)
817            }
818            TokenInstruction::Revoke => {
819                msg!("Instruction: Revoke");
820                Self::process_revoke(program_id, accounts)
821            }
822            TokenInstruction::SetAuthority {
823                authority_type,
824                new_authority,
825            } => {
826                msg!("Instruction: SetAuthority");
827                Self::process_set_authority(program_id, accounts, authority_type, new_authority)
828            }
829            TokenInstruction::MintTo { amount } => {
830                msg!("Instruction: MintTo");
831                Self::process_mint_to(program_id, accounts, amount, None)
832            }
833            TokenInstruction::Burn { amount } => {
834                msg!("Instruction: Burn");
835                Self::process_burn(program_id, accounts, amount, None)
836            }
837            TokenInstruction::CloseAccount => {
838                msg!("Instruction: CloseAccount");
839                Self::process_close_account(program_id, accounts)
840            }
841            TokenInstruction::FreezeAccount => {
842                msg!("Instruction: FreezeAccount");
843                Self::process_toggle_freeze_account(program_id, accounts, true)
844            }
845            TokenInstruction::ThawAccount => {
846                msg!("Instruction: ThawAccount");
847                Self::process_toggle_freeze_account(program_id, accounts, false)
848            }
849            TokenInstruction::TransferChecked { amount, decimals } => {
850                msg!("Instruction: TransferChecked");
851                Self::process_transfer(program_id, accounts, amount, Some(decimals))
852            }
853            TokenInstruction::ApproveChecked { amount, decimals } => {
854                msg!("Instruction: ApproveChecked");
855                Self::process_approve(program_id, accounts, amount, Some(decimals))
856            }
857            TokenInstruction::MintToChecked { amount, decimals } => {
858                msg!("Instruction: MintToChecked");
859                Self::process_mint_to(program_id, accounts, amount, Some(decimals))
860            }
861            TokenInstruction::BurnChecked { amount, decimals } => {
862                msg!("Instruction: BurnChecked");
863                Self::process_burn(program_id, accounts, amount, Some(decimals))
864            }
865            TokenInstruction::GetAccountDataSize => {
866                msg!("Instruction: GetAccountDataSize");
867                Self::process_get_account_data_size(program_id, accounts)
868            }
869            TokenInstruction::InitializeImmutableOwner => {
870                msg!("Instruction: InitializeImmutableOwner");
871                Self::process_initialize_immutable_owner(accounts)
872            }
873            TokenInstruction::AmountToUiAmount { amount } => {
874                msg!("Instruction: AmountToUiAmount");
875                Self::process_amount_to_ui_amount(program_id, accounts, amount)
876            }
877            TokenInstruction::UiAmountToAmount { ui_amount } => {
878                msg!("Instruction: UiAmountToAmount");
879                Self::process_ui_amount_to_amount(program_id, accounts, ui_amount)
880            }
881            TokenInstruction::Anchor {
882                txid,
883                input_to_sign,
884            } => {
885                msg!("Instruction: Anchor");
886                Self::process_anchor(program_id, accounts, txid, input_to_sign)
887            }
888        }
889    }
890
891    /// Checks that the account is owned by the expected program
892    pub fn check_account_owner(program_id: &Pubkey, account_info: &AccountInfo) -> ProgramResult {
893        if !Self::cmp_pubkeys(program_id, account_info.owner) {
894            Err(ProgramError::IncorrectProgramId)
895        } else {
896            Ok(())
897        }
898    }
899
900    /// Checks two pubkeys for equality in a computationally cheap way using
901    /// `sol_memcmp`
902    pub fn cmp_pubkeys(a: &Pubkey, b: &Pubkey) -> bool {
903        sol_memcmp(a.as_ref(), b.as_ref(), PUBKEY_BYTES) == 0
904    }
905
906    /// Validates owner(s) are present
907    pub fn validate_owner(
908        program_id: &Pubkey,
909        expected_owner: &Pubkey,
910        owner_account_info: &AccountInfo,
911        signers: &[AccountInfo],
912    ) -> ProgramResult {
913        if !Self::cmp_pubkeys(expected_owner, owner_account_info.key) {
914            return Err(TokenError::OwnerMismatch.into());
915        }
916        if Self::cmp_pubkeys(program_id, owner_account_info.owner)
917            && owner_account_info.data_len() == Multisig::get_packed_len()
918        {
919            let multisig = Multisig::unpack(&owner_account_info.data.borrow())?;
920            let mut num_signers = 0;
921            let mut matched = [false; MAX_SIGNERS];
922            for signer in signers.iter() {
923                for (position, key) in multisig.signers[0..multisig.n as usize].iter().enumerate() {
924                    if Self::cmp_pubkeys(key, signer.key) && !matched[position] {
925                        if !signer.is_signer {
926                            return Err(ProgramError::MissingRequiredSignature);
927                        }
928                        matched[position] = true;
929                        num_signers += 1;
930                    }
931                }
932            }
933            if num_signers < multisig.m {
934                return Err(ProgramError::MissingRequiredSignature);
935            }
936            return Ok(());
937        } else if !owner_account_info.is_signer {
938            return Err(ProgramError::MissingRequiredSignature);
939        }
940        Ok(())
941    }
942}
943
944/// Helper function to mostly delete an account in a test environment.  We could
945/// potentially muck around the bytes assuming that a vec is passed in, but that
946/// would be more trouble than it's worth.
947#[cfg(not(target_os = "solana"))]
948fn delete_account(account_info: &AccountInfo) -> Result<(), ProgramError> {
949    account_info.assign(&Pubkey::from_slice(b"11111111111111111111111111111111"));
950    let mut account_data = account_info.data.borrow_mut();
951    let data_len = account_data.len();
952    arch_program::program_memory::sol_memset(*account_data, 0, data_len);
953    Ok(())
954}
955
956/// Helper function to totally delete an account on-chain
957#[cfg(target_os = "solana")]
958fn delete_account(account_info: &AccountInfo) -> Result<(), ProgramError> {
959    account_info.assign(&Pubkey::system_program());
960    account_info.realloc(0, false)
961}