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