solana_extra_wasm/program/spl_token_2022/
processor.rs

1//! Program state processor
2
3use {
4    crate::program::spl_token_2022::{
5        amount_to_ui_amount_string_trimmed, check_program_account, cmp_pubkeys,
6        error::TokenError,
7        extension::{
8            // TODO:
9            // confidential_transfer::{self, ConfidentialTransferAccount},
10            default_account_state::{self, DefaultAccountState},
11            immutable_owner::ImmutableOwner,
12            interest_bearing_mint::{self, InterestBearingConfig},
13            memo_transfer::{self, check_previous_sibling_instruction_is_memo, memo_required},
14            mint_close_authority::MintCloseAuthority,
15            non_transferable::NonTransferable,
16            reallocate,
17            transfer_fee::{self, TransferFeeAmount, TransferFeeConfig},
18            ExtensionType,
19            StateWithExtensions,
20            StateWithExtensionsMut,
21        },
22        instruction::{is_valid_signer_index, AuthorityType, TokenInstruction, MAX_SIGNERS},
23        native_mint,
24        state::{Account, AccountState, Mint, Multisig},
25        try_ui_amount_into_amount,
26    },
27    num_traits::FromPrimitive,
28    solana_sdk::{
29        account_info::{next_account_info, AccountInfo},
30        clock::Clock,
31        decode_error::DecodeError,
32        entrypoint::ProgramResult,
33        msg,
34        program::{invoke, invoke_signed, set_return_data},
35        program_error::{PrintProgramError, ProgramError},
36        program_memory::sol_memset,
37        program_option::COption,
38        program_pack::Pack,
39        pubkey::Pubkey,
40        system_instruction,
41        sysvar::{rent::Rent, Sysvar},
42    },
43    std::convert::{TryFrom, TryInto},
44};
45
46/// Program state handler.
47pub struct Processor {}
48impl Processor {
49    fn _process_initialize_mint(
50        accounts: &[AccountInfo],
51        decimals: u8,
52        mint_authority: Pubkey,
53        freeze_authority: COption<Pubkey>,
54        rent_sysvar_account: bool,
55    ) -> ProgramResult {
56        let account_info_iter = &mut accounts.iter();
57        let mint_info = next_account_info(account_info_iter)?;
58        let mint_data_len = mint_info.data_len();
59        let mut mint_data = mint_info.data.borrow_mut();
60        let rent = if rent_sysvar_account {
61            Rent::from_account_info(next_account_info(account_info_iter)?)?
62        } else {
63            Rent::get()?
64        };
65
66        if !rent.is_exempt(mint_info.lamports(), mint_data_len) {
67            return Err(TokenError::NotRentExempt.into());
68        }
69
70        let mut mint = StateWithExtensionsMut::<Mint>::unpack_uninitialized(&mut mint_data)?;
71        if mint.base.is_initialized {
72            return Err(TokenError::AlreadyInUse.into());
73        }
74
75        let extension_types = mint.get_extension_types()?;
76        if ExtensionType::get_account_len::<Mint>(&extension_types) != mint_data_len {
77            return Err(ProgramError::InvalidAccountData);
78        }
79
80        if let Ok(default_account_state) = mint.get_extension_mut::<DefaultAccountState>() {
81            let default_account_state = AccountState::try_from(default_account_state.state)
82                .or(Err(ProgramError::InvalidAccountData))?;
83            if default_account_state == AccountState::Frozen && freeze_authority.is_none() {
84                return Err(TokenError::MintCannotFreeze.into());
85            }
86        }
87
88        mint.base.mint_authority = COption::Some(mint_authority);
89        mint.base.decimals = decimals;
90        mint.base.is_initialized = true;
91        mint.base.freeze_authority = freeze_authority;
92        mint.pack_base();
93        mint.init_account_type()?;
94
95        Ok(())
96    }
97
98    /// Processes an [InitializeMint](enum.TokenInstruction.html) instruction.
99    pub fn process_initialize_mint(
100        accounts: &[AccountInfo],
101        decimals: u8,
102        mint_authority: Pubkey,
103        freeze_authority: COption<Pubkey>,
104    ) -> ProgramResult {
105        Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority, true)
106    }
107
108    /// Processes an [InitializeMint2](enum.TokenInstruction.html) instruction.
109    pub fn process_initialize_mint2(
110        accounts: &[AccountInfo],
111        decimals: u8,
112        mint_authority: Pubkey,
113        freeze_authority: COption<Pubkey>,
114    ) -> ProgramResult {
115        Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority, false)
116    }
117
118    fn _process_initialize_account(
119        accounts: &[AccountInfo],
120        owner: Option<&Pubkey>,
121        rent_sysvar_account: bool,
122    ) -> ProgramResult {
123        let account_info_iter = &mut accounts.iter();
124        let new_account_info = next_account_info(account_info_iter)?;
125        let mint_info = next_account_info(account_info_iter)?;
126        let owner = if let Some(owner) = owner {
127            owner
128        } else {
129            next_account_info(account_info_iter)?.key
130        };
131        let new_account_info_data_len = new_account_info.data_len();
132        let rent = if rent_sysvar_account {
133            Rent::from_account_info(next_account_info(account_info_iter)?)?
134        } else {
135            Rent::get()?
136        };
137
138        let mut account_data = new_account_info.data.borrow_mut();
139        // unpack_uninitialized checks account.base.is_initialized() under the hood
140        let mut account =
141            StateWithExtensionsMut::<Account>::unpack_uninitialized(&mut account_data)?;
142
143        if !rent.is_exempt(new_account_info.lamports(), new_account_info_data_len) {
144            return Err(TokenError::NotRentExempt.into());
145        }
146
147        // get_required_account_extensions checks mint validity
148        let mint_data = mint_info.data.borrow();
149        let mint = StateWithExtensions::<Mint>::unpack(&mint_data)
150            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
151        let required_extensions =
152            Self::get_required_account_extensions_from_unpacked_mint(mint_info.owner, &mint)?;
153        if ExtensionType::get_account_len::<Account>(&required_extensions)
154            > new_account_info_data_len
155        {
156            return Err(ProgramError::InvalidAccountData);
157        }
158        for extension in required_extensions {
159            account.init_account_extension_from_type(extension)?;
160        }
161
162        let starting_state =
163            if let Ok(default_account_state) = mint.get_extension::<DefaultAccountState>() {
164                AccountState::try_from(default_account_state.state)
165                    .or(Err(ProgramError::InvalidAccountData))?
166            } else {
167                AccountState::Initialized
168            };
169
170        account.base.mint = *mint_info.key;
171        account.base.owner = *owner;
172        account.base.close_authority = COption::None;
173        account.base.delegate = COption::None;
174        account.base.delegated_amount = 0;
175        account.base.state = starting_state;
176        if cmp_pubkeys(mint_info.key, &native_mint::id()) {
177            let rent_exempt_reserve = rent.minimum_balance(new_account_info_data_len);
178            account.base.is_native = COption::Some(rent_exempt_reserve);
179            account.base.amount = new_account_info
180                .lamports()
181                .checked_sub(rent_exempt_reserve)
182                .ok_or(TokenError::Overflow)?;
183        } else {
184            account.base.is_native = COption::None;
185            account.base.amount = 0;
186        };
187
188        account.pack_base();
189        account.init_account_type()?;
190
191        Ok(())
192    }
193
194    /// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction.
195    pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult {
196        Self::_process_initialize_account(accounts, None, true)
197    }
198
199    /// Processes an [InitializeAccount2](enum.TokenInstruction.html) instruction.
200    pub fn process_initialize_account2(accounts: &[AccountInfo], owner: Pubkey) -> ProgramResult {
201        Self::_process_initialize_account(accounts, Some(&owner), true)
202    }
203
204    /// Processes an [InitializeAccount3](enum.TokenInstruction.html) instruction.
205    pub fn process_initialize_account3(accounts: &[AccountInfo], owner: Pubkey) -> ProgramResult {
206        Self::_process_initialize_account(accounts, Some(&owner), false)
207    }
208
209    fn _process_initialize_multisig(
210        accounts: &[AccountInfo],
211        m: u8,
212        rent_sysvar_account: bool,
213    ) -> ProgramResult {
214        let account_info_iter = &mut accounts.iter();
215        let multisig_info = next_account_info(account_info_iter)?;
216        let multisig_info_data_len = multisig_info.data_len();
217        let rent = if rent_sysvar_account {
218            Rent::from_account_info(next_account_info(account_info_iter)?)?
219        } else {
220            Rent::get()?
221        };
222
223        let mut multisig = Multisig::unpack_unchecked(&multisig_info.data.borrow())?;
224        if multisig.is_initialized {
225            return Err(TokenError::AlreadyInUse.into());
226        }
227
228        if !rent.is_exempt(multisig_info.lamports(), multisig_info_data_len) {
229            return Err(TokenError::NotRentExempt.into());
230        }
231
232        let signer_infos = account_info_iter.as_slice();
233        multisig.m = m;
234        multisig.n = signer_infos.len() as u8;
235        if !is_valid_signer_index(multisig.n as usize) {
236            return Err(TokenError::InvalidNumberOfProvidedSigners.into());
237        }
238        if !is_valid_signer_index(multisig.m as usize) {
239            return Err(TokenError::InvalidNumberOfRequiredSigners.into());
240        }
241        for (i, signer_info) in signer_infos.iter().enumerate() {
242            multisig.signers[i] = *signer_info.key;
243        }
244        multisig.is_initialized = true;
245
246        Multisig::pack(multisig, &mut multisig_info.data.borrow_mut())?;
247
248        Ok(())
249    }
250
251    /// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction.
252    pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult {
253        Self::_process_initialize_multisig(accounts, m, true)
254    }
255
256    /// Processes a [InitializeMultisig2](enum.TokenInstruction.html) instruction.
257    pub fn process_initialize_multisig2(accounts: &[AccountInfo], m: u8) -> ProgramResult {
258        Self::_process_initialize_multisig(accounts, m, false)
259    }
260
261    /// Processes a [Transfer](enum.TokenInstruction.html) instruction.
262    pub fn process_transfer(
263        program_id: &Pubkey,
264        accounts: &[AccountInfo],
265        amount: u64,
266        expected_decimals: Option<u8>,
267        expected_fee: Option<u64>,
268    ) -> ProgramResult {
269        let account_info_iter = &mut accounts.iter();
270
271        let source_account_info = next_account_info(account_info_iter)?;
272
273        let expected_mint_info = if let Some(expected_decimals) = expected_decimals {
274            Some((next_account_info(account_info_iter)?, expected_decimals))
275        } else {
276            None
277        };
278
279        let destination_account_info = next_account_info(account_info_iter)?;
280        let authority_info = next_account_info(account_info_iter)?;
281        let authority_info_data_len = authority_info.data_len();
282
283        let mut source_account_data = source_account_info.data.borrow_mut();
284        let mut source_account =
285            StateWithExtensionsMut::<Account>::unpack(&mut source_account_data)?;
286        if source_account.base.is_frozen() {
287            return Err(TokenError::AccountFrozen.into());
288        }
289        if source_account.base.amount < amount {
290            return Err(TokenError::InsufficientFunds.into());
291        }
292        let fee = if let Some((mint_info, expected_decimals)) = expected_mint_info {
293            if !cmp_pubkeys(&source_account.base.mint, mint_info.key) {
294                return Err(TokenError::MintMismatch.into());
295            }
296
297            let mint_data = mint_info.try_borrow_data()?;
298            let mint = StateWithExtensions::<Mint>::unpack(&mint_data)?;
299
300            if mint.get_extension::<NonTransferable>().is_ok() {
301                return Err(TokenError::NonTransferable.into());
302            }
303
304            if expected_decimals != mint.base.decimals {
305                return Err(TokenError::MintDecimalsMismatch.into());
306            }
307
308            if let Ok(transfer_fee_config) = mint.get_extension::<TransferFeeConfig>() {
309                transfer_fee_config
310                    .calculate_epoch_fee(Clock::get()?.epoch, amount)
311                    .ok_or(TokenError::Overflow)?
312            } else {
313                0
314            }
315        } else {
316            // Transfer fee amount extension exists on the account, but no mint
317            // was provided to calculate the fee, abort
318            if source_account
319                .get_extension_mut::<TransferFeeAmount>()
320                .is_ok()
321            {
322                return Err(TokenError::MintRequiredForTransfer.into());
323            } else {
324                0
325            }
326        };
327        if let Some(expected_fee) = expected_fee {
328            if expected_fee != fee {
329                msg!("Calculated fee {}, received {}", fee, expected_fee);
330                return Err(TokenError::FeeMismatch.into());
331            }
332        }
333
334        let self_transfer = cmp_pubkeys(source_account_info.key, destination_account_info.key);
335        match source_account.base.delegate {
336            COption::Some(ref delegate) if cmp_pubkeys(authority_info.key, delegate) => {
337                Self::validate_owner(
338                    program_id,
339                    delegate,
340                    authority_info,
341                    authority_info_data_len,
342                    account_info_iter.as_slice(),
343                )?;
344                if source_account.base.delegated_amount < amount {
345                    return Err(TokenError::InsufficientFunds.into());
346                }
347                if !self_transfer {
348                    source_account.base.delegated_amount = source_account
349                        .base
350                        .delegated_amount
351                        .checked_sub(amount)
352                        .ok_or(TokenError::Overflow)?;
353                    if source_account.base.delegated_amount == 0 {
354                        source_account.base.delegate = COption::None;
355                    }
356                }
357            }
358            _ => Self::validate_owner(
359                program_id,
360                &source_account.base.owner,
361                authority_info,
362                authority_info_data_len,
363                account_info_iter.as_slice(),
364            )?,
365        };
366
367        // Revisit this later to see if it's worth adding a check to reduce
368        // compute costs, ie:
369        // if self_transfer || amount == 0
370        check_program_account(source_account_info.owner)?;
371        check_program_account(destination_account_info.owner)?;
372
373        // This check MUST occur just before the amounts are manipulated
374        // to ensure self-transfers are fully validated
375        if self_transfer {
376            return Ok(());
377        }
378
379        // self-transfer was dealt with earlier, so this *should* be safe
380        let mut destination_account_data = destination_account_info.data.borrow_mut();
381        let mut destination_account =
382            StateWithExtensionsMut::<Account>::unpack(&mut destination_account_data)?;
383
384        if destination_account.base.is_frozen() {
385            return Err(TokenError::AccountFrozen.into());
386        }
387        if !cmp_pubkeys(&source_account.base.mint, &destination_account.base.mint) {
388            return Err(TokenError::MintMismatch.into());
389        }
390
391        if memo_required(&destination_account) {
392            check_previous_sibling_instruction_is_memo()?;
393        }
394
395        source_account.base.amount = source_account
396            .base
397            .amount
398            .checked_sub(amount)
399            .ok_or(TokenError::Overflow)?;
400        let credited_amount = amount.checked_sub(fee).ok_or(TokenError::Overflow)?;
401        destination_account.base.amount = destination_account
402            .base
403            .amount
404            .checked_add(credited_amount)
405            .ok_or(TokenError::Overflow)?;
406        if fee > 0 {
407            if let Ok(extension) = destination_account.get_extension_mut::<TransferFeeAmount>() {
408                let new_withheld_amount = u64::from(extension.withheld_amount)
409                    .checked_add(fee)
410                    .ok_or(TokenError::Overflow)?;
411                extension.withheld_amount = new_withheld_amount.into();
412            } else {
413                // Use the generic error since this should never happen. If there's
414                // a fee, then the mint has a fee configured, which means all accounts
415                // must have the withholding.
416                return Err(TokenError::InvalidState.into());
417            }
418        }
419
420        if source_account.base.is_native() {
421            let source_starting_lamports = source_account_info.lamports();
422            **source_account_info.lamports.borrow_mut() = source_starting_lamports
423                .checked_sub(amount)
424                .ok_or(TokenError::Overflow)?;
425
426            let destination_starting_lamports = destination_account_info.lamports();
427            **destination_account_info.lamports.borrow_mut() = destination_starting_lamports
428                .checked_add(amount)
429                .ok_or(TokenError::Overflow)?;
430        }
431
432        source_account.pack_base();
433        destination_account.pack_base();
434
435        Ok(())
436    }
437
438    /// Processes an [Approve](enum.TokenInstruction.html) instruction.
439    pub fn process_approve(
440        program_id: &Pubkey,
441        accounts: &[AccountInfo],
442        amount: u64,
443        expected_decimals: Option<u8>,
444    ) -> ProgramResult {
445        let account_info_iter = &mut accounts.iter();
446
447        let source_account_info = next_account_info(account_info_iter)?;
448
449        let expected_mint_info = if let Some(expected_decimals) = expected_decimals {
450            Some((next_account_info(account_info_iter)?, expected_decimals))
451        } else {
452            None
453        };
454        let delegate_info = next_account_info(account_info_iter)?;
455        let owner_info = next_account_info(account_info_iter)?;
456        let owner_info_data_len = owner_info.data_len();
457
458        let mut source_account_data = source_account_info.data.borrow_mut();
459        let mut source_account =
460            StateWithExtensionsMut::<Account>::unpack(&mut source_account_data)?;
461
462        if source_account.base.is_frozen() {
463            return Err(TokenError::AccountFrozen.into());
464        }
465
466        if let Some((mint_info, expected_decimals)) = expected_mint_info {
467            if !cmp_pubkeys(&source_account.base.mint, mint_info.key) {
468                return Err(TokenError::MintMismatch.into());
469            }
470
471            let mint_data = mint_info.data.borrow();
472            let mint = StateWithExtensions::<Mint>::unpack(&mint_data)?;
473            if expected_decimals != mint.base.decimals {
474                return Err(TokenError::MintDecimalsMismatch.into());
475            }
476        }
477
478        Self::validate_owner(
479            program_id,
480            &source_account.base.owner,
481            owner_info,
482            owner_info_data_len,
483            account_info_iter.as_slice(),
484        )?;
485
486        source_account.base.delegate = COption::Some(*delegate_info.key);
487        source_account.base.delegated_amount = amount;
488        source_account.pack_base();
489
490        Ok(())
491    }
492
493    /// Processes an [Revoke](enum.TokenInstruction.html) instruction.
494    pub fn process_revoke(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
495        let account_info_iter = &mut accounts.iter();
496        let source_account_info = next_account_info(account_info_iter)?;
497        let authority_info = next_account_info(account_info_iter)?;
498        let authority_info_data_len = authority_info.data_len();
499
500        let mut source_account_data = source_account_info.data.borrow_mut();
501        let mut source_account =
502            StateWithExtensionsMut::<Account>::unpack(&mut source_account_data)?;
503        if source_account.base.is_frozen() {
504            return Err(TokenError::AccountFrozen.into());
505        }
506
507        Self::validate_owner(
508            program_id,
509            match source_account.base.delegate {
510                COption::Some(ref delegate) if cmp_pubkeys(authority_info.key, delegate) => {
511                    delegate
512                }
513                _ => &source_account.base.owner,
514            },
515            authority_info,
516            authority_info_data_len,
517            account_info_iter.as_slice(),
518        )?;
519
520        source_account.base.delegate = COption::None;
521        source_account.base.delegated_amount = 0;
522        source_account.pack_base();
523
524        Ok(())
525    }
526
527    /// Processes a [SetAuthority](enum.TokenInstruction.html) instruction.
528    pub fn process_set_authority(
529        program_id: &Pubkey,
530        accounts: &[AccountInfo],
531        authority_type: AuthorityType,
532        new_authority: COption<Pubkey>,
533    ) -> ProgramResult {
534        let account_info_iter = &mut accounts.iter();
535        let account_info = next_account_info(account_info_iter)?;
536        let authority_info = next_account_info(account_info_iter)?;
537        let authority_info_data_len = authority_info.data_len();
538
539        let mut account_data = account_info.data.borrow_mut();
540        if let Ok(mut account) = StateWithExtensionsMut::<Account>::unpack(&mut account_data) {
541            if account.base.is_frozen() {
542                return Err(TokenError::AccountFrozen.into());
543            }
544
545            match authority_type {
546                AuthorityType::AccountOwner => {
547                    Self::validate_owner(
548                        program_id,
549                        &account.base.owner,
550                        authority_info,
551                        authority_info_data_len,
552                        account_info_iter.as_slice(),
553                    )?;
554
555                    if account.get_extension_mut::<ImmutableOwner>().is_ok() {
556                        return Err(TokenError::ImmutableOwner.into());
557                    }
558
559                    if let COption::Some(authority) = new_authority {
560                        account.base.owner = authority;
561                    } else {
562                        return Err(TokenError::InvalidInstruction.into());
563                    }
564
565                    account.base.delegate = COption::None;
566                    account.base.delegated_amount = 0;
567
568                    if account.base.is_native() {
569                        account.base.close_authority = COption::None;
570                    }
571                }
572                AuthorityType::CloseAccount => {
573                    let authority = account.base.close_authority.unwrap_or(account.base.owner);
574                    Self::validate_owner(
575                        program_id,
576                        &authority,
577                        authority_info,
578                        authority_info_data_len,
579                        account_info_iter.as_slice(),
580                    )?;
581                    account.base.close_authority = new_authority;
582                }
583                _ => {
584                    return Err(TokenError::AuthorityTypeNotSupported.into());
585                }
586            }
587            account.pack_base();
588        } else if let Ok(mut mint) = StateWithExtensionsMut::<Mint>::unpack(&mut account_data) {
589            match authority_type {
590                AuthorityType::MintTokens => {
591                    // Once a mint's supply is fixed, it cannot be undone by setting a new
592                    // mint_authority
593                    let mint_authority = mint
594                        .base
595                        .mint_authority
596                        .ok_or(Into::<ProgramError>::into(TokenError::FixedSupply))?;
597                    Self::validate_owner(
598                        program_id,
599                        &mint_authority,
600                        authority_info,
601                        authority_info_data_len,
602                        account_info_iter.as_slice(),
603                    )?;
604                    mint.base.mint_authority = new_authority;
605                    mint.pack_base();
606                }
607                AuthorityType::FreezeAccount => {
608                    // Once a mint's freeze authority is disabled, it cannot be re-enabled by
609                    // setting a new freeze_authority
610                    let freeze_authority = mint
611                        .base
612                        .freeze_authority
613                        .ok_or(Into::<ProgramError>::into(TokenError::MintCannotFreeze))?;
614                    Self::validate_owner(
615                        program_id,
616                        &freeze_authority,
617                        authority_info,
618                        authority_info_data_len,
619                        account_info_iter.as_slice(),
620                    )?;
621                    mint.base.freeze_authority = new_authority;
622                    mint.pack_base();
623                }
624                AuthorityType::CloseMint => {
625                    let extension = mint.get_extension_mut::<MintCloseAuthority>()?;
626                    let maybe_close_authority: Option<Pubkey> = extension.close_authority.into();
627                    let close_authority =
628                        maybe_close_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
629                    Self::validate_owner(
630                        program_id,
631                        &close_authority,
632                        authority_info,
633                        authority_info_data_len,
634                        account_info_iter.as_slice(),
635                    )?;
636                    extension.close_authority = new_authority.try_into()?;
637                }
638                AuthorityType::TransferFeeConfig => {
639                    let extension = mint.get_extension_mut::<TransferFeeConfig>()?;
640                    let maybe_transfer_fee_config_authority: Option<Pubkey> =
641                        extension.transfer_fee_config_authority.into();
642                    let transfer_fee_config_authority = maybe_transfer_fee_config_authority
643                        .ok_or(TokenError::AuthorityTypeNotSupported)?;
644                    Self::validate_owner(
645                        program_id,
646                        &transfer_fee_config_authority,
647                        authority_info,
648                        authority_info_data_len,
649                        account_info_iter.as_slice(),
650                    )?;
651                    extension.transfer_fee_config_authority = new_authority.try_into()?;
652                }
653                AuthorityType::WithheldWithdraw => {
654                    let extension = mint.get_extension_mut::<TransferFeeConfig>()?;
655                    let maybe_withdraw_withheld_authority: Option<Pubkey> =
656                        extension.withdraw_withheld_authority.into();
657                    let withdraw_withheld_authority = maybe_withdraw_withheld_authority
658                        .ok_or(TokenError::AuthorityTypeNotSupported)?;
659                    Self::validate_owner(
660                        program_id,
661                        &withdraw_withheld_authority,
662                        authority_info,
663                        authority_info_data_len,
664                        account_info_iter.as_slice(),
665                    )?;
666                    extension.withdraw_withheld_authority = new_authority.try_into()?;
667                }
668                AuthorityType::InterestRate => {
669                    let extension = mint.get_extension_mut::<InterestBearingConfig>()?;
670                    let maybe_rate_authority: Option<Pubkey> = extension.rate_authority.into();
671                    let rate_authority =
672                        maybe_rate_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
673                    Self::validate_owner(
674                        program_id,
675                        &rate_authority,
676                        authority_info,
677                        authority_info_data_len,
678                        account_info_iter.as_slice(),
679                    )?;
680                    extension.rate_authority = new_authority.try_into()?;
681                }
682                _ => {
683                    return Err(TokenError::AuthorityTypeNotSupported.into());
684                }
685            }
686        } else {
687            return Err(ProgramError::InvalidAccountData);
688        }
689
690        Ok(())
691    }
692
693    /// Processes a [MintTo](enum.TokenInstruction.html) instruction.
694    pub fn process_mint_to(
695        program_id: &Pubkey,
696        accounts: &[AccountInfo],
697        amount: u64,
698        expected_decimals: Option<u8>,
699    ) -> ProgramResult {
700        let account_info_iter = &mut accounts.iter();
701        let mint_info = next_account_info(account_info_iter)?;
702        let destination_account_info = next_account_info(account_info_iter)?;
703        let owner_info = next_account_info(account_info_iter)?;
704        let owner_info_data_len = owner_info.data_len();
705
706        let mut destination_account_data = destination_account_info.data.borrow_mut();
707        let mut destination_account =
708            StateWithExtensionsMut::<Account>::unpack(&mut destination_account_data)?;
709        if destination_account.base.is_frozen() {
710            return Err(TokenError::AccountFrozen.into());
711        }
712
713        if destination_account.base.is_native() {
714            return Err(TokenError::NativeNotSupported.into());
715        }
716        if !cmp_pubkeys(mint_info.key, &destination_account.base.mint) {
717            return Err(TokenError::MintMismatch.into());
718        }
719
720        let mut mint_data = mint_info.data.borrow_mut();
721        let mut mint = StateWithExtensionsMut::<Mint>::unpack(&mut mint_data)?;
722
723        // If the mint if non-transferable, only allow minting to accounts
724        // with immutable ownership.
725        if mint.get_extension::<NonTransferable>().is_ok()
726            && destination_account
727                .get_extension::<ImmutableOwner>()
728                .is_err()
729        {
730            return Err(TokenError::NonTransferableNeedsImmutableOwnership.into());
731        }
732
733        if let Some(expected_decimals) = expected_decimals {
734            if expected_decimals != mint.base.decimals {
735                return Err(TokenError::MintDecimalsMismatch.into());
736            }
737        }
738
739        match mint.base.mint_authority {
740            COption::Some(mint_authority) => Self::validate_owner(
741                program_id,
742                &mint_authority,
743                owner_info,
744                owner_info_data_len,
745                account_info_iter.as_slice(),
746            )?,
747            COption::None => return Err(TokenError::FixedSupply.into()),
748        }
749
750        // Revisit this later to see if it's worth adding a check to reduce
751        // compute costs, ie:
752        // if amount == 0
753        check_program_account(mint_info.owner)?;
754        check_program_account(destination_account_info.owner)?;
755
756        destination_account.base.amount = destination_account
757            .base
758            .amount
759            .checked_add(amount)
760            .ok_or(TokenError::Overflow)?;
761
762        mint.base.supply = mint
763            .base
764            .supply
765            .checked_add(amount)
766            .ok_or(TokenError::Overflow)?;
767
768        mint.pack_base();
769        destination_account.pack_base();
770
771        Ok(())
772    }
773
774    /// Processes a [Burn](enum.TokenInstruction.html) instruction.
775    pub fn process_burn(
776        program_id: &Pubkey,
777        accounts: &[AccountInfo],
778        amount: u64,
779        expected_decimals: Option<u8>,
780    ) -> ProgramResult {
781        let account_info_iter = &mut accounts.iter();
782
783        let source_account_info = next_account_info(account_info_iter)?;
784        let mint_info = next_account_info(account_info_iter)?;
785        let authority_info = next_account_info(account_info_iter)?;
786        let authority_info_data_len = authority_info.data_len();
787
788        let mut source_account_data = source_account_info.data.borrow_mut();
789        let mut source_account =
790            StateWithExtensionsMut::<Account>::unpack(&mut source_account_data)?;
791        let mut mint_data = mint_info.data.borrow_mut();
792        let mut mint = StateWithExtensionsMut::<Mint>::unpack(&mut mint_data)?;
793
794        if source_account.base.is_frozen() {
795            return Err(TokenError::AccountFrozen.into());
796        }
797        if source_account.base.is_native() {
798            return Err(TokenError::NativeNotSupported.into());
799        }
800        if source_account.base.amount < amount {
801            return Err(TokenError::InsufficientFunds.into());
802        }
803        if mint_info.key != &source_account.base.mint {
804            return Err(TokenError::MintMismatch.into());
805        }
806
807        if let Some(expected_decimals) = expected_decimals {
808            if expected_decimals != mint.base.decimals {
809                return Err(TokenError::MintDecimalsMismatch.into());
810            }
811        }
812
813        if !source_account
814            .base
815            .is_owned_by_system_program_or_incinerator()
816        {
817            match source_account.base.delegate {
818                COption::Some(ref delegate) if cmp_pubkeys(authority_info.key, delegate) => {
819                    Self::validate_owner(
820                        program_id,
821                        delegate,
822                        authority_info,
823                        authority_info_data_len,
824                        account_info_iter.as_slice(),
825                    )?;
826
827                    if source_account.base.delegated_amount < amount {
828                        return Err(TokenError::InsufficientFunds.into());
829                    }
830                    source_account.base.delegated_amount = source_account
831                        .base
832                        .delegated_amount
833                        .checked_sub(amount)
834                        .ok_or(TokenError::Overflow)?;
835                    if source_account.base.delegated_amount == 0 {
836                        source_account.base.delegate = COption::None;
837                    }
838                }
839                _ => Self::validate_owner(
840                    program_id,
841                    &source_account.base.owner,
842                    authority_info,
843                    authority_info_data_len,
844                    account_info_iter.as_slice(),
845                )?,
846            }
847        }
848
849        // Revisit this later to see if it's worth adding a check to reduce
850        // compute costs, ie:
851        // if amount == 0
852        check_program_account(source_account_info.owner)?;
853        check_program_account(mint_info.owner)?;
854
855        source_account.base.amount = source_account
856            .base
857            .amount
858            .checked_sub(amount)
859            .ok_or(TokenError::Overflow)?;
860        mint.base.supply = mint
861            .base
862            .supply
863            .checked_sub(amount)
864            .ok_or(TokenError::Overflow)?;
865
866        source_account.pack_base();
867        mint.pack_base();
868
869        Ok(())
870    }
871
872    /// Processes a [CloseAccount](enum.TokenInstruction.html) instruction.
873    pub fn process_close_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
874        let account_info_iter = &mut accounts.iter();
875        let source_account_info = next_account_info(account_info_iter)?;
876        let destination_account_info = next_account_info(account_info_iter)?;
877        let authority_info = next_account_info(account_info_iter)?;
878        let authority_info_data_len = authority_info.data_len();
879
880        if cmp_pubkeys(source_account_info.key, destination_account_info.key) {
881            return Err(ProgramError::InvalidAccountData);
882        }
883
884        let mut source_account_data = source_account_info.data.borrow_mut();
885        if let Ok(source_account) = StateWithExtensions::<Account>::unpack(&source_account_data) {
886            if !source_account.base.is_native() && source_account.base.amount != 0 {
887                return Err(TokenError::NonNativeHasBalance.into());
888            }
889
890            let authority = source_account
891                .base
892                .close_authority
893                .unwrap_or(source_account.base.owner);
894
895            if !source_account
896                .base
897                .is_owned_by_system_program_or_incinerator()
898            {
899                Self::validate_owner(
900                    program_id,
901                    &authority,
902                    authority_info,
903                    authority_info_data_len,
904                    account_info_iter.as_slice(),
905                )?;
906            } else if !solana_sdk::incinerator::check_id(destination_account_info.key) {
907                return Err(ProgramError::InvalidAccountData);
908            }
909
910            // TODO:
911            // if let Ok(confidential_transfer_state) =
912            //     source_account.get_extension::<ConfidentialTransferAccount>()
913            // {
914            //     confidential_transfer_state.closable()?
915            // }
916
917            if let Ok(transfer_fee_state) = source_account.get_extension::<TransferFeeAmount>() {
918                transfer_fee_state.closable()?
919            }
920        } else if let Ok(mint) = StateWithExtensions::<Mint>::unpack(&source_account_data) {
921            let extension = mint.get_extension::<MintCloseAuthority>()?;
922            let maybe_authority: Option<Pubkey> = extension.close_authority.into();
923            let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
924            Self::validate_owner(
925                program_id,
926                &authority,
927                authority_info,
928                authority_info_data_len,
929                account_info_iter.as_slice(),
930            )?;
931
932            if mint.base.supply != 0 {
933                return Err(TokenError::MintHasSupply.into());
934            }
935        } else {
936            return Err(ProgramError::UninitializedAccount);
937        }
938
939        let destination_starting_lamports = destination_account_info.lamports();
940        **destination_account_info.lamports.borrow_mut() = destination_starting_lamports
941            .checked_add(source_account_info.lamports())
942            .ok_or(TokenError::Overflow)?;
943
944        **source_account_info.lamports.borrow_mut() = 0;
945        let data_len = source_account_data.len();
946        sol_memset(*source_account_data, 0, data_len);
947
948        Ok(())
949    }
950
951    /// Processes a [FreezeAccount](enum.TokenInstruction.html) or a
952    /// [ThawAccount](enum.TokenInstruction.html) instruction.
953    pub fn process_toggle_freeze_account(
954        program_id: &Pubkey,
955        accounts: &[AccountInfo],
956        freeze: bool,
957    ) -> ProgramResult {
958        let account_info_iter = &mut accounts.iter();
959        let source_account_info = next_account_info(account_info_iter)?;
960        let mint_info = next_account_info(account_info_iter)?;
961        let authority_info = next_account_info(account_info_iter)?;
962        let authority_info_data_len = authority_info.data_len();
963
964        let mut source_account_data = source_account_info.data.borrow_mut();
965        let mut source_account =
966            StateWithExtensionsMut::<Account>::unpack(&mut source_account_data)?;
967        if freeze && source_account.base.is_frozen() || !freeze && !source_account.base.is_frozen()
968        {
969            return Err(TokenError::InvalidState.into());
970        }
971        if source_account.base.is_native() {
972            return Err(TokenError::NativeNotSupported.into());
973        }
974        if !cmp_pubkeys(mint_info.key, &source_account.base.mint) {
975            return Err(TokenError::MintMismatch.into());
976        }
977
978        let mint_data = mint_info.data.borrow();
979        let mint = StateWithExtensions::<Mint>::unpack(&mint_data)?;
980        match mint.base.freeze_authority {
981            COption::Some(authority) => Self::validate_owner(
982                program_id,
983                &authority,
984                authority_info,
985                authority_info_data_len,
986                account_info_iter.as_slice(),
987            ),
988            COption::None => Err(TokenError::MintCannotFreeze.into()),
989        }?;
990
991        source_account.base.state = if freeze {
992            AccountState::Frozen
993        } else {
994            AccountState::Initialized
995        };
996
997        source_account.pack_base();
998
999        Ok(())
1000    }
1001
1002    /// Processes a [SyncNative](enum.TokenInstruction.html) instruction
1003    pub fn process_sync_native(accounts: &[AccountInfo]) -> ProgramResult {
1004        let account_info_iter = &mut accounts.iter();
1005        let native_account_info = next_account_info(account_info_iter)?;
1006
1007        check_program_account(native_account_info.owner)?;
1008        let mut native_account_data = native_account_info.data.borrow_mut();
1009        let mut native_account =
1010            StateWithExtensionsMut::<Account>::unpack(&mut native_account_data)?;
1011
1012        if let COption::Some(rent_exempt_reserve) = native_account.base.is_native {
1013            let new_amount = native_account_info
1014                .lamports()
1015                .checked_sub(rent_exempt_reserve)
1016                .ok_or(TokenError::Overflow)?;
1017            if new_amount < native_account.base.amount {
1018                return Err(TokenError::InvalidState.into());
1019            }
1020            native_account.base.amount = new_amount;
1021        } else {
1022            return Err(TokenError::NonNativeNotSupported.into());
1023        }
1024
1025        native_account.pack_base();
1026        Ok(())
1027    }
1028
1029    /// Processes an [InitializeMintCloseAuthority](enum.TokenInstruction.html) instruction
1030    pub fn process_initialize_mint_close_authority(
1031        accounts: &[AccountInfo],
1032        close_authority: COption<Pubkey>,
1033    ) -> ProgramResult {
1034        let account_info_iter = &mut accounts.iter();
1035        let mint_account_info = next_account_info(account_info_iter)?;
1036
1037        let mut mint_data = mint_account_info.data.borrow_mut();
1038        let mut mint = StateWithExtensionsMut::<Mint>::unpack_uninitialized(&mut mint_data)?;
1039        let extension = mint.init_extension::<MintCloseAuthority>(true)?;
1040        extension.close_authority = close_authority.try_into()?;
1041
1042        Ok(())
1043    }
1044
1045    /// Processes a [GetAccountDataSize](enum.TokenInstruction.html) instruction
1046    pub fn process_get_account_data_size(
1047        accounts: &[AccountInfo],
1048        new_extension_types: Vec<ExtensionType>,
1049    ) -> ProgramResult {
1050        let account_info_iter = &mut accounts.iter();
1051        let mint_account_info = next_account_info(account_info_iter)?;
1052
1053        let mut account_extensions = Self::get_required_account_extensions(mint_account_info)?;
1054        // ExtensionType::get_account_len() dedupes types, so just a dumb concatenation is fine
1055        // here
1056        account_extensions.extend_from_slice(&new_extension_types);
1057
1058        let account_len = ExtensionType::get_account_len::<Account>(&account_extensions);
1059        set_return_data(&account_len.to_le_bytes());
1060
1061        Ok(())
1062    }
1063
1064    /// Processes an [InitializeImmutableOwner](enum.TokenInstruction.html) instruction
1065    pub fn process_initialize_immutable_owner(accounts: &[AccountInfo]) -> ProgramResult {
1066        let account_info_iter = &mut accounts.iter();
1067        let token_account_info = next_account_info(account_info_iter)?;
1068        let token_account_data = &mut token_account_info.data.borrow_mut();
1069        let mut token_account =
1070            StateWithExtensionsMut::<Account>::unpack_uninitialized(token_account_data)?;
1071        token_account
1072            .init_extension::<ImmutableOwner>(true)
1073            .map(|_| ())
1074    }
1075
1076    /// Processes an [AmountToUiAmount](enum.TokenInstruction.html) instruction
1077    pub fn process_amount_to_ui_amount(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
1078        let account_info_iter = &mut accounts.iter();
1079        let mint_info = next_account_info(account_info_iter)?;
1080        check_program_account(mint_info.owner)?;
1081
1082        let mint_data = mint_info.data.borrow();
1083        let mint = StateWithExtensions::<Mint>::unpack(&mint_data)
1084            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
1085        let ui_amount = if let Ok(extension) = mint.get_extension::<InterestBearingConfig>() {
1086            let unix_timestamp = Clock::get()?.unix_timestamp;
1087            extension
1088                .amount_to_ui_amount(amount, mint.base.decimals, unix_timestamp)
1089                .ok_or(ProgramError::InvalidArgument)?
1090        } else {
1091            amount_to_ui_amount_string_trimmed(amount, mint.base.decimals)
1092        };
1093
1094        set_return_data(&ui_amount.into_bytes());
1095        Ok(())
1096    }
1097
1098    /// Processes an [AmountToUiAmount](enum.TokenInstruction.html) instruction
1099    pub fn process_ui_amount_to_amount(accounts: &[AccountInfo], ui_amount: &str) -> ProgramResult {
1100        let account_info_iter = &mut accounts.iter();
1101        let mint_info = next_account_info(account_info_iter)?;
1102        check_program_account(mint_info.owner)?;
1103
1104        let mint_data = mint_info.data.borrow();
1105        let mint = StateWithExtensions::<Mint>::unpack(&mint_data)
1106            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
1107        let amount = if let Ok(extension) = mint.get_extension::<InterestBearingConfig>() {
1108            let unix_timestamp = Clock::get()?.unix_timestamp;
1109            extension.try_ui_amount_into_amount(ui_amount, mint.base.decimals, unix_timestamp)?
1110        } else {
1111            try_ui_amount_into_amount(ui_amount.to_string(), mint.base.decimals)?
1112        };
1113
1114        set_return_data(&amount.to_le_bytes());
1115        Ok(())
1116    }
1117
1118    /// Processes a [CreateNativeMint](enum.TokenInstruction.html) instruction
1119    pub fn process_create_native_mint(accounts: &[AccountInfo]) -> ProgramResult {
1120        let account_info_iter = &mut accounts.iter();
1121        let payer_info = next_account_info(account_info_iter)?;
1122        let native_mint_info = next_account_info(account_info_iter)?;
1123        let system_program_info = next_account_info(account_info_iter)?;
1124
1125        if *native_mint_info.key != native_mint::id() {
1126            return Err(TokenError::InvalidMint.into());
1127        }
1128
1129        let rent = Rent::get()?;
1130        let new_minimum_balance = rent.minimum_balance(Mint::get_packed_len());
1131        let lamports_diff = new_minimum_balance.saturating_sub(native_mint_info.lamports());
1132        invoke(
1133            &system_instruction::transfer(payer_info.key, native_mint_info.key, lamports_diff),
1134            &[
1135                payer_info.clone(),
1136                native_mint_info.clone(),
1137                system_program_info.clone(),
1138            ],
1139        )?;
1140
1141        invoke_signed(
1142            &system_instruction::allocate(native_mint_info.key, Mint::get_packed_len() as u64),
1143            &[native_mint_info.clone(), system_program_info.clone()],
1144            &[native_mint::PROGRAM_ADDRESS_SEEDS],
1145        )?;
1146
1147        invoke_signed(
1148            &system_instruction::assign(
1149                native_mint_info.key,
1150                &crate::program::spl_token_2022::id(),
1151            ),
1152            &[native_mint_info.clone(), system_program_info.clone()],
1153            &[native_mint::PROGRAM_ADDRESS_SEEDS],
1154        )?;
1155
1156        Mint::pack(
1157            Mint {
1158                decimals: native_mint::DECIMALS,
1159                is_initialized: true,
1160                ..Mint::default()
1161            },
1162            &mut native_mint_info.data.borrow_mut(),
1163        )
1164    }
1165
1166    /// Processes an [InitializeNonTransferableMint](enum.TokenInstruction.html) instruction
1167    pub fn process_initialize_non_transferable_mint(accounts: &[AccountInfo]) -> ProgramResult {
1168        let account_info_iter = &mut accounts.iter();
1169        let mint_account_info = next_account_info(account_info_iter)?;
1170
1171        let mut mint_data = mint_account_info.data.borrow_mut();
1172        let mut mint = StateWithExtensionsMut::<Mint>::unpack_uninitialized(&mut mint_data)?;
1173        mint.init_extension::<NonTransferable>(true)?;
1174
1175        Ok(())
1176    }
1177
1178    /// Processes an [Instruction](enum.Instruction.html).
1179    pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
1180        let instruction = TokenInstruction::unpack(input)?;
1181
1182        match instruction {
1183            TokenInstruction::InitializeMint {
1184                decimals,
1185                mint_authority,
1186                freeze_authority,
1187            } => {
1188                msg!("Instruction: InitializeMint");
1189                Self::process_initialize_mint(accounts, decimals, mint_authority, freeze_authority)
1190            }
1191            TokenInstruction::InitializeMint2 {
1192                decimals,
1193                mint_authority,
1194                freeze_authority,
1195            } => {
1196                msg!("Instruction: InitializeMint2");
1197                Self::process_initialize_mint2(accounts, decimals, mint_authority, freeze_authority)
1198            }
1199            TokenInstruction::InitializeAccount => {
1200                msg!("Instruction: InitializeAccount");
1201                Self::process_initialize_account(accounts)
1202            }
1203            TokenInstruction::InitializeAccount2 { owner } => {
1204                msg!("Instruction: InitializeAccount2");
1205                Self::process_initialize_account2(accounts, owner)
1206            }
1207            TokenInstruction::InitializeAccount3 { owner } => {
1208                msg!("Instruction: InitializeAccount3");
1209                Self::process_initialize_account3(accounts, owner)
1210            }
1211            TokenInstruction::InitializeMultisig { m } => {
1212                msg!("Instruction: InitializeMultisig");
1213                Self::process_initialize_multisig(accounts, m)
1214            }
1215            TokenInstruction::InitializeMultisig2 { m } => {
1216                msg!("Instruction: InitializeMultisig2");
1217                Self::process_initialize_multisig2(accounts, m)
1218            }
1219            #[allow(deprecated)]
1220            TokenInstruction::Transfer { amount } => {
1221                msg!("Instruction: Transfer");
1222                Self::process_transfer(program_id, accounts, amount, None, None)
1223            }
1224            TokenInstruction::Approve { amount } => {
1225                msg!("Instruction: Approve");
1226                Self::process_approve(program_id, accounts, amount, None)
1227            }
1228            TokenInstruction::Revoke => {
1229                msg!("Instruction: Revoke");
1230                Self::process_revoke(program_id, accounts)
1231            }
1232            TokenInstruction::SetAuthority {
1233                authority_type,
1234                new_authority,
1235            } => {
1236                msg!("Instruction: SetAuthority");
1237                Self::process_set_authority(program_id, accounts, authority_type, new_authority)
1238            }
1239            TokenInstruction::MintTo { amount } => {
1240                msg!("Instruction: MintTo");
1241                Self::process_mint_to(program_id, accounts, amount, None)
1242            }
1243            TokenInstruction::Burn { amount } => {
1244                msg!("Instruction: Burn");
1245                Self::process_burn(program_id, accounts, amount, None)
1246            }
1247            TokenInstruction::CloseAccount => {
1248                msg!("Instruction: CloseAccount");
1249                Self::process_close_account(program_id, accounts)
1250            }
1251            TokenInstruction::FreezeAccount => {
1252                msg!("Instruction: FreezeAccount");
1253                Self::process_toggle_freeze_account(program_id, accounts, true)
1254            }
1255            TokenInstruction::ThawAccount => {
1256                msg!("Instruction: ThawAccount");
1257                Self::process_toggle_freeze_account(program_id, accounts, false)
1258            }
1259            TokenInstruction::TransferChecked { amount, decimals } => {
1260                msg!("Instruction: TransferChecked");
1261                Self::process_transfer(program_id, accounts, amount, Some(decimals), None)
1262            }
1263            TokenInstruction::ApproveChecked { amount, decimals } => {
1264                msg!("Instruction: ApproveChecked");
1265                Self::process_approve(program_id, accounts, amount, Some(decimals))
1266            }
1267            TokenInstruction::MintToChecked { amount, decimals } => {
1268                msg!("Instruction: MintToChecked");
1269                Self::process_mint_to(program_id, accounts, amount, Some(decimals))
1270            }
1271            TokenInstruction::BurnChecked { amount, decimals } => {
1272                msg!("Instruction: BurnChecked");
1273                Self::process_burn(program_id, accounts, amount, Some(decimals))
1274            }
1275            TokenInstruction::SyncNative => {
1276                msg!("Instruction: SyncNative");
1277                Self::process_sync_native(accounts)
1278            }
1279            TokenInstruction::GetAccountDataSize { extension_types } => {
1280                msg!("Instruction: GetAccountDataSize");
1281                Self::process_get_account_data_size(accounts, extension_types)
1282            }
1283            TokenInstruction::InitializeMintCloseAuthority { close_authority } => {
1284                msg!("Instruction: InitializeMintCloseAuthority");
1285                Self::process_initialize_mint_close_authority(accounts, close_authority)
1286            }
1287            TokenInstruction::TransferFeeExtension(instruction) => {
1288                transfer_fee::processor::process_instruction(program_id, accounts, instruction)
1289            }
1290            // TODO:
1291            TokenInstruction::ConfidentialTransferExtension => {
1292                Err(ProgramError::InvalidArgument)
1293                // confidential_transfer::processor::process_instruction(
1294                //     program_id,
1295                //     accounts,
1296                //     &input[1..],
1297                // )
1298            }
1299            TokenInstruction::DefaultAccountStateExtension => {
1300                default_account_state::processor::process_instruction(
1301                    program_id,
1302                    accounts,
1303                    &input[1..],
1304                )
1305            }
1306            TokenInstruction::InitializeImmutableOwner => {
1307                msg!("Instruction: InitializeImmutableOwner");
1308                Self::process_initialize_immutable_owner(accounts)
1309            }
1310            TokenInstruction::AmountToUiAmount { amount } => {
1311                msg!("Instruction: AmountToUiAmount");
1312                Self::process_amount_to_ui_amount(accounts, amount)
1313            }
1314            TokenInstruction::UiAmountToAmount { ui_amount } => {
1315                msg!("Instruction: UiAmountToAmount");
1316                Self::process_ui_amount_to_amount(accounts, ui_amount)
1317            }
1318            TokenInstruction::Reallocate { extension_types } => {
1319                msg!("Instruction: Reallocate");
1320                reallocate::process_reallocate(program_id, accounts, extension_types)
1321            }
1322            TokenInstruction::MemoTransferExtension => {
1323                memo_transfer::processor::process_instruction(program_id, accounts, &input[1..])
1324            }
1325            TokenInstruction::CreateNativeMint => {
1326                msg!("Instruction: CreateNativeMint");
1327                Self::process_create_native_mint(accounts)
1328            }
1329            TokenInstruction::InitializeNonTransferableMint => {
1330                msg!("Instruction: InitializeNonTransferableMint");
1331                Self::process_initialize_non_transferable_mint(accounts)
1332            }
1333            TokenInstruction::InterestBearingMintExtension => {
1334                interest_bearing_mint::processor::process_instruction(
1335                    program_id,
1336                    accounts,
1337                    &input[1..],
1338                )
1339            }
1340        }
1341    }
1342
1343    /// Validates owner(s) are present. Used for Mints and Accounts only.
1344    pub fn validate_owner(
1345        program_id: &Pubkey,
1346        expected_owner: &Pubkey,
1347        owner_account_info: &AccountInfo,
1348        owner_account_data_len: usize,
1349        signers: &[AccountInfo],
1350    ) -> ProgramResult {
1351        if !cmp_pubkeys(expected_owner, owner_account_info.key) {
1352            return Err(TokenError::OwnerMismatch.into());
1353        }
1354
1355        if cmp_pubkeys(program_id, owner_account_info.owner)
1356            && owner_account_data_len == Multisig::get_packed_len()
1357        {
1358            let multisig = Multisig::unpack(&owner_account_info.data.borrow())?;
1359            let mut num_signers = 0;
1360            let mut matched = [false; MAX_SIGNERS];
1361            for signer in signers.iter() {
1362                for (position, key) in multisig.signers[0..multisig.n as usize].iter().enumerate() {
1363                    if cmp_pubkeys(key, signer.key) && !matched[position] {
1364                        if !signer.is_signer {
1365                            return Err(ProgramError::MissingRequiredSignature);
1366                        }
1367                        matched[position] = true;
1368                        num_signers += 1;
1369                    }
1370                }
1371            }
1372            if num_signers < multisig.m {
1373                return Err(ProgramError::MissingRequiredSignature);
1374            }
1375            return Ok(());
1376        } else if !owner_account_info.is_signer {
1377            return Err(ProgramError::MissingRequiredSignature);
1378        }
1379        Ok(())
1380    }
1381
1382    fn get_required_account_extensions(
1383        mint_account_info: &AccountInfo,
1384    ) -> Result<Vec<ExtensionType>, ProgramError> {
1385        let mint_data = mint_account_info.data.borrow();
1386        let state = StateWithExtensions::<Mint>::unpack(&mint_data)
1387            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
1388        Self::get_required_account_extensions_from_unpacked_mint(mint_account_info.owner, &state)
1389    }
1390
1391    fn get_required_account_extensions_from_unpacked_mint(
1392        token_program_id: &Pubkey,
1393        state: &StateWithExtensions<Mint>,
1394    ) -> Result<Vec<ExtensionType>, ProgramError> {
1395        check_program_account(token_program_id)?;
1396        let mint_extensions: Vec<ExtensionType> = state.get_extension_types()?;
1397        Ok(ExtensionType::get_required_init_account_extensions(
1398            &mint_extensions,
1399        ))
1400    }
1401}
1402
1403impl PrintProgramError for TokenError {
1404    fn print<E>(&self)
1405    where
1406        E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
1407    {
1408        match self {
1409            TokenError::NotRentExempt => msg!("Error: Lamport balance below rent-exempt threshold"),
1410            TokenError::InsufficientFunds => msg!("Error: insufficient funds"),
1411            TokenError::InvalidMint => msg!("Error: Invalid Mint"),
1412            TokenError::MintMismatch => msg!("Error: Account not associated with this Mint"),
1413            TokenError::OwnerMismatch => msg!("Error: owner does not match"),
1414            TokenError::FixedSupply => msg!("Error: the total supply of this token is fixed"),
1415            TokenError::AlreadyInUse => msg!("Error: account or token already in use"),
1416            TokenError::InvalidNumberOfProvidedSigners => {
1417                msg!("Error: Invalid number of provided signers")
1418            }
1419            TokenError::InvalidNumberOfRequiredSigners => {
1420                msg!("Error: Invalid number of required signers")
1421            }
1422            TokenError::UninitializedState => msg!("Error: State is uninitialized"),
1423            TokenError::NativeNotSupported => {
1424                msg!("Error: Instruction does not support native tokens")
1425            }
1426            TokenError::NonNativeHasBalance => {
1427                msg!("Error: Non-native account can only be closed if its balance is zero")
1428            }
1429            TokenError::InvalidInstruction => msg!("Error: Invalid instruction"),
1430            TokenError::InvalidState => msg!("Error: Invalid account state for operation"),
1431            TokenError::Overflow => msg!("Error: Operation overflowed"),
1432            TokenError::AuthorityTypeNotSupported => {
1433                msg!("Error: Account does not support specified authority type")
1434            }
1435            TokenError::MintCannotFreeze => msg!("Error: This token mint cannot freeze accounts"),
1436            TokenError::AccountFrozen => msg!("Error: Account is frozen"),
1437            TokenError::MintDecimalsMismatch => {
1438                msg!("Error: decimals different from the Mint decimals")
1439            }
1440            TokenError::NonNativeNotSupported => {
1441                msg!("Error: Instruction does not support non-native tokens")
1442            }
1443            TokenError::ExtensionTypeMismatch => {
1444                msg!("Error: New extension type does not match already existing extensions")
1445            }
1446            TokenError::ExtensionBaseMismatch => {
1447                msg!("Error: Extension does not match the base type provided")
1448            }
1449            TokenError::ExtensionAlreadyInitialized => {
1450                msg!("Error: Extension already initialized on this account")
1451            }
1452            TokenError::ConfidentialTransferAccountHasBalance => {
1453                msg!("Error: An account can only be closed if its confidential balance is zero")
1454            }
1455            TokenError::ConfidentialTransferAccountNotApproved => {
1456                msg!("Error: Account not approved for confidential transfers")
1457            }
1458            TokenError::ConfidentialTransferDepositsAndTransfersDisabled => {
1459                msg!("Error: Account not accepting deposits or transfers")
1460            }
1461            TokenError::ConfidentialTransferElGamalPubkeyMismatch => {
1462                msg!("Error: ElGamal public key mismatch")
1463            }
1464            TokenError::ConfidentialTransferBalanceMismatch => {
1465                msg!("Error: Balance mismatch")
1466            }
1467            TokenError::MintHasSupply => {
1468                msg!("Error: Mint has non-zero supply. Burn all tokens before closing the mint")
1469            }
1470            TokenError::NoAuthorityExists => {
1471                msg!("Error: No authority exists to perform the desired operation");
1472            }
1473            TokenError::TransferFeeExceedsMaximum => {
1474                msg!("Error: Transfer fee exceeds maximum of 10,000 basis points");
1475            }
1476            TokenError::MintRequiredForTransfer => {
1477                msg!("Mint required for this account to transfer tokens, use `transfer_checked` or `transfer_checked_with_fee`");
1478            }
1479            TokenError::FeeMismatch => {
1480                msg!("Calculated fee does not match expected fee");
1481            }
1482            TokenError::FeeParametersMismatch => {
1483                msg!("Fee parameters associated with zero-knowledge proofs do not match fee parameters in mint")
1484            }
1485            TokenError::ImmutableOwner => {
1486                msg!("The owner authority cannot be changed");
1487            }
1488            TokenError::AccountHasWithheldTransferFees => {
1489                msg!("Error: An account can only be closed if its withheld fee balance is zero, harvest fees to the mint and try again");
1490            }
1491            TokenError::NoMemo => {
1492                msg!("Error: No memo in previous instruction; required for recipient to receive a transfer");
1493            }
1494            TokenError::NonTransferable => {
1495                msg!("Transfer is disabled for this mint");
1496            }
1497            TokenError::NonTransferableNeedsImmutableOwnership => {
1498                msg!("Non-transferable tokens can't be minted to an account without immutable ownership");
1499            }
1500            TokenError::MaximumPendingBalanceCreditCounterExceeded => {
1501                msg!("The total number of `Deposit` and `Transfer` instructions to an account cannot exceed the associated `maximum_pending_balance_credit_counter`");
1502            }
1503        }
1504    }
1505}