Skip to main content

rialo_spl_token_2022/
processor.rs

1// Copyright (c) Subzero Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3//! Program state processor
4
5use std::convert::{TryFrom, TryInto};
6
7use rialo_s_account_info::{next_account_info, AccountInfo};
8use rialo_s_clock::Clock;
9use rialo_s_cpi::{invoke, invoke_signed, set_return_data};
10use rialo_s_msg::msg;
11use rialo_s_program_error::{ProgramError, ProgramResult};
12use rialo_s_program_option::COption;
13use rialo_s_program_pack::Pack;
14use rialo_s_pubkey::Pubkey;
15use rialo_s_rent::Rent;
16use rialo_s_sdk_ids::system_program;
17use rialo_s_spl_pod::{
18    bytemuck::{pod_from_bytes, pod_from_bytes_mut},
19    primitives::{PodBool, PodU64},
20};
21use rialo_s_spl_token_group_interface::instruction::TokenGroupInstruction;
22use rialo_s_spl_token_metadata_interface::instruction::TokenMetadataInstruction;
23use rialo_s_system_interface::instruction as system_instruction;
24use rialo_s_sysvar::Sysvar;
25use rialo_spl_token_2022_interface::{
26    check_program_account,
27    error::TokenError,
28    extension::{
29        confidential_mint_burn::ConfidentialMintBurn,
30        confidential_transfer::{ConfidentialTransferAccount, ConfidentialTransferMint},
31        confidential_transfer_fee::{ConfidentialTransferFeeAmount, ConfidentialTransferFeeConfig},
32        cpi_guard::CpiGuard,
33        default_account_state::DefaultAccountState,
34        group_member_pointer::GroupMemberPointer,
35        group_pointer::GroupPointer,
36        immutable_owner::ImmutableOwner,
37        interest_bearing_mint::InterestBearingConfig,
38        memo_transfer::memo_required,
39        metadata_pointer::MetadataPointer,
40        mint_close_authority::MintCloseAuthority,
41        non_transferable::{NonTransferable, NonTransferableAccount},
42        pausable::{PausableAccount, PausableConfig},
43        permanent_delegate::{get_permanent_delegate, PermanentDelegate},
44        scaled_ui_amount::ScaledUiAmountConfig,
45        transfer_fee::{TransferFeeAmount, TransferFeeConfig},
46        transfer_hook::{TransferHook, TransferHookAccount},
47        AccountType, BaseStateWithExtensions, BaseStateWithExtensionsMut, ExtensionType,
48        PodStateWithExtensions, PodStateWithExtensionsMut,
49    },
50    instruction::{
51        decode_instruction_data, decode_instruction_type, derive_tombstone_pda,
52        is_valid_signer_index, AuthorityType, MAX_SIGNERS, TOMBSTONE_LEN, TOMBSTONE_SEED,
53    },
54    native_mint,
55    pod::{PodAccount, PodCOption, PodMint, PodMultisig},
56    state::{Account, AccountState, Mint, MintTombstone, PackedSizeOf},
57};
58
59use crate::{
60    extension::{
61        confidential_mint_burn, confidential_transfer, confidential_transfer_fee,
62        cpi_guard::{self, in_cpi},
63        default_account_state, group_member_pointer, group_pointer, interest_bearing_mint,
64        memo_transfer::{self, check_previous_sibling_instruction_is_memo},
65        metadata_pointer, pausable, reallocate, scaled_ui_amount, token_group, token_metadata,
66        transfer_fee, transfer_hook,
67    },
68    pod_instruction::{
69        decode_instruction_data_with_coption_pubkey, AmountCheckedData, AmountData,
70        InitializeMintData, InitializeMultisigData, PodTokenInstruction, SetAuthorityData,
71    },
72};
73
74pub(crate) enum TransferInstruction {
75    Unchecked,
76    Checked { decimals: u8 },
77    CheckedWithFee { decimals: u8, fee: u64 },
78}
79
80pub(crate) enum InstructionVariant {
81    Unchecked,
82    Checked { decimals: u8 },
83}
84/// Program state handler.
85pub struct Processor {}
86impl Processor {
87    fn _process_initialize_mint(
88        accounts: &[AccountInfo<'_>],
89        decimals: u8,
90        mint_authority: &Pubkey,
91        freeze_authority: PodCOption<Pubkey>,
92        rent_sysvar_account: bool,
93    ) -> ProgramResult {
94        let account_info_iter = &mut accounts.iter();
95        let mint_info = next_account_info(account_info_iter)?;
96        let mint_data_len = mint_info.data_len();
97        let mut mint_data = mint_info.data.borrow_mut();
98
99        // Rent handling:
100        // - InitializeMint:    rent_sysvar_account = true  -> next account = Rent sysvar
101        // - InitializeMint2:   rent_sysvar_account = false -> next account = tombstone PDA
102        let rent = if rent_sysvar_account {
103            // Original behavior (no tombstone)
104            let rent_info = next_account_info(account_info_iter)?;
105            Rent::from_account_info(rent_info)?
106        } else {
107            // InitializeMint2 with tombstone guard
108            let tomb_info = next_account_info(account_info_iter)?;
109
110            let (expected_pda, _bump) = derive_tombstone_pda(&crate::id(), mint_info.key);
111            if tomb_info.key != &expected_pda {
112                return Err(TokenError::TombstoneAddressMismatch.into());
113            }
114
115            // If tombstone exists and holds kelvins, this mint address is retired
116            if tomb_info.kelvins() > 0 {
117                return Err(TokenError::MintRetired.into());
118            }
119
120            Rent::get()?
121        };
122
123        if !rent.is_exempt(mint_info.kelvins(), mint_data_len) {
124            return Err(TokenError::NotRentExempt.into());
125        }
126
127        let mut mint = PodStateWithExtensionsMut::<PodMint>::unpack_uninitialized(&mut mint_data)?;
128        let extension_types = mint.get_extension_types()?;
129        if ExtensionType::try_calculate_account_len::<Mint>(&extension_types)? != mint_data_len {
130            return Err(ProgramError::InvalidAccountData);
131        }
132        ExtensionType::check_for_invalid_mint_extension_combinations(&extension_types)?;
133
134        if let Ok(default_account_state) = mint.get_extension_mut::<DefaultAccountState>() {
135            let default_account_state = AccountState::try_from(default_account_state.state)
136                .or(Err(ProgramError::InvalidAccountData))?;
137            if default_account_state == AccountState::Frozen && freeze_authority.is_none() {
138                return Err(TokenError::MintCannotFreeze.into());
139            }
140        }
141
142        mint.base.mint_authority = PodCOption::some(*mint_authority);
143        mint.base.decimals = decimals;
144        mint.base.is_initialized = PodBool::from_bool(true);
145        mint.base.freeze_authority = freeze_authority;
146        mint.init_account_type()?;
147
148        Ok(())
149    }
150
151    /// Processes an [`InitializeMint`](enum.TokenInstruction.html) instruction.
152    pub fn process_initialize_mint(
153        accounts: &[AccountInfo<'_>],
154        decimals: u8,
155        mint_authority: &Pubkey,
156        freeze_authority: PodCOption<Pubkey>,
157    ) -> ProgramResult {
158        Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority, true)
159    }
160
161    /// Processes an [`InitializeMint2`](enum.TokenInstruction.html)
162    /// instruction.
163    pub fn process_initialize_mint2(
164        accounts: &[AccountInfo<'_>],
165        decimals: u8,
166        mint_authority: &Pubkey,
167        freeze_authority: PodCOption<Pubkey>,
168    ) -> ProgramResult {
169        Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority, false)
170    }
171
172    fn _process_initialize_account(
173        accounts: &[AccountInfo<'_>],
174        owner: Option<&Pubkey>,
175        rent_sysvar_account: bool,
176    ) -> ProgramResult {
177        let account_info_iter = &mut accounts.iter();
178        let new_account_info = next_account_info(account_info_iter)?;
179        let mint_info = next_account_info(account_info_iter)?;
180        let owner = if let Some(owner) = owner {
181            owner
182        } else {
183            next_account_info(account_info_iter)?.key
184        };
185        let new_account_info_data_len = new_account_info.data_len();
186        let rent = if rent_sysvar_account {
187            Rent::from_account_info(next_account_info(account_info_iter)?)?
188        } else {
189            Rent::get()?
190        };
191
192        let mut account_data = new_account_info.data.borrow_mut();
193        // unpack_uninitialized checks account.base.is_initialized() under the hood
194        let mut account =
195            PodStateWithExtensionsMut::<PodAccount>::unpack_uninitialized(&mut account_data)?;
196
197        if !rent.is_exempt(new_account_info.kelvins(), new_account_info_data_len) {
198            return Err(TokenError::NotRentExempt.into());
199        }
200
201        // get_required_account_extensions checks mint validity
202        let mint_data = mint_info.data.borrow();
203        let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data)
204            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
205        if mint
206            .get_extension::<PermanentDelegate>()
207            .map(|e| Option::<Pubkey>::from(e.delegate).is_some())
208            .unwrap_or(false)
209        {
210            msg!("Warning: Mint has a permanent delegate, so tokens in this account may be seized at any time");
211        }
212        let required_extensions =
213            Self::get_required_account_extensions_from_unpacked_mint(mint_info.owner, &mint)?;
214        if ExtensionType::try_calculate_account_len::<Account>(&required_extensions)?
215            > new_account_info_data_len
216        {
217            return Err(ProgramError::InvalidAccountData);
218        }
219        for extension in required_extensions {
220            account.init_account_extension_from_type(extension)?;
221        }
222
223        let starting_state =
224            if let Ok(default_account_state) = mint.get_extension::<DefaultAccountState>() {
225                AccountState::try_from(default_account_state.state)
226                    .or(Err(ProgramError::InvalidAccountData))?
227            } else {
228                AccountState::Initialized
229            };
230
231        account.base.mint = *mint_info.key;
232        account.base.owner = *owner;
233        account.base.close_authority = PodCOption::none();
234        account.base.delegate = PodCOption::none();
235        account.base.delegated_amount = 0.into();
236        account.base.state = starting_state.into();
237        if mint_info.key == &native_mint::id() {
238            let rent_exempt_reserve = rent.minimum_balance(new_account_info_data_len);
239            account.base.is_native = PodCOption::some(rent_exempt_reserve.into());
240            account.base.amount = new_account_info
241                .kelvins()
242                .checked_sub(rent_exempt_reserve)
243                .ok_or(TokenError::Overflow)?
244                .into();
245        } else {
246            account.base.is_native = PodCOption::none();
247            account.base.amount = 0.into();
248        };
249
250        account.init_account_type()?;
251
252        Ok(())
253    }
254
255    /// Processes an [`InitializeAccount`](enum.TokenInstruction.html)
256    /// instruction.
257    pub fn process_initialize_account(accounts: &[AccountInfo<'_>]) -> ProgramResult {
258        Self::_process_initialize_account(accounts, None, true)
259    }
260
261    /// Processes an [`InitializeAccount2`](enum.TokenInstruction.html)
262    /// instruction.
263    pub fn process_initialize_account2(
264        accounts: &[AccountInfo<'_>],
265        owner: &Pubkey,
266    ) -> ProgramResult {
267        Self::_process_initialize_account(accounts, Some(owner), true)
268    }
269
270    /// Processes an [`InitializeAccount3`](enum.TokenInstruction.html)
271    /// instruction.
272    pub fn process_initialize_account3(
273        accounts: &[AccountInfo<'_>],
274        owner: &Pubkey,
275    ) -> ProgramResult {
276        Self::_process_initialize_account(accounts, Some(owner), false)
277    }
278
279    fn _process_initialize_multisig(
280        accounts: &[AccountInfo<'_>],
281        m: u8,
282        rent_sysvar_account: bool,
283    ) -> ProgramResult {
284        let account_info_iter = &mut accounts.iter();
285        let multisig_info = next_account_info(account_info_iter)?;
286        let multisig_info_data_len = multisig_info.data_len();
287        let rent = if rent_sysvar_account {
288            Rent::from_account_info(next_account_info(account_info_iter)?)?
289        } else {
290            Rent::get()?
291        };
292
293        let mut multisig_data = multisig_info.data.borrow_mut();
294        let multisig = pod_from_bytes_mut::<PodMultisig>(&mut multisig_data)?;
295        if bool::from(multisig.is_initialized) {
296            return Err(TokenError::AlreadyInUse.into());
297        }
298
299        if !rent.is_exempt(multisig_info.kelvins(), multisig_info_data_len) {
300            return Err(TokenError::NotRentExempt.into());
301        }
302
303        let signer_infos = account_info_iter.as_slice();
304        multisig.m = m;
305        multisig.n = signer_infos.len() as u8;
306        if !is_valid_signer_index(multisig.n as usize) {
307            return Err(TokenError::InvalidNumberOfProvidedSigners.into());
308        }
309        if !is_valid_signer_index(multisig.m as usize) {
310            return Err(TokenError::InvalidNumberOfRequiredSigners.into());
311        }
312        for (i, signer_info) in signer_infos.iter().enumerate() {
313            multisig.signers[i] = *signer_info.key;
314        }
315        multisig.is_initialized = true.into();
316
317        Ok(())
318    }
319
320    /// Processes a [`InitializeMultisig`](enum.TokenInstruction.html)
321    /// instruction.
322    pub fn process_initialize_multisig(accounts: &[AccountInfo<'_>], m: u8) -> ProgramResult {
323        Self::_process_initialize_multisig(accounts, m, true)
324    }
325
326    /// Processes a [`InitializeMultisig2`](enum.TokenInstruction.html)
327    /// instruction.
328    pub fn process_initialize_multisig2(accounts: &[AccountInfo<'_>], m: u8) -> ProgramResult {
329        Self::_process_initialize_multisig(accounts, m, false)
330    }
331
332    /// Processes a [`Transfer`](enum.TokenInstruction.html) instruction.
333    pub(crate) fn process_transfer(
334        program_id: &Pubkey,
335        accounts: &[AccountInfo<'_>],
336        amount: u64,
337        transfer_instruction: TransferInstruction,
338    ) -> ProgramResult {
339        let account_info_iter = &mut accounts.iter();
340
341        let source_account_info = next_account_info(account_info_iter)?;
342
343        let expected_mint_info = match transfer_instruction {
344            TransferInstruction::Unchecked => None,
345            TransferInstruction::Checked { decimals }
346            | TransferInstruction::CheckedWithFee { decimals, .. } => {
347                Some((next_account_info(account_info_iter)?, decimals))
348            }
349        };
350
351        let destination_account_info = next_account_info(account_info_iter)?;
352        let authority_info = next_account_info(account_info_iter)?;
353        let authority_info_data_len = authority_info.data_len();
354
355        let mut source_account_data = source_account_info.data.borrow_mut();
356        let mut source_account =
357            PodStateWithExtensionsMut::<PodAccount>::unpack(&mut source_account_data)?;
358        if source_account.base.is_frozen() {
359            return Err(TokenError::AccountFrozen.into());
360        }
361        let source_amount = u64::from(source_account.base.amount);
362        if source_amount < amount {
363            return Err(TokenError::InsufficientFunds.into());
364        }
365        if source_account
366            .get_extension::<NonTransferableAccount>()
367            .is_ok()
368        {
369            return Err(TokenError::NonTransferable.into());
370        }
371
372        let (calculated_fee, maybe_permanent_delegate, maybe_transfer_hook_program_id) =
373            if let Some((mint_info, expected_decimals)) = expected_mint_info {
374                if &source_account.base.mint != mint_info.key {
375                    return Err(TokenError::MintMismatch.into());
376                }
377
378                let mint_data = mint_info.try_borrow_data()?;
379                let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data)?;
380
381                if expected_decimals != mint.base.decimals {
382                    return Err(TokenError::MintDecimalsMismatch.into());
383                }
384
385                let fee = if let Ok(transfer_fee_config) = mint.get_extension::<TransferFeeConfig>()
386                {
387                    transfer_fee_config
388                        .calculate_fee(Clock::get()?.unix_timestamp as u64, amount)
389                        .ok_or(TokenError::Overflow)?
390                } else {
391                    0
392                };
393
394                if let Ok(extension) = mint.get_extension::<PausableConfig>() {
395                    if extension.paused.into() {
396                        return Err(TokenError::MintPaused.into());
397                    }
398                }
399
400                let maybe_permanent_delegate = get_permanent_delegate(&mint);
401                let maybe_transfer_hook_program_id = transfer_hook::get_program_id(&mint);
402
403                (
404                    fee,
405                    maybe_permanent_delegate,
406                    maybe_transfer_hook_program_id,
407                )
408            } else {
409                // Transfer hook extension exists on the account, but no mint
410                // was provided to figure out required accounts, abort
411                if source_account
412                    .get_extension::<TransferHookAccount>()
413                    .is_ok()
414                {
415                    return Err(TokenError::MintRequiredForTransfer.into());
416                }
417
418                // Transfer fee amount extension exists on the account, but no mint
419                // was provided to calculate the fee, abort
420                if source_account
421                    .get_extension_mut::<TransferFeeAmount>()
422                    .is_ok()
423                {
424                    return Err(TokenError::MintRequiredForTransfer.into());
425                }
426
427                // Pausable extension exists on the account, but no mint
428                // was provided to see if it's paused, abort
429                if source_account.get_extension::<PausableAccount>().is_ok() {
430                    return Err(TokenError::MintRequiredForTransfer.into());
431                }
432
433                (0, None, None)
434            };
435        if let TransferInstruction::CheckedWithFee { fee, .. } = transfer_instruction {
436            if calculated_fee != fee {
437                msg!("Calculated fee {}, received {}", calculated_fee, fee);
438                return Err(TokenError::FeeMismatch.into());
439            }
440        }
441
442        let self_transfer = source_account_info.key == destination_account_info.key;
443        if let Ok(cpi_guard) = source_account.get_extension::<CpiGuard>() {
444            // Blocks all cases where the authority has signed if CPI Guard is
445            // enabled, including:
446            // * the account is delegated to the owner
447            // * the account owner is the permanent delegate
448            if *authority_info.key == source_account.base.owner
449                && cpi_guard.lock_cpi.into()
450                && in_cpi()
451            {
452                return Err(TokenError::CpiGuardTransferBlocked.into());
453            }
454        }
455        match (source_account.base.delegate, maybe_permanent_delegate) {
456            (_, Some(ref delegate)) if authority_info.key == delegate => Self::validate_owner(
457                program_id,
458                delegate,
459                authority_info,
460                authority_info_data_len,
461                account_info_iter.as_slice(),
462            )?,
463            (
464                PodCOption {
465                    option: PodCOption::<Pubkey>::SOME,
466                    value: delegate,
467                },
468                _,
469            ) if authority_info.key == &delegate => {
470                Self::validate_owner(
471                    program_id,
472                    &delegate,
473                    authority_info,
474                    authority_info_data_len,
475                    account_info_iter.as_slice(),
476                )?;
477                let delegated_amount = u64::from(source_account.base.delegated_amount);
478                if delegated_amount < amount {
479                    return Err(TokenError::InsufficientFunds.into());
480                }
481                if !self_transfer {
482                    source_account.base.delegated_amount = delegated_amount
483                        .checked_sub(amount)
484                        .ok_or(TokenError::Overflow)?
485                        .into();
486                    if u64::from(source_account.base.delegated_amount) == 0 {
487                        source_account.base.delegate = PodCOption::none();
488                    }
489                }
490            }
491            _ => {
492                Self::validate_owner(
493                    program_id,
494                    &source_account.base.owner,
495                    authority_info,
496                    authority_info_data_len,
497                    account_info_iter.as_slice(),
498                )?;
499            }
500        }
501
502        // Revisit this later to see if it's worth adding a check to reduce
503        // compute costs, ie:
504        // if self_transfer || amount == 0
505        check_program_account(source_account_info.owner)?;
506        check_program_account(destination_account_info.owner)?;
507
508        // This check MUST occur just before the amounts are manipulated
509        // to ensure self-transfers are fully validated
510        if self_transfer {
511            if memo_required(&source_account) {
512                check_previous_sibling_instruction_is_memo()?;
513            }
514            return Ok(());
515        }
516
517        // self-transfer was dealt with earlier, so this *should* be safe
518        let mut destination_account_data = destination_account_info.data.borrow_mut();
519        let mut destination_account =
520            PodStateWithExtensionsMut::<PodAccount>::unpack(&mut destination_account_data)?;
521
522        if destination_account.base.is_frozen() {
523            return Err(TokenError::AccountFrozen.into());
524        }
525        if source_account.base.mint != destination_account.base.mint {
526            return Err(TokenError::MintMismatch.into());
527        }
528
529        if memo_required(&destination_account) {
530            check_previous_sibling_instruction_is_memo()?;
531        }
532
533        if let Ok(confidential_transfer_state) =
534            destination_account.get_extension::<ConfidentialTransferAccount>()
535        {
536            confidential_transfer_state.non_confidential_transfer_allowed()?
537        }
538
539        source_account.base.amount = source_amount
540            .checked_sub(amount)
541            .ok_or(TokenError::Overflow)?
542            .into();
543        let credited_amount = amount
544            .checked_sub(calculated_fee)
545            .ok_or(TokenError::Overflow)?;
546        destination_account.base.amount = u64::from(destination_account.base.amount)
547            .checked_add(credited_amount)
548            .ok_or(TokenError::Overflow)?
549            .into();
550        if calculated_fee > 0 {
551            if let Ok(extension) = destination_account.get_extension_mut::<TransferFeeAmount>() {
552                let new_withheld_amount = u64::from(extension.withheld_amount)
553                    .checked_add(calculated_fee)
554                    .ok_or(TokenError::Overflow)?;
555                extension.withheld_amount = new_withheld_amount.into();
556            } else {
557                // Use the generic error since this should never happen. If there's
558                // a fee, then the mint has a fee configured, which means all accounts
559                // must have the withholding.
560                return Err(TokenError::InvalidState.into());
561            }
562        }
563
564        if source_account.base.is_native() {
565            let source_starting_kelvins = source_account_info.kelvins();
566            **source_account_info.kelvins.borrow_mut() = source_starting_kelvins
567                .checked_sub(amount)
568                .ok_or(TokenError::Overflow)?;
569
570            let destination_starting_kelvins = destination_account_info.kelvins();
571            **destination_account_info.kelvins.borrow_mut() = destination_starting_kelvins
572                .checked_add(amount)
573                .ok_or(TokenError::Overflow)?;
574        }
575
576        if let Some(program_id) = maybe_transfer_hook_program_id {
577            if let Some((mint_info, _)) = expected_mint_info {
578                // set transferring flags
579                transfer_hook::set_transferring(&mut source_account)?;
580                transfer_hook::set_transferring(&mut destination_account)?;
581
582                // must drop these to avoid the double-borrow during CPI
583                drop(source_account_data);
584                drop(destination_account_data);
585                rialo_s_spl_transfer_hook_interface::onchain::invoke_execute(
586                    &program_id,
587                    source_account_info.clone(),
588                    mint_info.clone(),
589                    destination_account_info.clone(),
590                    authority_info.clone(),
591                    account_info_iter.as_slice(),
592                    amount,
593                )?;
594
595                // unset transferring flag
596                transfer_hook::unset_transferring(source_account_info)?;
597                transfer_hook::unset_transferring(destination_account_info)?;
598            } else {
599                return Err(TokenError::MintRequiredForTransfer.into());
600            }
601        }
602
603        Ok(())
604    }
605
606    /// Processes an [`Approve`](enum.TokenInstruction.html) instruction.
607    pub(crate) fn process_approve(
608        program_id: &Pubkey,
609        accounts: &[AccountInfo<'_>],
610        amount: u64,
611        instruction_variant: InstructionVariant,
612    ) -> ProgramResult {
613        let account_info_iter = &mut accounts.iter();
614
615        let source_account_info = next_account_info(account_info_iter)?;
616
617        let expected_mint_info =
618            if let InstructionVariant::Checked { decimals } = instruction_variant {
619                Some((next_account_info(account_info_iter)?, decimals))
620            } else {
621                None
622            };
623        let delegate_info = next_account_info(account_info_iter)?;
624        let owner_info = next_account_info(account_info_iter)?;
625        let owner_info_data_len = owner_info.data_len();
626
627        let mut source_account_data = source_account_info.data.borrow_mut();
628        let source_account =
629            PodStateWithExtensionsMut::<PodAccount>::unpack(&mut source_account_data)?;
630
631        if source_account.base.is_frozen() {
632            return Err(TokenError::AccountFrozen.into());
633        }
634
635        if let Some((mint_info, expected_decimals)) = expected_mint_info {
636            if &source_account.base.mint != mint_info.key {
637                return Err(TokenError::MintMismatch.into());
638            }
639
640            let mint_data = mint_info.data.borrow();
641            let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data)?;
642            if expected_decimals != mint.base.decimals {
643                return Err(TokenError::MintDecimalsMismatch.into());
644            }
645        }
646
647        Self::validate_owner(
648            program_id,
649            &source_account.base.owner,
650            owner_info,
651            owner_info_data_len,
652            account_info_iter.as_slice(),
653        )?;
654
655        if let Ok(cpi_guard) = source_account.get_extension::<CpiGuard>() {
656            if cpi_guard.lock_cpi.into() && in_cpi() {
657                return Err(TokenError::CpiGuardApproveBlocked.into());
658            }
659        }
660
661        source_account.base.delegate = PodCOption::some(*delegate_info.key);
662        source_account.base.delegated_amount = amount.into();
663
664        Ok(())
665    }
666
667    /// Processes an [`Revoke`](enum.TokenInstruction.html) instruction.
668    pub fn process_revoke(program_id: &Pubkey, accounts: &[AccountInfo<'_>]) -> ProgramResult {
669        let account_info_iter = &mut accounts.iter();
670        let source_account_info = next_account_info(account_info_iter)?;
671        let authority_info = next_account_info(account_info_iter)?;
672        let authority_info_data_len = authority_info.data_len();
673
674        let mut source_account_data = source_account_info.data.borrow_mut();
675        let source_account =
676            PodStateWithExtensionsMut::<PodAccount>::unpack(&mut source_account_data)?;
677        if source_account.base.is_frozen() {
678            return Err(TokenError::AccountFrozen.into());
679        }
680
681        Self::validate_owner(
682            program_id,
683            match &source_account.base.delegate {
684                PodCOption {
685                    option: PodCOption::<Pubkey>::SOME,
686                    value: delegate,
687                } if authority_info.key == delegate => delegate,
688                _ => &source_account.base.owner,
689            },
690            authority_info,
691            authority_info_data_len,
692            account_info_iter.as_slice(),
693        )?;
694
695        source_account.base.delegate = PodCOption::none();
696        source_account.base.delegated_amount = 0.into();
697
698        Ok(())
699    }
700
701    /// Processes a [`SetAuthority`](enum.TokenInstruction.html) instruction.
702    pub fn process_set_authority(
703        program_id: &Pubkey,
704        accounts: &[AccountInfo<'_>],
705        authority_type: AuthorityType,
706        new_authority: PodCOption<Pubkey>,
707    ) -> ProgramResult {
708        let account_info_iter = &mut accounts.iter();
709        let account_info = next_account_info(account_info_iter)?;
710        let authority_info = next_account_info(account_info_iter)?;
711        let authority_info_data_len = authority_info.data_len();
712
713        let mut account_data = account_info.data.borrow_mut();
714        if let Ok(mut account) = PodStateWithExtensionsMut::<PodAccount>::unpack(&mut account_data)
715        {
716            if account.base.is_frozen() {
717                return Err(TokenError::AccountFrozen.into());
718            }
719
720            match authority_type {
721                AuthorityType::AccountOwner => {
722                    Self::validate_owner(
723                        program_id,
724                        &account.base.owner,
725                        authority_info,
726                        authority_info_data_len,
727                        account_info_iter.as_slice(),
728                    )?;
729
730                    if account.get_extension_mut::<ImmutableOwner>().is_ok() {
731                        return Err(TokenError::ImmutableOwner.into());
732                    }
733
734                    if let Ok(cpi_guard) = account.get_extension::<CpiGuard>() {
735                        if cpi_guard.lock_cpi.into() && in_cpi() {
736                            return Err(TokenError::CpiGuardSetAuthorityBlocked.into());
737                        } else if cpi_guard.lock_cpi.into() {
738                            return Err(TokenError::CpiGuardOwnerChangeBlocked.into());
739                        }
740                    }
741
742                    if let PodCOption {
743                        option: PodCOption::<Pubkey>::SOME,
744                        value: authority,
745                    } = new_authority
746                    {
747                        account.base.owner = authority;
748                    } else {
749                        return Err(TokenError::InvalidInstruction.into());
750                    }
751
752                    account.base.delegate = PodCOption::none();
753                    account.base.delegated_amount = 0.into();
754
755                    if account.base.is_native() {
756                        account.base.close_authority = PodCOption::none();
757                    }
758                }
759                AuthorityType::CloseAccount => {
760                    let authority = account.base.close_authority.unwrap_or(account.base.owner);
761                    Self::validate_owner(
762                        program_id,
763                        &authority,
764                        authority_info,
765                        authority_info_data_len,
766                        account_info_iter.as_slice(),
767                    )?;
768
769                    if let Ok(cpi_guard) = account.get_extension::<CpiGuard>() {
770                        if cpi_guard.lock_cpi.into() && in_cpi() && new_authority.is_some() {
771                            return Err(TokenError::CpiGuardSetAuthorityBlocked.into());
772                        }
773                    }
774
775                    account.base.close_authority = new_authority;
776                }
777                _ => {
778                    return Err(TokenError::AuthorityTypeNotSupported.into());
779                }
780            }
781        } else if let Ok(mut mint) = PodStateWithExtensionsMut::<PodMint>::unpack(&mut account_data)
782        {
783            match authority_type {
784                AuthorityType::MintTokens => {
785                    // Once a mint's supply is fixed, it cannot be undone by setting a new
786                    // mint_authority
787                    let mint_authority = mint
788                        .base
789                        .mint_authority
790                        .ok_or(Into::<ProgramError>::into(TokenError::FixedSupply))?;
791                    Self::validate_owner(
792                        program_id,
793                        &mint_authority,
794                        authority_info,
795                        authority_info_data_len,
796                        account_info_iter.as_slice(),
797                    )?;
798                    mint.base.mint_authority = new_authority;
799                }
800                AuthorityType::FreezeAccount => {
801                    // Once a mint's freeze authority is disabled, it cannot be re-enabled by
802                    // setting a new freeze_authority
803                    let freeze_authority = mint
804                        .base
805                        .freeze_authority
806                        .ok_or(Into::<ProgramError>::into(TokenError::MintCannotFreeze))?;
807                    Self::validate_owner(
808                        program_id,
809                        &freeze_authority,
810                        authority_info,
811                        authority_info_data_len,
812                        account_info_iter.as_slice(),
813                    )?;
814                    mint.base.freeze_authority = new_authority;
815                }
816                AuthorityType::CloseMint => {
817                    let extension = mint.get_extension_mut::<MintCloseAuthority>()?;
818                    let maybe_close_authority: Option<Pubkey> = extension.close_authority.into();
819                    let close_authority =
820                        maybe_close_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
821                    Self::validate_owner(
822                        program_id,
823                        &close_authority,
824                        authority_info,
825                        authority_info_data_len,
826                        account_info_iter.as_slice(),
827                    )?;
828                    extension.close_authority = new_authority.try_into()?;
829                }
830                AuthorityType::TransferFeeConfig => {
831                    let extension = mint.get_extension_mut::<TransferFeeConfig>()?;
832                    let maybe_transfer_fee_config_authority: Option<Pubkey> =
833                        extension.transfer_fee_config_authority.into();
834                    let transfer_fee_config_authority = maybe_transfer_fee_config_authority
835                        .ok_or(TokenError::AuthorityTypeNotSupported)?;
836                    Self::validate_owner(
837                        program_id,
838                        &transfer_fee_config_authority,
839                        authority_info,
840                        authority_info_data_len,
841                        account_info_iter.as_slice(),
842                    )?;
843                    extension.transfer_fee_config_authority = new_authority.try_into()?;
844                }
845                AuthorityType::WithheldWithdraw => {
846                    let extension = mint.get_extension_mut::<TransferFeeConfig>()?;
847                    let maybe_withdraw_withheld_authority: Option<Pubkey> =
848                        extension.withdraw_withheld_authority.into();
849                    let withdraw_withheld_authority = maybe_withdraw_withheld_authority
850                        .ok_or(TokenError::AuthorityTypeNotSupported)?;
851                    Self::validate_owner(
852                        program_id,
853                        &withdraw_withheld_authority,
854                        authority_info,
855                        authority_info_data_len,
856                        account_info_iter.as_slice(),
857                    )?;
858                    extension.withdraw_withheld_authority = new_authority.try_into()?;
859                }
860                AuthorityType::InterestRate => {
861                    let extension = mint.get_extension_mut::<InterestBearingConfig>()?;
862                    let maybe_rate_authority: Option<Pubkey> = extension.rate_authority.into();
863                    let rate_authority =
864                        maybe_rate_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
865                    Self::validate_owner(
866                        program_id,
867                        &rate_authority,
868                        authority_info,
869                        authority_info_data_len,
870                        account_info_iter.as_slice(),
871                    )?;
872                    extension.rate_authority = new_authority.try_into()?;
873                }
874                AuthorityType::PermanentDelegate => {
875                    let extension = mint.get_extension_mut::<PermanentDelegate>()?;
876                    let maybe_delegate: Option<Pubkey> = extension.delegate.into();
877                    let delegate = maybe_delegate.ok_or(TokenError::AuthorityTypeNotSupported)?;
878                    Self::validate_owner(
879                        program_id,
880                        &delegate,
881                        authority_info,
882                        authority_info_data_len,
883                        account_info_iter.as_slice(),
884                    )?;
885                    extension.delegate = new_authority.try_into()?;
886                }
887                AuthorityType::ConfidentialTransferMint => {
888                    let extension = mint.get_extension_mut::<ConfidentialTransferMint>()?;
889                    let maybe_confidential_transfer_mint_authority: Option<Pubkey> =
890                        extension.authority.into();
891                    let confidential_transfer_mint_authority =
892                        maybe_confidential_transfer_mint_authority
893                            .ok_or(TokenError::AuthorityTypeNotSupported)?;
894                    Self::validate_owner(
895                        program_id,
896                        &confidential_transfer_mint_authority,
897                        authority_info,
898                        authority_info_data_len,
899                        account_info_iter.as_slice(),
900                    )?;
901                    extension.authority = new_authority.try_into()?;
902                }
903                AuthorityType::TransferHookProgramId => {
904                    let extension = mint.get_extension_mut::<TransferHook>()?;
905                    let maybe_authority: Option<Pubkey> = extension.authority.into();
906                    let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
907                    Self::validate_owner(
908                        program_id,
909                        &authority,
910                        authority_info,
911                        authority_info_data_len,
912                        account_info_iter.as_slice(),
913                    )?;
914                    extension.authority = new_authority.try_into()?;
915                }
916                AuthorityType::ConfidentialTransferFeeConfig => {
917                    let extension = mint.get_extension_mut::<ConfidentialTransferFeeConfig>()?;
918                    let maybe_authority: Option<Pubkey> = extension.authority.into();
919                    let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
920                    Self::validate_owner(
921                        program_id,
922                        &authority,
923                        authority_info,
924                        authority_info_data_len,
925                        account_info_iter.as_slice(),
926                    )?;
927                    extension.authority = new_authority.try_into()?;
928                }
929                AuthorityType::MetadataPointer => {
930                    let extension = mint.get_extension_mut::<MetadataPointer>()?;
931                    let maybe_authority: Option<Pubkey> = extension.authority.into();
932                    let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
933                    Self::validate_owner(
934                        program_id,
935                        &authority,
936                        authority_info,
937                        authority_info_data_len,
938                        account_info_iter.as_slice(),
939                    )?;
940                    extension.authority = new_authority.try_into()?;
941                }
942                AuthorityType::GroupPointer => {
943                    let extension = mint.get_extension_mut::<GroupPointer>()?;
944                    let maybe_authority: Option<Pubkey> = extension.authority.into();
945                    let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
946                    Self::validate_owner(
947                        program_id,
948                        &authority,
949                        authority_info,
950                        authority_info_data_len,
951                        account_info_iter.as_slice(),
952                    )?;
953                    extension.authority = new_authority.try_into()?;
954                }
955                AuthorityType::GroupMemberPointer => {
956                    let extension = mint.get_extension_mut::<GroupMemberPointer>()?;
957                    let maybe_authority: Option<Pubkey> = extension.authority.into();
958                    let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
959                    Self::validate_owner(
960                        program_id,
961                        &authority,
962                        authority_info,
963                        authority_info_data_len,
964                        account_info_iter.as_slice(),
965                    )?;
966                    extension.authority = new_authority.try_into()?;
967                }
968                AuthorityType::ScaledUiAmount => {
969                    let extension = mint.get_extension_mut::<ScaledUiAmountConfig>()?;
970                    let maybe_authority: Option<Pubkey> = extension.authority.into();
971                    let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
972                    Self::validate_owner(
973                        program_id,
974                        &authority,
975                        authority_info,
976                        authority_info_data_len,
977                        account_info_iter.as_slice(),
978                    )?;
979                    extension.authority = new_authority.try_into()?;
980                }
981                AuthorityType::Pause => {
982                    let extension = mint.get_extension_mut::<PausableConfig>()?;
983                    let maybe_authority: Option<Pubkey> = extension.authority.into();
984                    let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
985                    Self::validate_owner(
986                        program_id,
987                        &authority,
988                        authority_info,
989                        authority_info_data_len,
990                        account_info_iter.as_slice(),
991                    )?;
992                    extension.authority = new_authority.try_into()?;
993                }
994                _ => {
995                    return Err(TokenError::AuthorityTypeNotSupported.into());
996                }
997            }
998        } else {
999            return Err(ProgramError::InvalidAccountData);
1000        }
1001
1002        Ok(())
1003    }
1004
1005    /// Processes a [`MintTo`](enum.TokenInstruction.html) instruction.
1006    pub(crate) fn process_mint_to(
1007        program_id: &Pubkey,
1008        accounts: &[AccountInfo<'_>],
1009        amount: u64,
1010        instruction_variant: InstructionVariant,
1011    ) -> ProgramResult {
1012        let account_info_iter = &mut accounts.iter();
1013        let mint_info = next_account_info(account_info_iter)?;
1014        let destination_account_info = next_account_info(account_info_iter)?;
1015        let owner_info = next_account_info(account_info_iter)?;
1016        let owner_info_data_len = owner_info.data_len();
1017
1018        let mut destination_account_data = destination_account_info.data.borrow_mut();
1019        let destination_account =
1020            PodStateWithExtensionsMut::<PodAccount>::unpack(&mut destination_account_data)?;
1021        if destination_account.base.is_frozen() {
1022            return Err(TokenError::AccountFrozen.into());
1023        }
1024
1025        if destination_account.base.is_native() {
1026            return Err(TokenError::NativeNotSupported.into());
1027        }
1028        if mint_info.key != &destination_account.base.mint {
1029            return Err(TokenError::MintMismatch.into());
1030        }
1031
1032        let mut mint_data = mint_info.data.borrow_mut();
1033        let mint = PodStateWithExtensionsMut::<PodMint>::unpack(&mut mint_data)?;
1034
1035        // If the mint if non-transferable, only allow minting to accounts
1036        // with immutable ownership.
1037        if mint.get_extension::<NonTransferable>().is_ok()
1038            && destination_account
1039                .get_extension::<ImmutableOwner>()
1040                .is_err()
1041        {
1042            return Err(TokenError::NonTransferableNeedsImmutableOwnership.into());
1043        }
1044
1045        if let Ok(extension) = mint.get_extension::<PausableConfig>() {
1046            if extension.paused.into() {
1047                return Err(TokenError::MintPaused.into());
1048            }
1049        }
1050
1051        if mint.get_extension::<ConfidentialMintBurn>().is_ok() {
1052            return Err(TokenError::IllegalMintBurnConversion.into());
1053        }
1054
1055        if let InstructionVariant::Checked { decimals } = instruction_variant {
1056            if decimals != mint.base.decimals {
1057                return Err(TokenError::MintDecimalsMismatch.into());
1058            }
1059        }
1060
1061        match &mint.base.mint_authority {
1062            PodCOption {
1063                option: PodCOption::<Pubkey>::SOME,
1064                value: mint_authority,
1065            } => Self::validate_owner(
1066                program_id,
1067                mint_authority,
1068                owner_info,
1069                owner_info_data_len,
1070                account_info_iter.as_slice(),
1071            )?,
1072            _ => return Err(TokenError::FixedSupply.into()),
1073        }
1074
1075        // Revisit this later to see if it's worth adding a check to reduce
1076        // compute costs, ie:
1077        // if amount == 0
1078        check_program_account(mint_info.owner)?;
1079        check_program_account(destination_account_info.owner)?;
1080
1081        destination_account.base.amount = u64::from(destination_account.base.amount)
1082            .checked_add(amount)
1083            .ok_or(TokenError::Overflow)?
1084            .into();
1085
1086        mint.base.supply = u64::from(mint.base.supply)
1087            .checked_add(amount)
1088            .ok_or(TokenError::Overflow)?
1089            .into();
1090
1091        Ok(())
1092    }
1093
1094    /// Processes a [`Burn`](enum.TokenInstruction.html) instruction.
1095    pub(crate) fn process_burn(
1096        program_id: &Pubkey,
1097        accounts: &[AccountInfo<'_>],
1098        amount: u64,
1099        instruction_variant: InstructionVariant,
1100    ) -> ProgramResult {
1101        let account_info_iter = &mut accounts.iter();
1102
1103        let source_account_info = next_account_info(account_info_iter)?;
1104        let mint_info = next_account_info(account_info_iter)?;
1105        let authority_info = next_account_info(account_info_iter)?;
1106        let authority_info_data_len = authority_info.data_len();
1107
1108        let mut source_account_data = source_account_info.data.borrow_mut();
1109        let source_account =
1110            PodStateWithExtensionsMut::<PodAccount>::unpack(&mut source_account_data)?;
1111        let mut mint_data = mint_info.data.borrow_mut();
1112        let mint = PodStateWithExtensionsMut::<PodMint>::unpack(&mut mint_data)?;
1113
1114        if source_account.base.is_frozen() {
1115            return Err(TokenError::AccountFrozen.into());
1116        }
1117        if source_account.base.is_native() {
1118            return Err(TokenError::NativeNotSupported.into());
1119        }
1120        if u64::from(source_account.base.amount) < amount {
1121            return Err(TokenError::InsufficientFunds.into());
1122        }
1123        if mint_info.key != &source_account.base.mint {
1124            return Err(TokenError::MintMismatch.into());
1125        }
1126
1127        if let InstructionVariant::Checked { decimals } = instruction_variant {
1128            if decimals != mint.base.decimals {
1129                return Err(TokenError::MintDecimalsMismatch.into());
1130            }
1131        }
1132        if let Ok(extension) = mint.get_extension::<PausableConfig>() {
1133            if extension.paused.into() {
1134                return Err(TokenError::MintPaused.into());
1135            }
1136        }
1137        let maybe_permanent_delegate = get_permanent_delegate(&mint);
1138
1139        if let Ok(cpi_guard) = source_account.get_extension::<CpiGuard>() {
1140            // Blocks all cases where the authority has signed if CPI Guard is
1141            // enabled, including:
1142            // * the account is delegated to the owner
1143            // * the account owner is the permanent delegate
1144            if *authority_info.key == source_account.base.owner
1145                && cpi_guard.lock_cpi.into()
1146                && in_cpi()
1147            {
1148                return Err(TokenError::CpiGuardBurnBlocked.into());
1149            }
1150        }
1151
1152        if !source_account
1153            .base
1154            .is_owned_by_system_program_or_incinerator()
1155        {
1156            match (&source_account.base.delegate, maybe_permanent_delegate) {
1157                (_, Some(ref delegate)) if authority_info.key == delegate => Self::validate_owner(
1158                    program_id,
1159                    delegate,
1160                    authority_info,
1161                    authority_info_data_len,
1162                    account_info_iter.as_slice(),
1163                )?,
1164                (
1165                    PodCOption {
1166                        option: PodCOption::<Pubkey>::SOME,
1167                        value: delegate,
1168                    },
1169                    _,
1170                ) if authority_info.key == delegate => {
1171                    Self::validate_owner(
1172                        program_id,
1173                        delegate,
1174                        authority_info,
1175                        authority_info_data_len,
1176                        account_info_iter.as_slice(),
1177                    )?;
1178
1179                    if u64::from(source_account.base.delegated_amount) < amount {
1180                        return Err(TokenError::InsufficientFunds.into());
1181                    }
1182                    source_account.base.delegated_amount =
1183                        u64::from(source_account.base.delegated_amount)
1184                            .checked_sub(amount)
1185                            .ok_or(TokenError::Overflow)?
1186                            .into();
1187                    if u64::from(source_account.base.delegated_amount) == 0 {
1188                        source_account.base.delegate = PodCOption::none();
1189                    }
1190                }
1191                _ => {
1192                    Self::validate_owner(
1193                        program_id,
1194                        &source_account.base.owner,
1195                        authority_info,
1196                        authority_info_data_len,
1197                        account_info_iter.as_slice(),
1198                    )?;
1199                }
1200            }
1201        }
1202
1203        // Revisit this later to see if it's worth adding a check to reduce
1204        // compute costs, ie:
1205        // if amount == 0
1206        check_program_account(source_account_info.owner)?;
1207        check_program_account(mint_info.owner)?;
1208
1209        source_account.base.amount = u64::from(source_account.base.amount)
1210            .checked_sub(amount)
1211            .ok_or(TokenError::Overflow)?
1212            .into();
1213        mint.base.supply = u64::from(mint.base.supply)
1214            .checked_sub(amount)
1215            .ok_or(TokenError::Overflow)?
1216            .into();
1217
1218        Ok(())
1219    }
1220
1221    /// Processes a [`CloseAccount`](enum.TokenInstruction.html) instruction.
1222    pub fn process_close_account(
1223        program_id: &Pubkey,
1224        accounts: &[AccountInfo<'_>],
1225    ) -> ProgramResult {
1226        let account_info_iter = &mut accounts.iter();
1227        let source_account_info = next_account_info(account_info_iter)?;
1228        let destination_account_info = next_account_info(account_info_iter)?;
1229        let authority_info = next_account_info(account_info_iter)?;
1230        let authority_info_data_len = authority_info.data_len();
1231
1232        if source_account_info.key == destination_account_info.key {
1233            return Err(ProgramError::InvalidAccountData);
1234        }
1235
1236        let source_account_data = source_account_info.data.borrow();
1237
1238        // Branch 1: regular token account
1239        if let Ok(source_account) =
1240            PodStateWithExtensions::<PodAccount>::unpack(&source_account_data)
1241        {
1242            if !source_account.base.is_native() && u64::from(source_account.base.amount) != 0 {
1243                return Err(TokenError::NonNativeHasBalance.into());
1244            }
1245
1246            let authority = source_account
1247                .base
1248                .close_authority
1249                .unwrap_or(source_account.base.owner);
1250
1251            if !source_account
1252                .base
1253                .is_owned_by_system_program_or_incinerator()
1254            {
1255                if let Ok(cpi_guard) = source_account.get_extension::<CpiGuard>() {
1256                    if cpi_guard.lock_cpi.into()
1257                        && in_cpi()
1258                        && destination_account_info.key != &source_account.base.owner
1259                    {
1260                        return Err(TokenError::CpiGuardCloseAccountBlocked.into());
1261                    }
1262                }
1263
1264                Self::validate_owner(
1265                    program_id,
1266                    &authority,
1267                    authority_info,
1268                    authority_info_data_len,
1269                    account_info_iter.as_slice(),
1270                )?;
1271            } else if !rialo_s_sdk_ids::incinerator::check_id(destination_account_info.key) {
1272                return Err(ProgramError::InvalidAccountData);
1273            }
1274
1275            if let Ok(confidential_transfer_state) =
1276                source_account.get_extension::<ConfidentialTransferAccount>()
1277            {
1278                confidential_transfer_state.closable()?
1279            }
1280
1281            if let Ok(confidential_transfer_fee_state) =
1282                source_account.get_extension::<ConfidentialTransferFeeAmount>()
1283            {
1284                confidential_transfer_fee_state.closable()?
1285            }
1286
1287            if let Ok(transfer_fee_state) = source_account.get_extension::<TransferFeeAmount>() {
1288                transfer_fee_state.closable()?
1289            }
1290        }
1291        // Branch 2: mint with MintCloseAuthority -> create tombstone and close
1292        else if let Ok(mint) = PodStateWithExtensions::<PodMint>::unpack(&source_account_data) {
1293            let extension = mint.get_extension::<MintCloseAuthority>()?;
1294            let maybe_authority: Option<Pubkey> = extension.close_authority.into();
1295            let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
1296            Self::validate_owner(
1297                program_id,
1298                &authority,
1299                authority_info,
1300                authority_info_data_len,
1301                account_info_iter.as_slice(),
1302            )?;
1303
1304            if u64::from(mint.base.supply) != 0 {
1305                return Err(TokenError::MintHasSupply.into());
1306            }
1307
1308            // At this point, CloseAccount is confirmed authorized for a mint with zero supply.
1309            // For a mint close with tombstone, we expect extra accounts at the end:
1310            // [..., tombstone_pda, payer, system_program]
1311            if accounts.len() < 6 {
1312                return Err(ProgramError::NotEnoughAccountKeys);
1313            }
1314
1315            let tombstone_index = accounts.len() - 3;
1316            let payer_index = accounts.len() - 2;
1317            let system_index = accounts.len() - 1;
1318
1319            let tombstone_account_info = &accounts[tombstone_index];
1320            let payer_account_info = &accounts[payer_index];
1321            let system_program_info = &accounts[system_index];
1322
1323            // Verify tombstone PDA address
1324            let (expected_pda, bump) = derive_tombstone_pda(program_id, source_account_info.key);
1325            if tombstone_account_info.key != &expected_pda {
1326                return Err(TokenError::TombstoneAddressMismatch.into());
1327            }
1328
1329            // If kelvins already present, tombstone exists -> do not recreate
1330            if tombstone_account_info.kelvins() > 0 {
1331                return Err(TokenError::TombstoneAlreadyExists.into());
1332            }
1333
1334            // Create tombstone account (payer funds it)
1335            let rent = Rent::get()?;
1336            let kelvins_needed = rent.minimum_balance(TOMBSTONE_LEN);
1337
1338            let create_ix = system_instruction::create_account(
1339                payer_account_info.key,
1340                tombstone_account_info.key,
1341                kelvins_needed,
1342                TOMBSTONE_LEN as u64,
1343                program_id,
1344            );
1345
1346            let signer_seeds: &[&[u8]] =
1347                &[TOMBSTONE_SEED, source_account_info.key.as_ref(), &[bump]];
1348
1349            invoke_signed(
1350                &create_ix,
1351                &[
1352                    payer_account_info.clone(),
1353                    tombstone_account_info.clone(),
1354                    system_program_info.clone(),
1355                ],
1356                &[signer_seeds],
1357            )?;
1358
1359            // Write tombstone data (minimal variant: retired marker)
1360            let tomb = MintTombstone {
1361                closed_at_slot: Clock::get()?.slot,
1362                closed_by: *authority_info.key,
1363                successor: COption::None,
1364                policy_hash16: [0u8; 16],
1365            };
1366
1367            {
1368                let mut data = tombstone_account_info.data.borrow_mut();
1369                MintTombstone::pack_into_slice(&tomb, &mut data[..MintTombstone::LEN]);
1370            }
1371        }
1372        // Not an account, not a mint
1373        else {
1374            return Err(ProgramError::UninitializedAccount);
1375        }
1376
1377        let destination_starting_kelvins = destination_account_info.kelvins();
1378        **destination_account_info.kelvins.borrow_mut() = destination_starting_kelvins
1379            .checked_add(source_account_info.kelvins())
1380            .ok_or(TokenError::Overflow)?;
1381
1382        **source_account_info.kelvins.borrow_mut() = 0;
1383        drop(source_account_data);
1384        delete_account(source_account_info)?;
1385
1386        Ok(())
1387    }
1388
1389    /// Processes a [`FreezeAccount`](enum.TokenInstruction.html) or a
1390    /// [`ThawAccount`](enum.TokenInstruction.html) instruction.
1391    pub fn process_toggle_freeze_account(
1392        program_id: &Pubkey,
1393        accounts: &[AccountInfo<'_>],
1394        freeze: bool,
1395    ) -> ProgramResult {
1396        let account_info_iter = &mut accounts.iter();
1397        let source_account_info = next_account_info(account_info_iter)?;
1398        let mint_info = next_account_info(account_info_iter)?;
1399        let authority_info = next_account_info(account_info_iter)?;
1400        let authority_info_data_len = authority_info.data_len();
1401
1402        let mut source_account_data = source_account_info.data.borrow_mut();
1403        let source_account =
1404            PodStateWithExtensionsMut::<PodAccount>::unpack(&mut source_account_data)?;
1405        if freeze && source_account.base.is_frozen() || !freeze && !source_account.base.is_frozen()
1406        {
1407            return Err(TokenError::InvalidState.into());
1408        }
1409        if source_account.base.is_native() {
1410            return Err(TokenError::NativeNotSupported.into());
1411        }
1412        if mint_info.key != &source_account.base.mint {
1413            return Err(TokenError::MintMismatch.into());
1414        }
1415
1416        let mint_data = mint_info.data.borrow();
1417        let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data)?;
1418        match &mint.base.freeze_authority {
1419            PodCOption {
1420                option: PodCOption::<Pubkey>::SOME,
1421                value: authority,
1422            } => Self::validate_owner(
1423                program_id,
1424                authority,
1425                authority_info,
1426                authority_info_data_len,
1427                account_info_iter.as_slice(),
1428            ),
1429            _ => Err(TokenError::MintCannotFreeze.into()),
1430        }?;
1431
1432        source_account.base.state = if freeze {
1433            AccountState::Frozen.into()
1434        } else {
1435            AccountState::Initialized.into()
1436        };
1437
1438        Ok(())
1439    }
1440
1441    /// Processes a [`SyncNative`](enum.TokenInstruction.html) instruction
1442    pub fn process_sync_native(accounts: &[AccountInfo<'_>]) -> ProgramResult {
1443        let account_info_iter = &mut accounts.iter();
1444        let native_account_info = next_account_info(account_info_iter)?;
1445
1446        check_program_account(native_account_info.owner)?;
1447        let mut native_account_data = native_account_info.data.borrow_mut();
1448        let native_account =
1449            PodStateWithExtensionsMut::<PodAccount>::unpack(&mut native_account_data)?;
1450
1451        match native_account.base.is_native {
1452            PodCOption {
1453                option: PodCOption::<PodU64>::SOME,
1454                value: amount,
1455            } => {
1456                let new_amount = native_account_info
1457                    .kelvins()
1458                    .checked_sub(u64::from(amount))
1459                    .ok_or(TokenError::Overflow)?;
1460                if new_amount < u64::from(native_account.base.amount) {
1461                    return Err(TokenError::InvalidState.into());
1462                }
1463                native_account.base.amount = new_amount.into();
1464            }
1465            _ => return Err(TokenError::NonNativeNotSupported.into()),
1466        }
1467
1468        Ok(())
1469    }
1470
1471    /// Processes an
1472    /// [`InitializeMintCloseAuthority`](enum.TokenInstruction.html)
1473    /// instruction
1474    pub fn process_initialize_mint_close_authority(
1475        accounts: &[AccountInfo<'_>],
1476        close_authority: PodCOption<Pubkey>,
1477    ) -> ProgramResult {
1478        let account_info_iter = &mut accounts.iter();
1479        let mint_account_info = next_account_info(account_info_iter)?;
1480
1481        let mut mint_data = mint_account_info.data.borrow_mut();
1482        let mut mint = PodStateWithExtensionsMut::<PodMint>::unpack_uninitialized(&mut mint_data)?;
1483        let extension = mint.init_extension::<MintCloseAuthority>(true)?;
1484        extension.close_authority = close_authority.try_into()?;
1485
1486        Ok(())
1487    }
1488
1489    /// Processes a [`GetAccountDataSize`](enum.TokenInstruction.html)
1490    /// instruction
1491    pub fn process_get_account_data_size(
1492        accounts: &[AccountInfo<'_>],
1493        new_extension_types: &[ExtensionType],
1494    ) -> ProgramResult {
1495        if new_extension_types
1496            .iter()
1497            .any(|&t| t.get_account_type() != AccountType::Account)
1498        {
1499            return Err(TokenError::ExtensionTypeMismatch.into());
1500        }
1501
1502        let account_info_iter = &mut accounts.iter();
1503        let mint_account_info = next_account_info(account_info_iter)?;
1504
1505        let mut account_extensions = Self::get_required_account_extensions(mint_account_info)?;
1506        // ExtensionType::try_calculate_account_len() dedupes types, so just a dumb
1507        // concatenation is fine here
1508        account_extensions.extend_from_slice(new_extension_types);
1509
1510        let account_len = ExtensionType::try_calculate_account_len::<Account>(&account_extensions)?;
1511        set_return_data(&account_len.to_le_bytes());
1512
1513        Ok(())
1514    }
1515
1516    /// Processes an [`InitializeImmutableOwner`](enum.TokenInstruction.html)
1517    /// instruction
1518    pub fn process_initialize_immutable_owner(accounts: &[AccountInfo<'_>]) -> ProgramResult {
1519        let account_info_iter = &mut accounts.iter();
1520        let token_account_info = next_account_info(account_info_iter)?;
1521        let token_account_data = &mut token_account_info.data.borrow_mut();
1522        let mut token_account =
1523            PodStateWithExtensionsMut::<PodAccount>::unpack_uninitialized(token_account_data)?;
1524        token_account
1525            .init_extension::<ImmutableOwner>(true)
1526            .map(|_| ())
1527    }
1528
1529    /// Processes an [`AmountToUiAmount`](enum.TokenInstruction.html)
1530    /// instruction
1531    pub fn process_amount_to_ui_amount(accounts: &[AccountInfo<'_>], amount: u64) -> ProgramResult {
1532        let account_info_iter = &mut accounts.iter();
1533        let mint_info = next_account_info(account_info_iter)?;
1534        check_program_account(mint_info.owner)?;
1535
1536        let mint_data = mint_info.data.borrow();
1537        let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data)
1538            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
1539        let ui_amount = if let Ok(extension) = mint.get_extension::<InterestBearingConfig>() {
1540            let unix_timestamp = Clock::get()?.unix_timestamp;
1541            extension
1542                .amount_to_ui_amount(amount, mint.base.decimals, unix_timestamp)
1543                .ok_or(ProgramError::InvalidArgument)?
1544        } else if let Ok(extension) = mint.get_extension::<ScaledUiAmountConfig>() {
1545            let unix_timestamp = Clock::get()?.unix_timestamp;
1546            extension
1547                .amount_to_ui_amount(amount, mint.base.decimals, unix_timestamp)
1548                .ok_or(ProgramError::InvalidArgument)?
1549        } else {
1550            crate::amount_to_ui_amount_string_trimmed(amount, mint.base.decimals)
1551        };
1552
1553        set_return_data(&ui_amount.into_bytes());
1554        Ok(())
1555    }
1556
1557    /// Processes an [`UiAmountToAmount`](enum.TokenInstruction.html)
1558    /// instruction
1559    pub fn process_ui_amount_to_amount(
1560        accounts: &[AccountInfo<'_>],
1561        ui_amount: &str,
1562    ) -> ProgramResult {
1563        let account_info_iter = &mut accounts.iter();
1564        let mint_info = next_account_info(account_info_iter)?;
1565        check_program_account(mint_info.owner)?;
1566
1567        let mint_data = mint_info.data.borrow();
1568        let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data)
1569            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
1570        let amount = if let Ok(extension) = mint.get_extension::<InterestBearingConfig>() {
1571            let unix_timestamp = Clock::get()?.unix_timestamp;
1572            extension.try_ui_amount_into_amount(ui_amount, mint.base.decimals, unix_timestamp)?
1573        } else if let Ok(extension) = mint.get_extension::<ScaledUiAmountConfig>() {
1574            let unix_timestamp = Clock::get()?.unix_timestamp;
1575            extension.try_ui_amount_into_amount(ui_amount, mint.base.decimals, unix_timestamp)?
1576        } else {
1577            crate::try_ui_amount_into_amount(ui_amount.to_string(), mint.base.decimals)?
1578        };
1579
1580        set_return_data(&amount.to_le_bytes());
1581        Ok(())
1582    }
1583
1584    /// Processes a [`CreateNativeMint`](enum.TokenInstruction.html) instruction
1585    pub fn process_create_native_mint(accounts: &[AccountInfo<'_>]) -> ProgramResult {
1586        let account_info_iter = &mut accounts.iter();
1587        let payer_info = next_account_info(account_info_iter)?;
1588        let native_mint_info = next_account_info(account_info_iter)?;
1589        let system_program_info = next_account_info(account_info_iter)?;
1590
1591        if *native_mint_info.key != native_mint::id() {
1592            return Err(TokenError::InvalidMint.into());
1593        }
1594
1595        let rent = Rent::get()?;
1596        let new_minimum_balance = rent.minimum_balance(Mint::get_packed_len());
1597        let kelvins_diff = new_minimum_balance.saturating_sub(native_mint_info.kelvins());
1598        invoke(
1599            &system_instruction::transfer(payer_info.key, native_mint_info.key, kelvins_diff),
1600            &[
1601                payer_info.clone(),
1602                native_mint_info.clone(),
1603                system_program_info.clone(),
1604            ],
1605        )?;
1606
1607        invoke_signed(
1608            &system_instruction::allocate(native_mint_info.key, Mint::get_packed_len() as u64),
1609            &[native_mint_info.clone(), system_program_info.clone()],
1610            &[native_mint::PROGRAM_ADDRESS_SEEDS],
1611        )?;
1612
1613        invoke_signed(
1614            &system_instruction::assign(native_mint_info.key, &crate::id()),
1615            &[native_mint_info.clone(), system_program_info.clone()],
1616            &[native_mint::PROGRAM_ADDRESS_SEEDS],
1617        )?;
1618
1619        Mint::pack(
1620            Mint {
1621                decimals: native_mint::DECIMALS,
1622                is_initialized: true,
1623                ..Mint::default()
1624            },
1625            &mut native_mint_info.data.borrow_mut(),
1626        )
1627    }
1628
1629    /// Processes an
1630    /// [`InitializeNonTransferableMint`](enum.TokenInstruction.html)
1631    /// instruction
1632    pub fn process_initialize_non_transferable_mint(accounts: &[AccountInfo<'_>]) -> ProgramResult {
1633        let account_info_iter = &mut accounts.iter();
1634        let mint_account_info = next_account_info(account_info_iter)?;
1635
1636        let mut mint_data = mint_account_info.data.borrow_mut();
1637        let mut mint = PodStateWithExtensionsMut::<PodMint>::unpack_uninitialized(&mut mint_data)?;
1638        mint.init_extension::<NonTransferable>(true)?;
1639
1640        Ok(())
1641    }
1642
1643    /// Processes an [`InitializePermanentDelegate`](enum.TokenInstruction.html)
1644    /// instruction
1645    pub fn process_initialize_permanent_delegate(
1646        accounts: &[AccountInfo<'_>],
1647        delegate: &Pubkey,
1648    ) -> ProgramResult {
1649        let account_info_iter = &mut accounts.iter();
1650        let mint_account_info = next_account_info(account_info_iter)?;
1651
1652        let mut mint_data = mint_account_info.data.borrow_mut();
1653        let mut mint = PodStateWithExtensionsMut::<PodMint>::unpack_uninitialized(&mut mint_data)?;
1654        let extension = mint.init_extension::<PermanentDelegate>(true)?;
1655        extension.delegate = Some(*delegate).try_into()?;
1656
1657        Ok(())
1658    }
1659
1660    /// Withdraw Excess Kelvins is used to recover Kelvins transferred to any
1661    /// `TokenProgram` owned account by moving them to another account
1662    /// of the source account.
1663    pub fn process_withdraw_excess_kelvins(
1664        program_id: &Pubkey,
1665        accounts: &[AccountInfo<'_>],
1666    ) -> ProgramResult {
1667        let account_info_iter = &mut accounts.iter();
1668
1669        let source_info = next_account_info(account_info_iter)?;
1670        let destination_info = next_account_info(account_info_iter)?;
1671        let authority_info = next_account_info(account_info_iter)?;
1672
1673        let source_data = source_info.data.borrow();
1674
1675        if let Ok(account) = PodStateWithExtensions::<PodAccount>::unpack(&source_data) {
1676            if account.base.is_native() {
1677                return Err(TokenError::NativeNotSupported.into());
1678            }
1679            Self::validate_owner(
1680                program_id,
1681                &account.base.owner,
1682                authority_info,
1683                authority_info.data_len(),
1684                account_info_iter.as_slice(),
1685            )?;
1686        } else if let Ok(mint) = PodStateWithExtensions::<PodMint>::unpack(&source_data) {
1687            match &mint.base.mint_authority {
1688                PodCOption {
1689                    option: PodCOption::<Pubkey>::SOME,
1690                    value: mint_authority,
1691                } => {
1692                    Self::validate_owner(
1693                        program_id,
1694                        mint_authority,
1695                        authority_info,
1696                        authority_info.data_len(),
1697                        account_info_iter.as_slice(),
1698                    )?;
1699                }
1700                PodCOption {
1701                    option: PodCOption::<Pubkey>::NONE,
1702                    value: _,
1703                } if source_info.key == authority_info.key => {
1704                    // This is a special case where there is no mint authority set but the mint
1705                    // account is the same as the authority account and, therefore, needs to be
1706                    // a signer.
1707                    if !authority_info.is_signer {
1708                        return Err(ProgramError::MissingRequiredSignature);
1709                    }
1710                }
1711                _ => return Err(TokenError::AuthorityTypeNotSupported.into()),
1712            }
1713        } else if source_data.len() == PodMultisig::SIZE_OF {
1714            Self::validate_owner(
1715                program_id,
1716                source_info.key,
1717                authority_info,
1718                authority_info.data_len(),
1719                account_info_iter.as_slice(),
1720            )?;
1721        } else {
1722            return Err(TokenError::InvalidState.into());
1723        }
1724
1725        let source_rent_exempt_reserve = Rent::get()?.minimum_balance(source_info.data_len());
1726
1727        let transfer_amount = source_info
1728            .kelvins()
1729            .checked_sub(source_rent_exempt_reserve)
1730            .ok_or(TokenError::NotRentExempt)?;
1731
1732        let source_starting_kelvins = source_info.kelvins();
1733        **source_info.kelvins.borrow_mut() = source_starting_kelvins
1734            .checked_sub(transfer_amount)
1735            .ok_or(TokenError::Overflow)?;
1736
1737        let destination_starting_kelvins = destination_info.kelvins();
1738        **destination_info.kelvins.borrow_mut() = destination_starting_kelvins
1739            .checked_add(transfer_amount)
1740            .ok_or(TokenError::Overflow)?;
1741
1742        Ok(())
1743    }
1744
1745    /// Processes an [`Instruction`](enum.Instruction.html).
1746    pub fn process(
1747        program_id: &Pubkey,
1748        accounts: &[AccountInfo<'_>],
1749        input: &[u8],
1750    ) -> ProgramResult {
1751        if let Ok(instruction_type) = decode_instruction_type(input) {
1752            match instruction_type {
1753                PodTokenInstruction::InitializeMint => {
1754                    msg!("Instruction: InitializeMint");
1755                    let (data, freeze_authority) =
1756                        decode_instruction_data_with_coption_pubkey::<InitializeMintData>(input)?;
1757                    Self::process_initialize_mint(
1758                        accounts,
1759                        data.decimals,
1760                        &data.mint_authority,
1761                        freeze_authority,
1762                    )
1763                }
1764                PodTokenInstruction::InitializeMint2 => {
1765                    msg!("Instruction: InitializeMint2");
1766                    let (data, freeze_authority) =
1767                        decode_instruction_data_with_coption_pubkey::<InitializeMintData>(input)?;
1768                    Self::process_initialize_mint2(
1769                        accounts,
1770                        data.decimals,
1771                        &data.mint_authority,
1772                        freeze_authority,
1773                    )
1774                }
1775                PodTokenInstruction::InitializeAccount => {
1776                    msg!("Instruction: InitializeAccount");
1777                    Self::process_initialize_account(accounts)
1778                }
1779                PodTokenInstruction::InitializeAccount2 => {
1780                    msg!("Instruction: InitializeAccount2");
1781                    let owner = decode_instruction_data::<Pubkey>(input)?;
1782                    Self::process_initialize_account2(accounts, owner)
1783                }
1784                PodTokenInstruction::InitializeAccount3 => {
1785                    msg!("Instruction: InitializeAccount3");
1786                    let owner = decode_instruction_data::<Pubkey>(input)?;
1787                    Self::process_initialize_account3(accounts, owner)
1788                }
1789                PodTokenInstruction::InitializeMultisig => {
1790                    msg!("Instruction: InitializeMultisig");
1791                    let data = decode_instruction_data::<InitializeMultisigData>(input)?;
1792                    Self::process_initialize_multisig(accounts, data.m)
1793                }
1794                PodTokenInstruction::InitializeMultisig2 => {
1795                    msg!("Instruction: InitializeMultisig2");
1796                    let data = decode_instruction_data::<InitializeMultisigData>(input)?;
1797                    Self::process_initialize_multisig2(accounts, data.m)
1798                }
1799                #[allow(deprecated)]
1800                PodTokenInstruction::Transfer => {
1801                    msg!("Instruction: Transfer");
1802                    let data = decode_instruction_data::<AmountData>(input)?;
1803                    Self::process_transfer(
1804                        program_id,
1805                        accounts,
1806                        data.amount.into(),
1807                        TransferInstruction::Unchecked,
1808                    )
1809                }
1810                PodTokenInstruction::Approve => {
1811                    msg!("Instruction: Approve");
1812                    let data = decode_instruction_data::<AmountData>(input)?;
1813                    Self::process_approve(
1814                        program_id,
1815                        accounts,
1816                        data.amount.into(),
1817                        InstructionVariant::Unchecked,
1818                    )
1819                }
1820                PodTokenInstruction::Revoke => {
1821                    msg!("Instruction: Revoke");
1822                    Self::process_revoke(program_id, accounts)
1823                }
1824                PodTokenInstruction::SetAuthority => {
1825                    msg!("Instruction: SetAuthority");
1826                    let (data, new_authority) =
1827                        decode_instruction_data_with_coption_pubkey::<SetAuthorityData>(input)?;
1828                    Self::process_set_authority(
1829                        program_id,
1830                        accounts,
1831                        AuthorityType::from(data.authority_type)?,
1832                        new_authority,
1833                    )
1834                }
1835                PodTokenInstruction::MintTo => {
1836                    msg!("Instruction: MintTo");
1837                    let data = decode_instruction_data::<AmountData>(input)?;
1838                    Self::process_mint_to(
1839                        program_id,
1840                        accounts,
1841                        data.amount.into(),
1842                        InstructionVariant::Unchecked,
1843                    )
1844                }
1845                PodTokenInstruction::Burn => {
1846                    msg!("Instruction: Burn");
1847                    let data = decode_instruction_data::<AmountData>(input)?;
1848                    Self::process_burn(
1849                        program_id,
1850                        accounts,
1851                        data.amount.into(),
1852                        InstructionVariant::Unchecked,
1853                    )
1854                }
1855                PodTokenInstruction::CloseAccount => {
1856                    msg!("Instruction: CloseAccount");
1857                    Self::process_close_account(program_id, accounts)
1858                }
1859                PodTokenInstruction::FreezeAccount => {
1860                    msg!("Instruction: FreezeAccount");
1861                    Self::process_toggle_freeze_account(program_id, accounts, true)
1862                }
1863                PodTokenInstruction::ThawAccount => {
1864                    msg!("Instruction: ThawAccount");
1865                    Self::process_toggle_freeze_account(program_id, accounts, false)
1866                }
1867                PodTokenInstruction::TransferChecked => {
1868                    msg!("Instruction: TransferChecked");
1869                    let data = decode_instruction_data::<AmountCheckedData>(input)?;
1870                    Self::process_transfer(
1871                        program_id,
1872                        accounts,
1873                        data.amount.into(),
1874                        TransferInstruction::Checked {
1875                            decimals: data.decimals,
1876                        },
1877                    )
1878                }
1879                PodTokenInstruction::ApproveChecked => {
1880                    msg!("Instruction: ApproveChecked");
1881                    let data = decode_instruction_data::<AmountCheckedData>(input)?;
1882                    Self::process_approve(
1883                        program_id,
1884                        accounts,
1885                        data.amount.into(),
1886                        InstructionVariant::Checked {
1887                            decimals: data.decimals,
1888                        },
1889                    )
1890                }
1891                PodTokenInstruction::MintToChecked => {
1892                    msg!("Instruction: MintToChecked");
1893                    let data = decode_instruction_data::<AmountCheckedData>(input)?;
1894                    Self::process_mint_to(
1895                        program_id,
1896                        accounts,
1897                        data.amount.into(),
1898                        InstructionVariant::Checked {
1899                            decimals: data.decimals,
1900                        },
1901                    )
1902                }
1903                PodTokenInstruction::BurnChecked => {
1904                    msg!("Instruction: BurnChecked");
1905                    let data = decode_instruction_data::<AmountCheckedData>(input)?;
1906                    Self::process_burn(
1907                        program_id,
1908                        accounts,
1909                        data.amount.into(),
1910                        InstructionVariant::Checked {
1911                            decimals: data.decimals,
1912                        },
1913                    )
1914                }
1915                PodTokenInstruction::SyncNative => {
1916                    msg!("Instruction: SyncNative");
1917                    Self::process_sync_native(accounts)
1918                }
1919                PodTokenInstruction::GetAccountDataSize => {
1920                    msg!("Instruction: GetAccountDataSize");
1921                    let extension_types = input[1..]
1922                        .chunks(std::mem::size_of::<ExtensionType>())
1923                        .map(ExtensionType::try_from)
1924                        .collect::<Result<Vec<_>, _>>()?;
1925                    Self::process_get_account_data_size(accounts, &extension_types)
1926                }
1927                PodTokenInstruction::InitializeMintCloseAuthority => {
1928                    msg!("Instruction: InitializeMintCloseAuthority");
1929                    let (_, close_authority) =
1930                        decode_instruction_data_with_coption_pubkey::<()>(input)?;
1931                    Self::process_initialize_mint_close_authority(accounts, close_authority)
1932                }
1933                PodTokenInstruction::TransferFeeExtension => {
1934                    transfer_fee::processor::process_instruction(program_id, accounts, &input[1..])
1935                }
1936                PodTokenInstruction::ConfidentialTransferExtension => {
1937                    confidential_transfer::processor::process_instruction(
1938                        program_id,
1939                        accounts,
1940                        &input[1..],
1941                    )
1942                }
1943                PodTokenInstruction::DefaultAccountStateExtension => {
1944                    default_account_state::processor::process_instruction(
1945                        program_id,
1946                        accounts,
1947                        &input[1..],
1948                    )
1949                }
1950                PodTokenInstruction::InitializeImmutableOwner => {
1951                    msg!("Instruction: InitializeImmutableOwner");
1952                    Self::process_initialize_immutable_owner(accounts)
1953                }
1954                PodTokenInstruction::AmountToUiAmount => {
1955                    msg!("Instruction: AmountToUiAmount");
1956                    let data = decode_instruction_data::<AmountData>(input)?;
1957                    Self::process_amount_to_ui_amount(accounts, data.amount.into())
1958                }
1959                PodTokenInstruction::UiAmountToAmount => {
1960                    msg!("Instruction: UiAmountToAmount");
1961                    let ui_amount = std::str::from_utf8(&input[1..])
1962                        .map_err(|_| TokenError::InvalidInstruction)?;
1963                    Self::process_ui_amount_to_amount(accounts, ui_amount)
1964                }
1965                PodTokenInstruction::Reallocate => {
1966                    msg!("Instruction: Reallocate");
1967                    let extension_types = input[1..]
1968                        .chunks(std::mem::size_of::<ExtensionType>())
1969                        .map(ExtensionType::try_from)
1970                        .collect::<Result<Vec<_>, _>>()?;
1971                    reallocate::process_reallocate(program_id, accounts, extension_types)
1972                }
1973                PodTokenInstruction::MemoTransferExtension => {
1974                    memo_transfer::processor::process_instruction(program_id, accounts, &input[1..])
1975                }
1976                PodTokenInstruction::CreateNativeMint => {
1977                    msg!("Instruction: CreateNativeMint");
1978                    Self::process_create_native_mint(accounts)
1979                }
1980                PodTokenInstruction::InitializeNonTransferableMint => {
1981                    msg!("Instruction: InitializeNonTransferableMint");
1982                    Self::process_initialize_non_transferable_mint(accounts)
1983                }
1984                PodTokenInstruction::InterestBearingMintExtension => {
1985                    interest_bearing_mint::processor::process_instruction(
1986                        program_id,
1987                        accounts,
1988                        &input[1..],
1989                    )
1990                }
1991                PodTokenInstruction::CpiGuardExtension => {
1992                    cpi_guard::processor::process_instruction(program_id, accounts, &input[1..])
1993                }
1994                PodTokenInstruction::InitializePermanentDelegate => {
1995                    msg!("Instruction: InitializePermanentDelegate");
1996                    let delegate = decode_instruction_data::<Pubkey>(input)?;
1997                    Self::process_initialize_permanent_delegate(accounts, delegate)
1998                }
1999                PodTokenInstruction::TransferHookExtension => {
2000                    transfer_hook::processor::process_instruction(program_id, accounts, &input[1..])
2001                }
2002                PodTokenInstruction::ConfidentialTransferFeeExtension => {
2003                    confidential_transfer_fee::processor::process_instruction(
2004                        program_id,
2005                        accounts,
2006                        &input[1..],
2007                    )
2008                }
2009                PodTokenInstruction::WithdrawExcessKelvins => {
2010                    msg!("Instruction: WithdrawExcessKelvins");
2011                    Self::process_withdraw_excess_kelvins(program_id, accounts)
2012                }
2013                PodTokenInstruction::MetadataPointerExtension => {
2014                    metadata_pointer::processor::process_instruction(
2015                        program_id,
2016                        accounts,
2017                        &input[1..],
2018                    )
2019                }
2020                PodTokenInstruction::GroupPointerExtension => {
2021                    group_pointer::processor::process_instruction(program_id, accounts, &input[1..])
2022                }
2023                PodTokenInstruction::GroupMemberPointerExtension => {
2024                    group_member_pointer::processor::process_instruction(
2025                        program_id,
2026                        accounts,
2027                        &input[1..],
2028                    )
2029                }
2030                PodTokenInstruction::ConfidentialMintBurnExtension => {
2031                    msg!("Instruction: ConfidentialMintBurnExtension");
2032                    confidential_mint_burn::processor::process_instruction(
2033                        program_id,
2034                        accounts,
2035                        &input[1..],
2036                    )
2037                }
2038                PodTokenInstruction::ScaledUiAmountExtension => {
2039                    msg!("Instruction: ScaledUiAmountExtension");
2040                    scaled_ui_amount::processor::process_instruction(
2041                        program_id,
2042                        accounts,
2043                        &input[1..],
2044                    )
2045                }
2046                PodTokenInstruction::PausableExtension => {
2047                    msg!("Instruction: PausableExtension");
2048                    pausable::processor::process_instruction(program_id, accounts, &input[1..])
2049                }
2050            }
2051        } else if let Ok(instruction) = TokenMetadataInstruction::unpack(input) {
2052            token_metadata::processor::process_instruction(program_id, accounts, instruction)
2053        } else if let Ok(instruction) = TokenGroupInstruction::unpack(input) {
2054            token_group::processor::process_instruction(program_id, accounts, instruction)
2055        } else {
2056            Err(TokenError::InvalidInstruction.into())
2057        }
2058    }
2059
2060    /// Validates owner(s) are present. Used for Mints and Accounts only.
2061    pub fn validate_owner(
2062        program_id: &Pubkey,
2063        expected_owner: &Pubkey,
2064        owner_account_info: &AccountInfo<'_>,
2065        owner_account_data_len: usize,
2066        signers: &[AccountInfo<'_>],
2067    ) -> ProgramResult {
2068        if expected_owner != owner_account_info.key {
2069            return Err(TokenError::OwnerMismatch.into());
2070        }
2071
2072        if program_id == owner_account_info.owner && owner_account_data_len == PodMultisig::SIZE_OF
2073        {
2074            let multisig_data = &owner_account_info.data.borrow();
2075            let multisig = pod_from_bytes::<PodMultisig>(multisig_data)?;
2076            let mut num_signers = 0;
2077            let mut matched = [false; MAX_SIGNERS];
2078            for signer in signers.iter() {
2079                for (position, key) in multisig.signers[0..multisig.n as usize].iter().enumerate() {
2080                    if key == signer.key && !matched[position] {
2081                        if !signer.is_signer {
2082                            return Err(ProgramError::MissingRequiredSignature);
2083                        }
2084                        matched[position] = true;
2085                        num_signers += 1;
2086                    }
2087                }
2088            }
2089            if num_signers < multisig.m {
2090                return Err(ProgramError::MissingRequiredSignature);
2091            }
2092            return Ok(());
2093        } else if !owner_account_info.is_signer {
2094            return Err(ProgramError::MissingRequiredSignature);
2095        }
2096        Ok(())
2097    }
2098
2099    fn get_required_account_extensions(
2100        mint_account_info: &AccountInfo<'_>,
2101    ) -> Result<Vec<ExtensionType>, ProgramError> {
2102        let mint_data = mint_account_info.data.borrow();
2103        let state = PodStateWithExtensions::<PodMint>::unpack(&mint_data)
2104            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
2105        Self::get_required_account_extensions_from_unpacked_mint(mint_account_info.owner, &state)
2106    }
2107
2108    fn get_required_account_extensions_from_unpacked_mint(
2109        token_program_id: &Pubkey,
2110        state: &PodStateWithExtensions<'_, PodMint>,
2111    ) -> Result<Vec<ExtensionType>, ProgramError> {
2112        check_program_account(token_program_id)?;
2113        let mint_extensions = state.get_extension_types()?;
2114        Ok(ExtensionType::get_required_init_account_extensions(
2115            &mint_extensions,
2116        ))
2117    }
2118}
2119
2120/// Helper function to mostly delete an account in a test environment.  We could
2121/// potentially muck around the bytes assuming that a vec is passed in, but that
2122/// would be more trouble than it's worth.
2123#[cfg(not(target_os = "solana"))]
2124fn delete_account(account_info: &AccountInfo<'_>) -> Result<(), ProgramError> {
2125    account_info.assign(&system_program::id());
2126    let mut account_data = account_info.data.borrow_mut();
2127    let data_len = account_data.len();
2128    rialo_s_program_memory::rlo_memset(*account_data, 0, data_len);
2129    Ok(())
2130}
2131
2132/// Helper function to totally delete an account on-chain
2133#[cfg(target_os = "solana")]
2134fn delete_account(account_info: &AccountInfo<'_>) -> Result<(), ProgramError> {
2135    account_info.assign(&system_program::id());
2136    account_info.realloc(0, true)
2137}
2138
2139#[cfg(test)]
2140mod tests {
2141    use std::sync::{Arc, RwLock};
2142
2143    use once_cell::sync::Lazy;
2144    use rialo_s_account::{
2145        create_account_for_test, create_is_signer_account_infos, Account as SolanaAccount,
2146    };
2147    use rialo_s_account_info::IntoAccountInfo;
2148    use rialo_s_clock::{Clock, Epoch};
2149    use rialo_s_instruction::Instruction;
2150    use rialo_s_program_error::PrintProgramError;
2151    use rialo_s_program_option::COption;
2152    use rialo_s_sdk_ids::sysvar::rent;
2153    use rialo_spl_token_2022_interface::{
2154        extension::transfer_fee::instruction::initialize_transfer_fee_config, instruction::*,
2155        state::Multisig,
2156    };
2157    use serial_test::serial;
2158
2159    use super::*;
2160
2161    static EXPECTED_DATA: Lazy<Arc<RwLock<Vec<u8>>>> =
2162        Lazy::new(|| Arc::new(RwLock::new(Vec::new())));
2163
2164    fn set_expected_data(expected_data: Vec<u8>) {
2165        *EXPECTED_DATA.write().unwrap() = expected_data;
2166    }
2167
2168    struct SyscallStubs {}
2169    impl rialo_s_sysvar::program_stubs::SyscallStubs for SyscallStubs {
2170        fn rlo_log(&self, _message: &str) {}
2171
2172        fn rlo_invoke_signed(
2173            &self,
2174            _instruction: &Instruction,
2175            _account_infos: &[AccountInfo<'_>],
2176            _signers_seeds: &[&[&[u8]]],
2177        ) -> ProgramResult {
2178            Err(ProgramError::Custom(42)) // Not supported
2179        }
2180
2181        #[allow(unsafe_code)]
2182        fn rlo_get_clock_sysvar(&self, var_addr: *mut u8) -> u64 {
2183            unsafe {
2184                *var_addr.cast::<Clock>() = Clock::default();
2185            }
2186            rialo_s_program_entrypoint::SUCCESS
2187        }
2188
2189        fn rlo_get_epoch_schedule_sysvar(&self, _var_addr: *mut u8) -> u64 {
2190            rialo_s_instruction::error::UNSUPPORTED_SYSVAR
2191        }
2192
2193        #[allow(deprecated)]
2194        fn rlo_get_fees_sysvar(&self, _var_addr: *mut u8) -> u64 {
2195            rialo_s_instruction::error::UNSUPPORTED_SYSVAR
2196        }
2197
2198        #[allow(unsafe_code)]
2199        fn rlo_get_rent_sysvar(&self, var_addr: *mut u8) -> u64 {
2200            unsafe {
2201                *var_addr.cast::<Rent>() = Rent::default();
2202            }
2203            rialo_s_program_entrypoint::SUCCESS
2204        }
2205
2206        fn rlo_set_return_data(&self, data: &[u8]) {
2207            assert_eq!(&*EXPECTED_DATA.read().unwrap(), data)
2208        }
2209    }
2210
2211    fn do_process_instruction(
2212        instruction: Instruction,
2213        accounts: Vec<&mut SolanaAccount>,
2214    ) -> ProgramResult {
2215        {
2216            use std::sync::Once;
2217            static ONCE: Once = Once::new();
2218
2219            ONCE.call_once(|| {
2220                rialo_s_sysvar::program_stubs::set_syscall_stubs(Box::new(SyscallStubs {}));
2221            });
2222        }
2223
2224        let mut meta = instruction
2225            .accounts
2226            .iter()
2227            .zip(accounts)
2228            .map(|(account_meta, account)| (&account_meta.pubkey, account_meta.is_signer, account))
2229            .collect::<Vec<_>>();
2230
2231        let account_infos = create_is_signer_account_infos(&mut meta);
2232        Processor::process(&instruction.program_id, &account_infos, &instruction.data)
2233    }
2234
2235    fn do_process_instruction_dups(
2236        instruction: Instruction,
2237        account_infos: Vec<AccountInfo<'_>>,
2238    ) -> ProgramResult {
2239        Processor::process(&instruction.program_id, &account_infos, &instruction.data)
2240    }
2241
2242    fn return_token_error_as_program_error() -> ProgramError {
2243        TokenError::MintMismatch.into()
2244    }
2245
2246    fn rent_sysvar() -> SolanaAccount {
2247        create_account_for_test(&Rent::default())
2248    }
2249
2250    fn mint_minimum_balance() -> u64 {
2251        Rent::default().minimum_balance(Mint::get_packed_len())
2252    }
2253
2254    fn account_minimum_balance() -> u64 {
2255        Rent::default().minimum_balance(Account::get_packed_len())
2256    }
2257
2258    fn multisig_minimum_balance() -> u64 {
2259        Rent::default().minimum_balance(Multisig::get_packed_len())
2260    }
2261
2262    fn native_mint() -> SolanaAccount {
2263        let mut rent_sysvar = rent_sysvar();
2264        let mut mint_account =
2265            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &crate::id());
2266        do_process_instruction(
2267            initialize_mint(
2268                &crate::id(),
2269                &crate::native_mint::id(),
2270                &Pubkey::default(),
2271                None,
2272                crate::native_mint::DECIMALS,
2273            )
2274            .unwrap(),
2275            vec![&mut mint_account, &mut rent_sysvar],
2276        )
2277        .unwrap();
2278        mint_account
2279    }
2280
2281    #[test]
2282    fn test_print_error() {
2283        let error = return_token_error_as_program_error();
2284        error.print::<TokenError>();
2285    }
2286
2287    #[test]
2288    fn test_error_as_custom() {
2289        assert_eq!(
2290            return_token_error_as_program_error(),
2291            ProgramError::Custom(3)
2292        );
2293    }
2294
2295    #[test]
2296    fn test_unique_account_sizes() {
2297        assert_ne!(Mint::get_packed_len(), 0);
2298        assert_ne!(Mint::get_packed_len(), Account::get_packed_len());
2299        assert_ne!(Mint::get_packed_len(), Multisig::get_packed_len());
2300        assert_ne!(Account::get_packed_len(), 0);
2301        assert_ne!(Account::get_packed_len(), Multisig::get_packed_len());
2302        assert_ne!(Multisig::get_packed_len(), 0);
2303    }
2304
2305    #[test]
2306    fn test_initialize_mint() {
2307        let program_id = crate::id();
2308        let owner_key = Pubkey::new_unique();
2309        let mint_key = Pubkey::new_unique();
2310        let mut mint_account = SolanaAccount::new(42, Mint::get_packed_len(), &program_id);
2311        let mint2_key = Pubkey::new_unique();
2312        let mut mint2_account =
2313            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
2314        let mut rent_sysvar = rent_sysvar();
2315
2316        // mint is not rent exempt
2317        assert_eq!(
2318            Err(TokenError::NotRentExempt.into()),
2319            do_process_instruction(
2320                initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
2321                vec![&mut mint_account, &mut rent_sysvar]
2322            )
2323        );
2324
2325        mint_account.kelvins = mint_minimum_balance();
2326
2327        // create new mint
2328        do_process_instruction(
2329            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
2330            vec![&mut mint_account, &mut rent_sysvar],
2331        )
2332        .unwrap();
2333
2334        // create twice
2335        assert_eq!(
2336            Err(TokenError::AlreadyInUse.into()),
2337            do_process_instruction(
2338                initialize_mint(&program_id, &mint_key, &owner_key, None, 2,).unwrap(),
2339                vec![&mut mint_account, &mut rent_sysvar]
2340            )
2341        );
2342
2343        // create another mint that can freeze
2344        do_process_instruction(
2345            initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(),
2346            vec![&mut mint2_account, &mut rent_sysvar],
2347        )
2348        .unwrap();
2349        let mint = Mint::unpack_unchecked(&mint2_account.data).unwrap();
2350        assert_eq!(mint.freeze_authority, COption::Some(owner_key));
2351    }
2352
2353    #[test]
2354    fn test_initialize_mint2() {
2355        let program_id = crate::id();
2356        let owner_key = Pubkey::new_unique();
2357        let mint_key = Pubkey::new_unique();
2358        let mut mint_account = SolanaAccount::new(42, Mint::get_packed_len(), &program_id);
2359        let mut mint_tombstone_account = SolanaAccount::new(0, 0, &program_id);
2360        let mint2_key = Pubkey::new_unique();
2361        let mut mint2_account =
2362            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
2363        let mut mint2_tombstone_account = SolanaAccount::new(0, 0, &program_id);
2364
2365        // mint is not rent exempt
2366        assert_eq!(
2367            Err(TokenError::NotRentExempt.into()),
2368            do_process_instruction(
2369                initialize_mint2(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
2370                vec![&mut mint_account, &mut mint_tombstone_account],
2371            )
2372        );
2373
2374        mint_account.kelvins = mint_minimum_balance();
2375
2376        // create new mint
2377        do_process_instruction(
2378            initialize_mint2(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
2379            vec![&mut mint_account, &mut mint_tombstone_account],
2380        )
2381        .unwrap();
2382
2383        // create twice
2384        assert_eq!(
2385            Err(TokenError::AlreadyInUse.into()),
2386            do_process_instruction(
2387                initialize_mint2(&program_id, &mint_key, &owner_key, None, 2,).unwrap(),
2388                vec![&mut mint_account, &mut mint_tombstone_account],
2389            )
2390        );
2391
2392        // create another mint that can freeze
2393        do_process_instruction(
2394            initialize_mint2(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(),
2395            vec![&mut mint2_account, &mut mint2_tombstone_account],
2396        )
2397        .unwrap();
2398        let mint = Mint::unpack_unchecked(&mint2_account.data).unwrap();
2399        assert_eq!(mint.freeze_authority, COption::Some(owner_key));
2400    }
2401
2402    #[test]
2403    fn test_initialize_mint_account() {
2404        let program_id = crate::id();
2405        let account_key = Pubkey::new_unique();
2406        let mut account_account = SolanaAccount::new(42, Account::get_packed_len(), &program_id);
2407        let owner_key = Pubkey::new_unique();
2408        let mut owner_account = SolanaAccount::default();
2409        let mint_key = Pubkey::new_unique();
2410        let mut mint_account =
2411            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
2412        let mut rent_sysvar = rent_sysvar();
2413
2414        // account is not rent exempt
2415        assert_eq!(
2416            Err(TokenError::NotRentExempt.into()),
2417            do_process_instruction(
2418                initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
2419                vec![
2420                    &mut account_account,
2421                    &mut mint_account,
2422                    &mut owner_account,
2423                    &mut rent_sysvar
2424                ],
2425            )
2426        );
2427
2428        account_account.kelvins = account_minimum_balance();
2429
2430        // mint is not valid (not initialized)
2431        assert_eq!(
2432            Err(TokenError::InvalidMint.into()),
2433            do_process_instruction(
2434                initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
2435                vec![
2436                    &mut account_account,
2437                    &mut mint_account,
2438                    &mut owner_account,
2439                    &mut rent_sysvar
2440                ],
2441            )
2442        );
2443
2444        // create mint
2445        do_process_instruction(
2446            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
2447            vec![&mut mint_account, &mut rent_sysvar],
2448        )
2449        .unwrap();
2450
2451        // mint not owned by program
2452        let not_program_id = Pubkey::new_unique();
2453        mint_account.owner = not_program_id;
2454        assert_eq!(
2455            Err(ProgramError::IncorrectProgramId),
2456            do_process_instruction(
2457                initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
2458                vec![
2459                    &mut account_account,
2460                    &mut mint_account,
2461                    &mut owner_account,
2462                    &mut rent_sysvar
2463                ],
2464            )
2465        );
2466        mint_account.owner = program_id;
2467
2468        // create account
2469        do_process_instruction(
2470            initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
2471            vec![
2472                &mut account_account,
2473                &mut mint_account,
2474                &mut owner_account,
2475                &mut rent_sysvar,
2476            ],
2477        )
2478        .unwrap();
2479
2480        // create twice
2481        assert_eq!(
2482            Err(TokenError::AlreadyInUse.into()),
2483            do_process_instruction(
2484                initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
2485                vec![
2486                    &mut account_account,
2487                    &mut mint_account,
2488                    &mut owner_account,
2489                    &mut rent_sysvar
2490                ],
2491            )
2492        );
2493    }
2494
2495    #[test]
2496    fn test_transfer_dups() {
2497        let program_id = crate::id();
2498        let account1_key = Pubkey::new_unique();
2499        let mut account1_account = SolanaAccount::new(
2500            account_minimum_balance(),
2501            Account::get_packed_len(),
2502            &program_id,
2503        );
2504        let mut account1_info: AccountInfo<'_> =
2505            (&account1_key, true, &mut account1_account).into();
2506        let account2_key = Pubkey::new_unique();
2507        let mut account2_account = SolanaAccount::new(
2508            account_minimum_balance(),
2509            Account::get_packed_len(),
2510            &program_id,
2511        );
2512        let mut account2_info: AccountInfo<'_> =
2513            (&account2_key, false, &mut account2_account).into();
2514        let account3_key = Pubkey::new_unique();
2515        let mut account3_account = SolanaAccount::new(
2516            account_minimum_balance(),
2517            Account::get_packed_len(),
2518            &program_id,
2519        );
2520        let account3_info: AccountInfo<'_> = (&account3_key, false, &mut account3_account).into();
2521        let account4_key = Pubkey::new_unique();
2522        let mut account4_account = SolanaAccount::new(
2523            account_minimum_balance(),
2524            Account::get_packed_len(),
2525            &program_id,
2526        );
2527        let account4_info: AccountInfo<'_> = (&account4_key, true, &mut account4_account).into();
2528        let multisig_key = Pubkey::new_unique();
2529        let mut multisig_account = SolanaAccount::new(
2530            multisig_minimum_balance(),
2531            Multisig::get_packed_len(),
2532            &program_id,
2533        );
2534        let multisig_info: AccountInfo<'_> = (&multisig_key, true, &mut multisig_account).into();
2535        let owner_key = Pubkey::new_unique();
2536        let mut owner_account = SolanaAccount::default();
2537        let owner_info: AccountInfo<'_> = (&owner_key, true, &mut owner_account).into();
2538        let mint_key = Pubkey::new_unique();
2539        let mut mint_account =
2540            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
2541        let mint_info: AccountInfo<'_> = (&mint_key, false, &mut mint_account).into();
2542        let rent_key = rent::id();
2543        let mut rent_sysvar = rent_sysvar();
2544        let rent_info: AccountInfo<'_> = (&rent_key, false, &mut rent_sysvar).into();
2545
2546        // create mint
2547        do_process_instruction_dups(
2548            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
2549            vec![mint_info.clone(), rent_info.clone()],
2550        )
2551        .unwrap();
2552
2553        // create account
2554        do_process_instruction_dups(
2555            initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(),
2556            vec![
2557                account1_info.clone(),
2558                mint_info.clone(),
2559                account1_info.clone(),
2560                rent_info.clone(),
2561            ],
2562        )
2563        .unwrap();
2564
2565        // create another account
2566        do_process_instruction_dups(
2567            initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(),
2568            vec![
2569                account2_info.clone(),
2570                mint_info.clone(),
2571                owner_info.clone(),
2572                rent_info.clone(),
2573            ],
2574        )
2575        .unwrap();
2576
2577        // mint to account
2578        do_process_instruction_dups(
2579            mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(),
2580            vec![mint_info.clone(), account1_info.clone(), owner_info.clone()],
2581        )
2582        .unwrap();
2583
2584        // source-owner transfer
2585        do_process_instruction_dups(
2586            #[allow(deprecated)]
2587            transfer(
2588                &program_id,
2589                &account1_key,
2590                &account2_key,
2591                &account1_key,
2592                &[],
2593                500,
2594            )
2595            .unwrap(),
2596            vec![
2597                account1_info.clone(),
2598                account2_info.clone(),
2599                account1_info.clone(),
2600            ],
2601        )
2602        .unwrap();
2603
2604        // source-owner TransferChecked
2605        do_process_instruction_dups(
2606            transfer_checked(
2607                &program_id,
2608                &account1_key,
2609                &mint_key,
2610                &account2_key,
2611                &account1_key,
2612                &[],
2613                500,
2614                2,
2615            )
2616            .unwrap(),
2617            vec![
2618                account1_info.clone(),
2619                mint_info.clone(),
2620                account2_info.clone(),
2621                account1_info.clone(),
2622            ],
2623        )
2624        .unwrap();
2625
2626        // source-delegate transfer
2627        let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap();
2628        account.amount = 1000;
2629        account.delegated_amount = 1000;
2630        account.delegate = COption::Some(account1_key);
2631        account.owner = owner_key;
2632        Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap();
2633
2634        do_process_instruction_dups(
2635            #[allow(deprecated)]
2636            transfer(
2637                &program_id,
2638                &account1_key,
2639                &account2_key,
2640                &account1_key,
2641                &[],
2642                500,
2643            )
2644            .unwrap(),
2645            vec![
2646                account1_info.clone(),
2647                account2_info.clone(),
2648                account1_info.clone(),
2649            ],
2650        )
2651        .unwrap();
2652
2653        // source-delegate TransferChecked
2654        do_process_instruction_dups(
2655            transfer_checked(
2656                &program_id,
2657                &account1_key,
2658                &mint_key,
2659                &account2_key,
2660                &account1_key,
2661                &[],
2662                500,
2663                2,
2664            )
2665            .unwrap(),
2666            vec![
2667                account1_info.clone(),
2668                mint_info.clone(),
2669                account2_info.clone(),
2670                account1_info.clone(),
2671            ],
2672        )
2673        .unwrap();
2674
2675        // test destination-owner transfer
2676        do_process_instruction_dups(
2677            initialize_account(&program_id, &account3_key, &mint_key, &account2_key).unwrap(),
2678            vec![
2679                account3_info.clone(),
2680                mint_info.clone(),
2681                account2_info.clone(),
2682                rent_info.clone(),
2683            ],
2684        )
2685        .unwrap();
2686        do_process_instruction_dups(
2687            mint_to(&program_id, &mint_key, &account3_key, &owner_key, &[], 1000).unwrap(),
2688            vec![mint_info.clone(), account3_info.clone(), owner_info.clone()],
2689        )
2690        .unwrap();
2691
2692        account1_info.is_signer = false;
2693        account2_info.is_signer = true;
2694        do_process_instruction_dups(
2695            #[allow(deprecated)]
2696            transfer(
2697                &program_id,
2698                &account3_key,
2699                &account2_key,
2700                &account2_key,
2701                &[],
2702                500,
2703            )
2704            .unwrap(),
2705            vec![
2706                account3_info.clone(),
2707                account2_info.clone(),
2708                account2_info.clone(),
2709            ],
2710        )
2711        .unwrap();
2712
2713        // destination-owner TransferChecked
2714        do_process_instruction_dups(
2715            transfer_checked(
2716                &program_id,
2717                &account3_key,
2718                &mint_key,
2719                &account2_key,
2720                &account2_key,
2721                &[],
2722                500,
2723                2,
2724            )
2725            .unwrap(),
2726            vec![
2727                account3_info.clone(),
2728                mint_info.clone(),
2729                account2_info.clone(),
2730                account2_info.clone(),
2731            ],
2732        )
2733        .unwrap();
2734
2735        // test source-multisig signer
2736        do_process_instruction_dups(
2737            initialize_multisig(&program_id, &multisig_key, &[&account4_key], 1).unwrap(),
2738            vec![
2739                multisig_info.clone(),
2740                rent_info.clone(),
2741                account4_info.clone(),
2742            ],
2743        )
2744        .unwrap();
2745
2746        do_process_instruction_dups(
2747            initialize_account(&program_id, &account4_key, &mint_key, &multisig_key).unwrap(),
2748            vec![
2749                account4_info.clone(),
2750                mint_info.clone(),
2751                multisig_info.clone(),
2752                rent_info.clone(),
2753            ],
2754        )
2755        .unwrap();
2756
2757        do_process_instruction_dups(
2758            mint_to(&program_id, &mint_key, &account4_key, &owner_key, &[], 1000).unwrap(),
2759            vec![mint_info.clone(), account4_info.clone(), owner_info.clone()],
2760        )
2761        .unwrap();
2762
2763        // source-multisig-signer transfer
2764        do_process_instruction_dups(
2765            #[allow(deprecated)]
2766            transfer(
2767                &program_id,
2768                &account4_key,
2769                &account2_key,
2770                &multisig_key,
2771                &[&account4_key],
2772                500,
2773            )
2774            .unwrap(),
2775            vec![
2776                account4_info.clone(),
2777                account2_info.clone(),
2778                multisig_info.clone(),
2779                account4_info.clone(),
2780            ],
2781        )
2782        .unwrap();
2783
2784        // source-multisig-signer TransferChecked
2785        do_process_instruction_dups(
2786            transfer_checked(
2787                &program_id,
2788                &account4_key,
2789                &mint_key,
2790                &account2_key,
2791                &multisig_key,
2792                &[&account4_key],
2793                500,
2794                2,
2795            )
2796            .unwrap(),
2797            vec![
2798                account4_info.clone(),
2799                mint_info.clone(),
2800                account2_info.clone(),
2801                multisig_info.clone(),
2802                account4_info.clone(),
2803            ],
2804        )
2805        .unwrap();
2806    }
2807
2808    #[test]
2809    fn test_transfer() {
2810        let program_id = crate::id();
2811        let account_key = Pubkey::new_unique();
2812        let mut account_account = SolanaAccount::new(
2813            account_minimum_balance(),
2814            Account::get_packed_len(),
2815            &program_id,
2816        );
2817        let account2_key = Pubkey::new_unique();
2818        let mut account2_account = SolanaAccount::new(
2819            account_minimum_balance(),
2820            Account::get_packed_len(),
2821            &program_id,
2822        );
2823        let account3_key = Pubkey::new_unique();
2824        let mut account3_account = SolanaAccount::new(
2825            account_minimum_balance(),
2826            Account::get_packed_len(),
2827            &program_id,
2828        );
2829        let delegate_key = Pubkey::new_unique();
2830        let mut delegate_account = SolanaAccount::default();
2831        let mismatch_key = Pubkey::new_unique();
2832        let mut mismatch_account = SolanaAccount::new(
2833            account_minimum_balance(),
2834            Account::get_packed_len(),
2835            &program_id,
2836        );
2837        let owner_key = Pubkey::new_unique();
2838        let mut owner_account = SolanaAccount::default();
2839        let owner2_key = Pubkey::new_unique();
2840        let mut owner2_account = SolanaAccount::default();
2841        let mint_key = Pubkey::new_unique();
2842        let mut mint_account =
2843            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
2844        let mint2_key = Pubkey::new_unique();
2845        let mut rent_sysvar = rent_sysvar();
2846
2847        // create mint
2848        do_process_instruction(
2849            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
2850            vec![&mut mint_account, &mut rent_sysvar],
2851        )
2852        .unwrap();
2853
2854        // create account
2855        do_process_instruction(
2856            initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
2857            vec![
2858                &mut account_account,
2859                &mut mint_account,
2860                &mut owner_account,
2861                &mut rent_sysvar,
2862            ],
2863        )
2864        .unwrap();
2865
2866        // create another account
2867        do_process_instruction(
2868            initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(),
2869            vec![
2870                &mut account2_account,
2871                &mut mint_account,
2872                &mut owner_account,
2873                &mut rent_sysvar,
2874            ],
2875        )
2876        .unwrap();
2877
2878        // create another account
2879        do_process_instruction(
2880            initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(),
2881            vec![
2882                &mut account3_account,
2883                &mut mint_account,
2884                &mut owner_account,
2885                &mut rent_sysvar,
2886            ],
2887        )
2888        .unwrap();
2889
2890        // create mismatch account
2891        do_process_instruction(
2892            initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(),
2893            vec![
2894                &mut mismatch_account,
2895                &mut mint_account,
2896                &mut owner_account,
2897                &mut rent_sysvar,
2898            ],
2899        )
2900        .unwrap();
2901        let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap();
2902        account.mint = mint2_key;
2903        Account::pack(account, &mut mismatch_account.data).unwrap();
2904
2905        // mint to account
2906        do_process_instruction(
2907            mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(),
2908            vec![&mut mint_account, &mut account_account, &mut owner_account],
2909        )
2910        .unwrap();
2911
2912        // missing signer
2913        #[allow(deprecated)]
2914        let mut instruction = transfer(
2915            &program_id,
2916            &account_key,
2917            &account2_key,
2918            &owner_key,
2919            &[],
2920            1000,
2921        )
2922        .unwrap();
2923        instruction.accounts[2].is_signer = false;
2924        assert_eq!(
2925            Err(ProgramError::MissingRequiredSignature),
2926            do_process_instruction(
2927                instruction,
2928                vec![
2929                    &mut account_account,
2930                    &mut account2_account,
2931                    &mut owner_account,
2932                ],
2933            )
2934        );
2935
2936        // mismatch mint
2937        assert_eq!(
2938            Err(TokenError::MintMismatch.into()),
2939            do_process_instruction(
2940                #[allow(deprecated)]
2941                transfer(
2942                    &program_id,
2943                    &account_key,
2944                    &mismatch_key,
2945                    &owner_key,
2946                    &[],
2947                    1000
2948                )
2949                .unwrap(),
2950                vec![
2951                    &mut account_account,
2952                    &mut mismatch_account,
2953                    &mut owner_account,
2954                ],
2955            )
2956        );
2957
2958        // missing owner
2959        assert_eq!(
2960            Err(TokenError::OwnerMismatch.into()),
2961            do_process_instruction(
2962                #[allow(deprecated)]
2963                transfer(
2964                    &program_id,
2965                    &account_key,
2966                    &account2_key,
2967                    &owner2_key,
2968                    &[],
2969                    1000
2970                )
2971                .unwrap(),
2972                vec![
2973                    &mut account_account,
2974                    &mut account2_account,
2975                    &mut owner2_account,
2976                ],
2977            )
2978        );
2979
2980        // account not owned by program
2981        let not_program_id = Pubkey::new_unique();
2982        account_account.owner = not_program_id;
2983        assert_eq!(
2984            Err(ProgramError::IncorrectProgramId),
2985            do_process_instruction(
2986                #[allow(deprecated)]
2987                transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 0,).unwrap(),
2988                vec![
2989                    &mut account_account,
2990                    &mut account2_account,
2991                    &mut owner2_account,
2992                ],
2993            )
2994        );
2995        account_account.owner = program_id;
2996
2997        // account 2 not owned by program
2998        let not_program_id = Pubkey::new_unique();
2999        account2_account.owner = not_program_id;
3000        assert_eq!(
3001            Err(ProgramError::IncorrectProgramId),
3002            do_process_instruction(
3003                #[allow(deprecated)]
3004                transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 0,).unwrap(),
3005                vec![
3006                    &mut account_account,
3007                    &mut account2_account,
3008                    &mut owner2_account,
3009                ],
3010            )
3011        );
3012        account2_account.owner = program_id;
3013
3014        // transfer
3015        do_process_instruction(
3016            #[allow(deprecated)]
3017            transfer(
3018                &program_id,
3019                &account_key,
3020                &account2_key,
3021                &owner_key,
3022                &[],
3023                1000,
3024            )
3025            .unwrap(),
3026            vec![
3027                &mut account_account,
3028                &mut account2_account,
3029                &mut owner_account,
3030            ],
3031        )
3032        .unwrap();
3033
3034        // insufficient funds
3035        assert_eq!(
3036            Err(TokenError::InsufficientFunds.into()),
3037            do_process_instruction(
3038                #[allow(deprecated)]
3039                transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 1).unwrap(),
3040                vec![
3041                    &mut account_account,
3042                    &mut account2_account,
3043                    &mut owner_account,
3044                ],
3045            )
3046        );
3047
3048        // transfer half back
3049        do_process_instruction(
3050            #[allow(deprecated)]
3051            transfer(
3052                &program_id,
3053                &account2_key,
3054                &account_key,
3055                &owner_key,
3056                &[],
3057                500,
3058            )
3059            .unwrap(),
3060            vec![
3061                &mut account2_account,
3062                &mut account_account,
3063                &mut owner_account,
3064            ],
3065        )
3066        .unwrap();
3067
3068        // incorrect decimals
3069        assert_eq!(
3070            Err(TokenError::MintDecimalsMismatch.into()),
3071            do_process_instruction(
3072                transfer_checked(
3073                    &program_id,
3074                    &account2_key,
3075                    &mint_key,
3076                    &account_key,
3077                    &owner_key,
3078                    &[],
3079                    1,
3080                    10 // <-- incorrect decimals
3081                )
3082                .unwrap(),
3083                vec![
3084                    &mut account2_account,
3085                    &mut mint_account,
3086                    &mut account_account,
3087                    &mut owner_account,
3088                ],
3089            )
3090        );
3091
3092        // incorrect mint
3093        assert_eq!(
3094            Err(TokenError::MintMismatch.into()),
3095            do_process_instruction(
3096                transfer_checked(
3097                    &program_id,
3098                    &account2_key,
3099                    &account3_key, // <-- incorrect mint
3100                    &account_key,
3101                    &owner_key,
3102                    &[],
3103                    1,
3104                    2
3105                )
3106                .unwrap(),
3107                vec![
3108                    &mut account2_account,
3109                    &mut account3_account, // <-- incorrect mint
3110                    &mut account_account,
3111                    &mut owner_account,
3112                ],
3113            )
3114        );
3115        // transfer rest with explicit decimals
3116        do_process_instruction(
3117            transfer_checked(
3118                &program_id,
3119                &account2_key,
3120                &mint_key,
3121                &account_key,
3122                &owner_key,
3123                &[],
3124                500,
3125                2,
3126            )
3127            .unwrap(),
3128            vec![
3129                &mut account2_account,
3130                &mut mint_account,
3131                &mut account_account,
3132                &mut owner_account,
3133            ],
3134        )
3135        .unwrap();
3136
3137        // insufficient funds
3138        assert_eq!(
3139            Err(TokenError::InsufficientFunds.into()),
3140            do_process_instruction(
3141                #[allow(deprecated)]
3142                transfer(&program_id, &account2_key, &account_key, &owner_key, &[], 1).unwrap(),
3143                vec![
3144                    &mut account2_account,
3145                    &mut account_account,
3146                    &mut owner_account,
3147                ],
3148            )
3149        );
3150
3151        // approve delegate
3152        do_process_instruction(
3153            approve(
3154                &program_id,
3155                &account_key,
3156                &delegate_key,
3157                &owner_key,
3158                &[],
3159                100,
3160            )
3161            .unwrap(),
3162            vec![
3163                &mut account_account,
3164                &mut delegate_account,
3165                &mut owner_account,
3166            ],
3167        )
3168        .unwrap();
3169
3170        // not a delegate of source account
3171        assert_eq!(
3172            Err(TokenError::OwnerMismatch.into()),
3173            do_process_instruction(
3174                #[allow(deprecated)]
3175                transfer(
3176                    &program_id,
3177                    &account_key,
3178                    &account2_key,
3179                    &owner2_key, // <-- incorrect owner or delegate
3180                    &[],
3181                    1,
3182                )
3183                .unwrap(),
3184                vec![
3185                    &mut account_account,
3186                    &mut account2_account,
3187                    &mut owner2_account,
3188                ],
3189            )
3190        );
3191
3192        // insufficient funds approved via delegate
3193        assert_eq!(
3194            Err(TokenError::InsufficientFunds.into()),
3195            do_process_instruction(
3196                #[allow(deprecated)]
3197                transfer(
3198                    &program_id,
3199                    &account_key,
3200                    &account2_key,
3201                    &delegate_key,
3202                    &[],
3203                    101
3204                )
3205                .unwrap(),
3206                vec![
3207                    &mut account_account,
3208                    &mut account2_account,
3209                    &mut delegate_account,
3210                ],
3211            )
3212        );
3213
3214        // transfer via delegate
3215        do_process_instruction(
3216            #[allow(deprecated)]
3217            transfer(
3218                &program_id,
3219                &account_key,
3220                &account2_key,
3221                &delegate_key,
3222                &[],
3223                100,
3224            )
3225            .unwrap(),
3226            vec![
3227                &mut account_account,
3228                &mut account2_account,
3229                &mut delegate_account,
3230            ],
3231        )
3232        .unwrap();
3233
3234        // insufficient funds approved via delegate
3235        assert_eq!(
3236            Err(TokenError::OwnerMismatch.into()),
3237            do_process_instruction(
3238                #[allow(deprecated)]
3239                transfer(
3240                    &program_id,
3241                    &account_key,
3242                    &account2_key,
3243                    &delegate_key,
3244                    &[],
3245                    1
3246                )
3247                .unwrap(),
3248                vec![
3249                    &mut account_account,
3250                    &mut account2_account,
3251                    &mut delegate_account,
3252                ],
3253            )
3254        );
3255
3256        // transfer rest
3257        do_process_instruction(
3258            #[allow(deprecated)]
3259            transfer(
3260                &program_id,
3261                &account_key,
3262                &account2_key,
3263                &owner_key,
3264                &[],
3265                900,
3266            )
3267            .unwrap(),
3268            vec![
3269                &mut account_account,
3270                &mut account2_account,
3271                &mut owner_account,
3272            ],
3273        )
3274        .unwrap();
3275
3276        // approve delegate
3277        do_process_instruction(
3278            approve(
3279                &program_id,
3280                &account_key,
3281                &delegate_key,
3282                &owner_key,
3283                &[],
3284                100,
3285            )
3286            .unwrap(),
3287            vec![
3288                &mut account_account,
3289                &mut delegate_account,
3290                &mut owner_account,
3291            ],
3292        )
3293        .unwrap();
3294
3295        // insufficient funds in source account via delegate
3296        assert_eq!(
3297            Err(TokenError::InsufficientFunds.into()),
3298            do_process_instruction(
3299                #[allow(deprecated)]
3300                transfer(
3301                    &program_id,
3302                    &account_key,
3303                    &account2_key,
3304                    &delegate_key,
3305                    &[],
3306                    100
3307                )
3308                .unwrap(),
3309                vec![
3310                    &mut account_account,
3311                    &mut account2_account,
3312                    &mut delegate_account,
3313                ],
3314            )
3315        );
3316    }
3317
3318    #[test]
3319    fn test_self_transfer() {
3320        let program_id = crate::id();
3321        let account_key = Pubkey::new_unique();
3322        let mut account_account = SolanaAccount::new(
3323            account_minimum_balance(),
3324            Account::get_packed_len(),
3325            &program_id,
3326        );
3327        let account2_key = Pubkey::new_unique();
3328        let mut account2_account = SolanaAccount::new(
3329            account_minimum_balance(),
3330            Account::get_packed_len(),
3331            &program_id,
3332        );
3333        let account3_key = Pubkey::new_unique();
3334        let mut account3_account = SolanaAccount::new(
3335            account_minimum_balance(),
3336            Account::get_packed_len(),
3337            &program_id,
3338        );
3339        let delegate_key = Pubkey::new_unique();
3340        let mut delegate_account = SolanaAccount::default();
3341        let owner_key = Pubkey::new_unique();
3342        let mut owner_account = SolanaAccount::default();
3343        let owner2_key = Pubkey::new_unique();
3344        let mut owner2_account = SolanaAccount::default();
3345        let mint_key = Pubkey::new_unique();
3346        let mut mint_account =
3347            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
3348        let mut rent_sysvar = rent_sysvar();
3349
3350        // create mint
3351        do_process_instruction(
3352            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
3353            vec![&mut mint_account, &mut rent_sysvar],
3354        )
3355        .unwrap();
3356
3357        // create account
3358        do_process_instruction(
3359            initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
3360            vec![
3361                &mut account_account,
3362                &mut mint_account,
3363                &mut owner_account,
3364                &mut rent_sysvar,
3365            ],
3366        )
3367        .unwrap();
3368
3369        // create another account
3370        do_process_instruction(
3371            initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(),
3372            vec![
3373                &mut account2_account,
3374                &mut mint_account,
3375                &mut owner_account,
3376                &mut rent_sysvar,
3377            ],
3378        )
3379        .unwrap();
3380
3381        // create another account
3382        do_process_instruction(
3383            initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(),
3384            vec![
3385                &mut account3_account,
3386                &mut mint_account,
3387                &mut owner_account,
3388                &mut rent_sysvar,
3389            ],
3390        )
3391        .unwrap();
3392
3393        // mint to account
3394        do_process_instruction(
3395            mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(),
3396            vec![&mut mint_account, &mut account_account, &mut owner_account],
3397        )
3398        .unwrap();
3399
3400        let account_info = (&account_key, false, &mut account_account).into_account_info();
3401        let account3_info = (&account3_key, false, &mut account3_account).into_account_info();
3402        let delegate_info = (&delegate_key, true, &mut delegate_account).into_account_info();
3403        let owner_info = (&owner_key, true, &mut owner_account).into_account_info();
3404        let owner2_info = (&owner2_key, true, &mut owner2_account).into_account_info();
3405        let mint_info = (&mint_key, false, &mut mint_account).into_account_info();
3406
3407        // transfer
3408        #[allow(deprecated)]
3409        let instruction = transfer(
3410            &program_id,
3411            account_info.key,
3412            account_info.key,
3413            owner_info.key,
3414            &[],
3415            1000,
3416        )
3417        .unwrap();
3418        assert_eq!(
3419            Ok(()),
3420            Processor::process(
3421                &instruction.program_id,
3422                &[
3423                    account_info.clone(),
3424                    account_info.clone(),
3425                    owner_info.clone(),
3426                ],
3427                &instruction.data,
3428            )
3429        );
3430        // no balance change...
3431        let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap();
3432        assert_eq!(account.amount, 1000);
3433
3434        // transfer checked
3435        let instruction = transfer_checked(
3436            &program_id,
3437            account_info.key,
3438            mint_info.key,
3439            account_info.key,
3440            owner_info.key,
3441            &[],
3442            1000,
3443            2,
3444        )
3445        .unwrap();
3446        assert_eq!(
3447            Ok(()),
3448            Processor::process(
3449                &instruction.program_id,
3450                &[
3451                    account_info.clone(),
3452                    mint_info.clone(),
3453                    account_info.clone(),
3454                    owner_info.clone(),
3455                ],
3456                &instruction.data,
3457            )
3458        );
3459        // no balance change...
3460        let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap();
3461        assert_eq!(account.amount, 1000);
3462
3463        // missing signer
3464        let mut owner_no_sign_info = owner_info.clone();
3465        #[allow(deprecated)]
3466        let mut instruction = transfer(
3467            &program_id,
3468            account_info.key,
3469            account_info.key,
3470            owner_no_sign_info.key,
3471            &[],
3472            1000,
3473        )
3474        .unwrap();
3475        instruction.accounts[2].is_signer = false;
3476        owner_no_sign_info.is_signer = false;
3477        assert_eq!(
3478            Err(ProgramError::MissingRequiredSignature),
3479            Processor::process(
3480                &instruction.program_id,
3481                &[
3482                    account_info.clone(),
3483                    account_info.clone(),
3484                    owner_no_sign_info.clone(),
3485                ],
3486                &instruction.data,
3487            )
3488        );
3489
3490        // missing signer checked
3491        let mut instruction = transfer_checked(
3492            &program_id,
3493            account_info.key,
3494            mint_info.key,
3495            account_info.key,
3496            owner_no_sign_info.key,
3497            &[],
3498            1000,
3499            2,
3500        )
3501        .unwrap();
3502        instruction.accounts[3].is_signer = false;
3503        assert_eq!(
3504            Err(ProgramError::MissingRequiredSignature),
3505            Processor::process(
3506                &instruction.program_id,
3507                &[
3508                    account_info.clone(),
3509                    mint_info.clone(),
3510                    account_info.clone(),
3511                    owner_no_sign_info,
3512                ],
3513                &instruction.data,
3514            )
3515        );
3516
3517        // missing owner
3518        #[allow(deprecated)]
3519        let instruction = transfer(
3520            &program_id,
3521            account_info.key,
3522            account_info.key,
3523            owner2_info.key,
3524            &[],
3525            1000,
3526        )
3527        .unwrap();
3528        assert_eq!(
3529            Err(TokenError::OwnerMismatch.into()),
3530            Processor::process(
3531                &instruction.program_id,
3532                &[
3533                    account_info.clone(),
3534                    account_info.clone(),
3535                    owner2_info.clone(),
3536                ],
3537                &instruction.data,
3538            )
3539        );
3540
3541        // missing owner checked
3542        let instruction = transfer_checked(
3543            &program_id,
3544            account_info.key,
3545            mint_info.key,
3546            account_info.key,
3547            owner2_info.key,
3548            &[],
3549            1000,
3550            2,
3551        )
3552        .unwrap();
3553        assert_eq!(
3554            Err(TokenError::OwnerMismatch.into()),
3555            Processor::process(
3556                &instruction.program_id,
3557                &[
3558                    account_info.clone(),
3559                    mint_info.clone(),
3560                    account_info.clone(),
3561                    owner2_info.clone(),
3562                ],
3563                &instruction.data,
3564            )
3565        );
3566
3567        // insufficient funds
3568        #[allow(deprecated)]
3569        let instruction = transfer(
3570            &program_id,
3571            account_info.key,
3572            account_info.key,
3573            owner_info.key,
3574            &[],
3575            1001,
3576        )
3577        .unwrap();
3578        assert_eq!(
3579            Err(TokenError::InsufficientFunds.into()),
3580            Processor::process(
3581                &instruction.program_id,
3582                &[
3583                    account_info.clone(),
3584                    account_info.clone(),
3585                    owner_info.clone(),
3586                ],
3587                &instruction.data,
3588            )
3589        );
3590
3591        // insufficient funds checked
3592        let instruction = transfer_checked(
3593            &program_id,
3594            account_info.key,
3595            mint_info.key,
3596            account_info.key,
3597            owner_info.key,
3598            &[],
3599            1001,
3600            2,
3601        )
3602        .unwrap();
3603        assert_eq!(
3604            Err(TokenError::InsufficientFunds.into()),
3605            Processor::process(
3606                &instruction.program_id,
3607                &[
3608                    account_info.clone(),
3609                    mint_info.clone(),
3610                    account_info.clone(),
3611                    owner_info.clone(),
3612                ],
3613                &instruction.data,
3614            )
3615        );
3616
3617        // incorrect decimals
3618        let instruction = transfer_checked(
3619            &program_id,
3620            account_info.key,
3621            mint_info.key,
3622            account_info.key,
3623            owner_info.key,
3624            &[],
3625            1,
3626            10, // <-- incorrect decimals
3627        )
3628        .unwrap();
3629        assert_eq!(
3630            Err(TokenError::MintDecimalsMismatch.into()),
3631            Processor::process(
3632                &instruction.program_id,
3633                &[
3634                    account_info.clone(),
3635                    mint_info.clone(),
3636                    account_info.clone(),
3637                    owner_info.clone(),
3638                ],
3639                &instruction.data,
3640            )
3641        );
3642
3643        // incorrect mint
3644        let instruction = transfer_checked(
3645            &program_id,
3646            account_info.key,
3647            account3_info.key, // <-- incorrect mint
3648            account_info.key,
3649            owner_info.key,
3650            &[],
3651            1,
3652            2,
3653        )
3654        .unwrap();
3655        assert_eq!(
3656            Err(TokenError::MintMismatch.into()),
3657            Processor::process(
3658                &instruction.program_id,
3659                &[
3660                    account_info.clone(),
3661                    account3_info.clone(), // <-- incorrect mint
3662                    account_info.clone(),
3663                    owner_info.clone(),
3664                ],
3665                &instruction.data,
3666            )
3667        );
3668
3669        // approve delegate
3670        let instruction = approve(
3671            &program_id,
3672            account_info.key,
3673            delegate_info.key,
3674            owner_info.key,
3675            &[],
3676            100,
3677        )
3678        .unwrap();
3679        Processor::process(
3680            &instruction.program_id,
3681            &[
3682                account_info.clone(),
3683                delegate_info.clone(),
3684                owner_info.clone(),
3685            ],
3686            &instruction.data,
3687        )
3688        .unwrap();
3689
3690        // delegate transfer
3691        #[allow(deprecated)]
3692        let instruction = transfer(
3693            &program_id,
3694            account_info.key,
3695            account_info.key,
3696            delegate_info.key,
3697            &[],
3698            100,
3699        )
3700        .unwrap();
3701        assert_eq!(
3702            Ok(()),
3703            Processor::process(
3704                &instruction.program_id,
3705                &[
3706                    account_info.clone(),
3707                    account_info.clone(),
3708                    delegate_info.clone(),
3709                ],
3710                &instruction.data,
3711            )
3712        );
3713        // no balance change...
3714        let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap();
3715        assert_eq!(account.amount, 1000);
3716        assert_eq!(account.delegated_amount, 100);
3717
3718        // delegate transfer checked
3719        let instruction = transfer_checked(
3720            &program_id,
3721            account_info.key,
3722            mint_info.key,
3723            account_info.key,
3724            delegate_info.key,
3725            &[],
3726            100,
3727            2,
3728        )
3729        .unwrap();
3730        assert_eq!(
3731            Ok(()),
3732            Processor::process(
3733                &instruction.program_id,
3734                &[
3735                    account_info.clone(),
3736                    mint_info.clone(),
3737                    account_info.clone(),
3738                    delegate_info.clone(),
3739                ],
3740                &instruction.data,
3741            )
3742        );
3743        // no balance change...
3744        let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap();
3745        assert_eq!(account.amount, 1000);
3746        assert_eq!(account.delegated_amount, 100);
3747
3748        // delegate insufficient funds
3749        #[allow(deprecated)]
3750        let instruction = transfer(
3751            &program_id,
3752            account_info.key,
3753            account_info.key,
3754            delegate_info.key,
3755            &[],
3756            101,
3757        )
3758        .unwrap();
3759        assert_eq!(
3760            Err(TokenError::InsufficientFunds.into()),
3761            Processor::process(
3762                &instruction.program_id,
3763                &[
3764                    account_info.clone(),
3765                    account_info.clone(),
3766                    delegate_info.clone(),
3767                ],
3768                &instruction.data,
3769            )
3770        );
3771
3772        // delegate insufficient funds checked
3773        let instruction = transfer_checked(
3774            &program_id,
3775            account_info.key,
3776            mint_info.key,
3777            account_info.key,
3778            delegate_info.key,
3779            &[],
3780            101,
3781            2,
3782        )
3783        .unwrap();
3784        assert_eq!(
3785            Err(TokenError::InsufficientFunds.into()),
3786            Processor::process(
3787                &instruction.program_id,
3788                &[
3789                    account_info.clone(),
3790                    mint_info.clone(),
3791                    account_info.clone(),
3792                    delegate_info.clone(),
3793                ],
3794                &instruction.data,
3795            )
3796        );
3797
3798        // owner transfer with delegate assigned
3799        #[allow(deprecated)]
3800        let instruction = transfer(
3801            &program_id,
3802            account_info.key,
3803            account_info.key,
3804            owner_info.key,
3805            &[],
3806            1000,
3807        )
3808        .unwrap();
3809        assert_eq!(
3810            Ok(()),
3811            Processor::process(
3812                &instruction.program_id,
3813                &[
3814                    account_info.clone(),
3815                    account_info.clone(),
3816                    owner_info.clone(),
3817                ],
3818                &instruction.data,
3819            )
3820        );
3821        // no balance change...
3822        let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap();
3823        assert_eq!(account.amount, 1000);
3824
3825        // owner transfer with delegate assigned checked
3826        let instruction = transfer_checked(
3827            &program_id,
3828            account_info.key,
3829            mint_info.key,
3830            account_info.key,
3831            owner_info.key,
3832            &[],
3833            1000,
3834            2,
3835        )
3836        .unwrap();
3837        assert_eq!(
3838            Ok(()),
3839            Processor::process(
3840                &instruction.program_id,
3841                &[
3842                    account_info.clone(),
3843                    mint_info.clone(),
3844                    account_info.clone(),
3845                    owner_info.clone(),
3846                ],
3847                &instruction.data,
3848            )
3849        );
3850        // no balance change...
3851        let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap();
3852        assert_eq!(account.amount, 1000);
3853    }
3854
3855    #[test]
3856    fn test_mintable_token_with_zero_supply() {
3857        let program_id = crate::id();
3858        let account_key = Pubkey::new_unique();
3859        let mut account_account = SolanaAccount::new(
3860            account_minimum_balance(),
3861            Account::get_packed_len(),
3862            &program_id,
3863        );
3864        let owner_key = Pubkey::new_unique();
3865        let mut owner_account = SolanaAccount::default();
3866        let mint_key = Pubkey::new_unique();
3867        let mut mint_account =
3868            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
3869        let mut rent_sysvar = rent_sysvar();
3870
3871        // create mint-able token with zero supply
3872        let decimals = 2;
3873        do_process_instruction(
3874            initialize_mint(&program_id, &mint_key, &owner_key, None, decimals).unwrap(),
3875            vec![&mut mint_account, &mut rent_sysvar],
3876        )
3877        .unwrap();
3878        let mint = Mint::unpack_unchecked(&mint_account.data).unwrap();
3879        assert_eq!(
3880            mint,
3881            Mint {
3882                mint_authority: COption::Some(owner_key),
3883                supply: 0,
3884                decimals,
3885                is_initialized: true,
3886                freeze_authority: COption::None,
3887            }
3888        );
3889
3890        // create account
3891        do_process_instruction(
3892            initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
3893            vec![
3894                &mut account_account,
3895                &mut mint_account,
3896                &mut owner_account,
3897                &mut rent_sysvar,
3898            ],
3899        )
3900        .unwrap();
3901
3902        // mint to
3903        do_process_instruction(
3904            mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(),
3905            vec![&mut mint_account, &mut account_account, &mut owner_account],
3906        )
3907        .unwrap();
3908        let _ = Mint::unpack(&mint_account.data).unwrap();
3909        let account = Account::unpack_unchecked(&account_account.data).unwrap();
3910        assert_eq!(account.amount, 42);
3911
3912        // mint to 2, with incorrect decimals
3913        assert_eq!(
3914            Err(TokenError::MintDecimalsMismatch.into()),
3915            do_process_instruction(
3916                mint_to_checked(
3917                    &program_id,
3918                    &mint_key,
3919                    &account_key,
3920                    &owner_key,
3921                    &[],
3922                    42,
3923                    decimals + 1
3924                )
3925                .unwrap(),
3926                vec![&mut mint_account, &mut account_account, &mut owner_account],
3927            )
3928        );
3929
3930        let _ = Mint::unpack(&mint_account.data).unwrap();
3931        let account = Account::unpack_unchecked(&account_account.data).unwrap();
3932        assert_eq!(account.amount, 42);
3933
3934        // mint to 2
3935        do_process_instruction(
3936            mint_to_checked(
3937                &program_id,
3938                &mint_key,
3939                &account_key,
3940                &owner_key,
3941                &[],
3942                42,
3943                decimals,
3944            )
3945            .unwrap(),
3946            vec![&mut mint_account, &mut account_account, &mut owner_account],
3947        )
3948        .unwrap();
3949        let _ = Mint::unpack(&mint_account.data).unwrap();
3950        let account = Account::unpack_unchecked(&account_account.data).unwrap();
3951        assert_eq!(account.amount, 84);
3952    }
3953
3954    #[test]
3955    fn test_approve_dups() {
3956        let program_id = crate::id();
3957        let account1_key = Pubkey::new_unique();
3958        let mut account1_account = SolanaAccount::new(
3959            account_minimum_balance(),
3960            Account::get_packed_len(),
3961            &program_id,
3962        );
3963        let account1_info: AccountInfo<'_> = (&account1_key, true, &mut account1_account).into();
3964        let account2_key = Pubkey::new_unique();
3965        let mut account2_account = SolanaAccount::new(
3966            account_minimum_balance(),
3967            Account::get_packed_len(),
3968            &program_id,
3969        );
3970        let account2_info: AccountInfo<'_> = (&account2_key, false, &mut account2_account).into();
3971        let account3_key = Pubkey::new_unique();
3972        let mut account3_account = SolanaAccount::new(
3973            account_minimum_balance(),
3974            Account::get_packed_len(),
3975            &program_id,
3976        );
3977        let account3_info: AccountInfo<'_> = (&account3_key, true, &mut account3_account).into();
3978        let multisig_key = Pubkey::new_unique();
3979        let mut multisig_account = SolanaAccount::new(
3980            multisig_minimum_balance(),
3981            Multisig::get_packed_len(),
3982            &program_id,
3983        );
3984        let multisig_info: AccountInfo<'_> = (&multisig_key, true, &mut multisig_account).into();
3985        let owner_key = Pubkey::new_unique();
3986        let mut owner_account = SolanaAccount::default();
3987        let owner_info: AccountInfo<'_> = (&owner_key, true, &mut owner_account).into();
3988        let mint_key = Pubkey::new_unique();
3989        let mut mint_account =
3990            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
3991        let mint_info: AccountInfo<'_> = (&mint_key, false, &mut mint_account).into();
3992        let rent_key = rent::id();
3993        let mut rent_sysvar = rent_sysvar();
3994        let rent_info: AccountInfo<'_> = (&rent_key, false, &mut rent_sysvar).into();
3995
3996        // create mint
3997        do_process_instruction_dups(
3998            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
3999            vec![mint_info.clone(), rent_info.clone()],
4000        )
4001        .unwrap();
4002
4003        // create account
4004        do_process_instruction_dups(
4005            initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(),
4006            vec![
4007                account1_info.clone(),
4008                mint_info.clone(),
4009                account1_info.clone(),
4010                rent_info.clone(),
4011            ],
4012        )
4013        .unwrap();
4014
4015        // create another account
4016        do_process_instruction_dups(
4017            initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(),
4018            vec![
4019                account2_info.clone(),
4020                mint_info.clone(),
4021                owner_info.clone(),
4022                rent_info.clone(),
4023            ],
4024        )
4025        .unwrap();
4026
4027        // mint to account
4028        do_process_instruction_dups(
4029            mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(),
4030            vec![mint_info.clone(), account1_info.clone(), owner_info.clone()],
4031        )
4032        .unwrap();
4033
4034        // source-owner approve
4035        do_process_instruction_dups(
4036            approve(
4037                &program_id,
4038                &account1_key,
4039                &account2_key,
4040                &account1_key,
4041                &[],
4042                500,
4043            )
4044            .unwrap(),
4045            vec![
4046                account1_info.clone(),
4047                account2_info.clone(),
4048                account1_info.clone(),
4049            ],
4050        )
4051        .unwrap();
4052
4053        // source-owner approve_checked
4054        do_process_instruction_dups(
4055            approve_checked(
4056                &program_id,
4057                &account1_key,
4058                &mint_key,
4059                &account2_key,
4060                &account1_key,
4061                &[],
4062                500,
4063                2,
4064            )
4065            .unwrap(),
4066            vec![
4067                account1_info.clone(),
4068                mint_info.clone(),
4069                account2_info.clone(),
4070                account1_info.clone(),
4071            ],
4072        )
4073        .unwrap();
4074
4075        // source-owner revoke
4076        do_process_instruction_dups(
4077            revoke(&program_id, &account1_key, &account1_key, &[]).unwrap(),
4078            vec![account1_info.clone(), account1_info.clone()],
4079        )
4080        .unwrap();
4081
4082        // test source-multisig signer
4083        do_process_instruction_dups(
4084            initialize_multisig(&program_id, &multisig_key, &[&account3_key], 1).unwrap(),
4085            vec![
4086                multisig_info.clone(),
4087                rent_info.clone(),
4088                account3_info.clone(),
4089            ],
4090        )
4091        .unwrap();
4092
4093        do_process_instruction_dups(
4094            initialize_account(&program_id, &account3_key, &mint_key, &multisig_key).unwrap(),
4095            vec![
4096                account3_info.clone(),
4097                mint_info.clone(),
4098                multisig_info.clone(),
4099                rent_info.clone(),
4100            ],
4101        )
4102        .unwrap();
4103
4104        do_process_instruction_dups(
4105            mint_to(&program_id, &mint_key, &account3_key, &owner_key, &[], 1000).unwrap(),
4106            vec![mint_info.clone(), account3_info.clone(), owner_info.clone()],
4107        )
4108        .unwrap();
4109
4110        // source-multisig-signer approve
4111        do_process_instruction_dups(
4112            approve(
4113                &program_id,
4114                &account3_key,
4115                &account2_key,
4116                &multisig_key,
4117                &[&account3_key],
4118                500,
4119            )
4120            .unwrap(),
4121            vec![
4122                account3_info.clone(),
4123                account2_info.clone(),
4124                multisig_info.clone(),
4125                account3_info.clone(),
4126            ],
4127        )
4128        .unwrap();
4129
4130        // source-multisig-signer approve_checked
4131        do_process_instruction_dups(
4132            approve_checked(
4133                &program_id,
4134                &account3_key,
4135                &mint_key,
4136                &account2_key,
4137                &multisig_key,
4138                &[&account3_key],
4139                500,
4140                2,
4141            )
4142            .unwrap(),
4143            vec![
4144                account3_info.clone(),
4145                mint_info.clone(),
4146                account2_info.clone(),
4147                multisig_info.clone(),
4148                account3_info.clone(),
4149            ],
4150        )
4151        .unwrap();
4152
4153        // source-owner multisig-signer
4154        do_process_instruction_dups(
4155            revoke(&program_id, &account3_key, &multisig_key, &[&account3_key]).unwrap(),
4156            vec![
4157                account3_info.clone(),
4158                multisig_info.clone(),
4159                account3_info.clone(),
4160            ],
4161        )
4162        .unwrap();
4163
4164        // approve to source
4165        do_process_instruction_dups(
4166            approve_checked(
4167                &program_id,
4168                &account2_key,
4169                &mint_key,
4170                &account2_key,
4171                &owner_key,
4172                &[],
4173                500,
4174                2,
4175            )
4176            .unwrap(),
4177            vec![
4178                account2_info.clone(),
4179                mint_info.clone(),
4180                account2_info.clone(),
4181                owner_info.clone(),
4182            ],
4183        )
4184        .unwrap();
4185
4186        // source-delegate revoke, force account2 to be a signer
4187        let account2_info: AccountInfo<'_> = (&account2_key, true, &mut account2_account).into();
4188        do_process_instruction_dups(
4189            revoke(&program_id, &account2_key, &account2_key, &[]).unwrap(),
4190            vec![account2_info.clone(), account2_info.clone()],
4191        )
4192        .unwrap();
4193    }
4194
4195    #[test]
4196    fn test_approve() {
4197        let program_id = crate::id();
4198        let account_key = Pubkey::new_unique();
4199        let mut account_account = SolanaAccount::new(
4200            account_minimum_balance(),
4201            Account::get_packed_len(),
4202            &program_id,
4203        );
4204        let account2_key = Pubkey::new_unique();
4205        let mut account2_account = SolanaAccount::new(
4206            account_minimum_balance(),
4207            Account::get_packed_len(),
4208            &program_id,
4209        );
4210        let delegate_key = Pubkey::new_unique();
4211        let mut delegate_account = SolanaAccount::default();
4212        let owner_key = Pubkey::new_unique();
4213        let mut owner_account = SolanaAccount::default();
4214        let owner2_key = Pubkey::new_unique();
4215        let mut owner2_account = SolanaAccount::default();
4216        let mint_key = Pubkey::new_unique();
4217        let mut mint_account =
4218            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
4219        let mut rent_sysvar = rent_sysvar();
4220
4221        // create mint
4222        do_process_instruction(
4223            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
4224            vec![&mut mint_account, &mut rent_sysvar],
4225        )
4226        .unwrap();
4227
4228        // create account
4229        do_process_instruction(
4230            initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
4231            vec![
4232                &mut account_account,
4233                &mut mint_account,
4234                &mut owner_account,
4235                &mut rent_sysvar,
4236            ],
4237        )
4238        .unwrap();
4239
4240        // create another account
4241        do_process_instruction(
4242            initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(),
4243            vec![
4244                &mut account2_account,
4245                &mut mint_account,
4246                &mut owner_account,
4247                &mut rent_sysvar,
4248            ],
4249        )
4250        .unwrap();
4251
4252        // mint to account
4253        do_process_instruction(
4254            mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(),
4255            vec![&mut mint_account, &mut account_account, &mut owner_account],
4256        )
4257        .unwrap();
4258
4259        // missing signer
4260        let mut instruction = approve(
4261            &program_id,
4262            &account_key,
4263            &delegate_key,
4264            &owner_key,
4265            &[],
4266            100,
4267        )
4268        .unwrap();
4269        instruction.accounts[2].is_signer = false;
4270        assert_eq!(
4271            Err(ProgramError::MissingRequiredSignature),
4272            do_process_instruction(
4273                instruction,
4274                vec![
4275                    &mut account_account,
4276                    &mut delegate_account,
4277                    &mut owner_account,
4278                ],
4279            )
4280        );
4281
4282        // no owner
4283        assert_eq!(
4284            Err(TokenError::OwnerMismatch.into()),
4285            do_process_instruction(
4286                approve(
4287                    &program_id,
4288                    &account_key,
4289                    &delegate_key,
4290                    &owner2_key,
4291                    &[],
4292                    100
4293                )
4294                .unwrap(),
4295                vec![
4296                    &mut account_account,
4297                    &mut delegate_account,
4298                    &mut owner2_account,
4299                ],
4300            )
4301        );
4302
4303        // approve delegate
4304        do_process_instruction(
4305            approve(
4306                &program_id,
4307                &account_key,
4308                &delegate_key,
4309                &owner_key,
4310                &[],
4311                100,
4312            )
4313            .unwrap(),
4314            vec![
4315                &mut account_account,
4316                &mut delegate_account,
4317                &mut owner_account,
4318            ],
4319        )
4320        .unwrap();
4321
4322        // approve delegate 2, with incorrect decimals
4323        assert_eq!(
4324            Err(TokenError::MintDecimalsMismatch.into()),
4325            do_process_instruction(
4326                approve_checked(
4327                    &program_id,
4328                    &account_key,
4329                    &mint_key,
4330                    &delegate_key,
4331                    &owner_key,
4332                    &[],
4333                    100,
4334                    0 // <-- incorrect decimals
4335                )
4336                .unwrap(),
4337                vec![
4338                    &mut account_account,
4339                    &mut mint_account,
4340                    &mut delegate_account,
4341                    &mut owner_account,
4342                ],
4343            )
4344        );
4345
4346        // approve delegate 2, with incorrect mint
4347        assert_eq!(
4348            Err(TokenError::MintMismatch.into()),
4349            do_process_instruction(
4350                approve_checked(
4351                    &program_id,
4352                    &account_key,
4353                    &account2_key, // <-- bad mint
4354                    &delegate_key,
4355                    &owner_key,
4356                    &[],
4357                    100,
4358                    0
4359                )
4360                .unwrap(),
4361                vec![
4362                    &mut account_account,
4363                    &mut account2_account, // <-- bad mint
4364                    &mut delegate_account,
4365                    &mut owner_account,
4366                ],
4367            )
4368        );
4369
4370        // approve delegate 2
4371        do_process_instruction(
4372            approve_checked(
4373                &program_id,
4374                &account_key,
4375                &mint_key,
4376                &delegate_key,
4377                &owner_key,
4378                &[],
4379                100,
4380                2,
4381            )
4382            .unwrap(),
4383            vec![
4384                &mut account_account,
4385                &mut mint_account,
4386                &mut delegate_account,
4387                &mut owner_account,
4388            ],
4389        )
4390        .unwrap();
4391
4392        // revoke delegate
4393        do_process_instruction(
4394            revoke(&program_id, &account_key, &owner_key, &[]).unwrap(),
4395            vec![&mut account_account, &mut owner_account],
4396        )
4397        .unwrap();
4398
4399        // approve delegate 3
4400        do_process_instruction(
4401            approve_checked(
4402                &program_id,
4403                &account_key,
4404                &mint_key,
4405                &delegate_key,
4406                &owner_key,
4407                &[],
4408                100,
4409                2,
4410            )
4411            .unwrap(),
4412            vec![
4413                &mut account_account,
4414                &mut mint_account,
4415                &mut delegate_account,
4416                &mut owner_account,
4417            ],
4418        )
4419        .unwrap();
4420
4421        // revoke by delegate
4422        do_process_instruction(
4423            revoke(&program_id, &account_key, &delegate_key, &[]).unwrap(),
4424            vec![&mut account_account, &mut delegate_account],
4425        )
4426        .unwrap();
4427
4428        // fails the second time
4429        assert_eq!(
4430            Err(TokenError::OwnerMismatch.into()),
4431            do_process_instruction(
4432                revoke(&program_id, &account_key, &delegate_key, &[]).unwrap(),
4433                vec![&mut account_account, &mut delegate_account],
4434            )
4435        );
4436    }
4437
4438    #[test]
4439    fn test_set_authority_dups() {
4440        let program_id = crate::id();
4441        let account1_key = Pubkey::new_unique();
4442        let mut account1_account = SolanaAccount::new(
4443            account_minimum_balance(),
4444            Account::get_packed_len(),
4445            &program_id,
4446        );
4447        let account1_info: AccountInfo<'_> = (&account1_key, true, &mut account1_account).into();
4448        let owner_key = Pubkey::new_unique();
4449        let mint_key = Pubkey::new_unique();
4450        let mut mint_account =
4451            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
4452        let mint_info: AccountInfo<'_> = (&mint_key, true, &mut mint_account).into();
4453        let rent_key = rent::id();
4454        let mut rent_sysvar = rent_sysvar();
4455        let rent_info: AccountInfo<'_> = (&rent_key, false, &mut rent_sysvar).into();
4456
4457        // create mint
4458        do_process_instruction_dups(
4459            initialize_mint(&program_id, &mint_key, &mint_key, Some(&mint_key), 2).unwrap(),
4460            vec![mint_info.clone(), rent_info.clone()],
4461        )
4462        .unwrap();
4463
4464        // create account
4465        do_process_instruction_dups(
4466            initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(),
4467            vec![
4468                account1_info.clone(),
4469                mint_info.clone(),
4470                account1_info.clone(),
4471                rent_info.clone(),
4472            ],
4473        )
4474        .unwrap();
4475
4476        // set mint_authority when currently self
4477        do_process_instruction_dups(
4478            set_authority(
4479                &program_id,
4480                &mint_key,
4481                Some(&owner_key),
4482                AuthorityType::MintTokens,
4483                &mint_key,
4484                &[],
4485            )
4486            .unwrap(),
4487            vec![mint_info.clone(), mint_info.clone()],
4488        )
4489        .unwrap();
4490
4491        // set freeze_authority when currently self
4492        do_process_instruction_dups(
4493            set_authority(
4494                &program_id,
4495                &mint_key,
4496                Some(&owner_key),
4497                AuthorityType::FreezeAccount,
4498                &mint_key,
4499                &[],
4500            )
4501            .unwrap(),
4502            vec![mint_info.clone(), mint_info.clone()],
4503        )
4504        .unwrap();
4505
4506        // set account owner when currently self
4507        do_process_instruction_dups(
4508            set_authority(
4509                &program_id,
4510                &account1_key,
4511                Some(&owner_key),
4512                AuthorityType::AccountOwner,
4513                &account1_key,
4514                &[],
4515            )
4516            .unwrap(),
4517            vec![account1_info.clone(), account1_info.clone()],
4518        )
4519        .unwrap();
4520
4521        // set close_authority when currently self
4522        let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap();
4523        account.close_authority = COption::Some(account1_key);
4524        Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap();
4525
4526        do_process_instruction_dups(
4527            set_authority(
4528                &program_id,
4529                &account1_key,
4530                Some(&owner_key),
4531                AuthorityType::CloseAccount,
4532                &account1_key,
4533                &[],
4534            )
4535            .unwrap(),
4536            vec![account1_info.clone(), account1_info.clone()],
4537        )
4538        .unwrap();
4539    }
4540
4541    #[test]
4542    fn test_set_authority() {
4543        let program_id = crate::id();
4544        let account_key = Pubkey::new_unique();
4545        let mut account_account = SolanaAccount::new(
4546            account_minimum_balance(),
4547            Account::get_packed_len(),
4548            &program_id,
4549        );
4550        let account2_key = Pubkey::new_unique();
4551        let mut account2_account = SolanaAccount::new(
4552            account_minimum_balance(),
4553            Account::get_packed_len(),
4554            &program_id,
4555        );
4556        let owner_key = Pubkey::new_unique();
4557        let mut owner_account = SolanaAccount::default();
4558        let owner2_key = Pubkey::new_unique();
4559        let mut owner2_account = SolanaAccount::default();
4560        let owner3_key = Pubkey::new_unique();
4561        let mut owner3_account = SolanaAccount::default();
4562        let mint_key = Pubkey::new_unique();
4563        let mut mint_account =
4564            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
4565        let mint2_key = Pubkey::new_unique();
4566        let mut mint2_account =
4567            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
4568        let mut rent_sysvar = rent_sysvar();
4569
4570        // create new mint with owner
4571        do_process_instruction(
4572            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
4573            vec![&mut mint_account, &mut rent_sysvar],
4574        )
4575        .unwrap();
4576
4577        // create mint with owner and freeze_authority
4578        do_process_instruction(
4579            initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(),
4580            vec![&mut mint2_account, &mut rent_sysvar],
4581        )
4582        .unwrap();
4583
4584        // invalid account
4585        assert_eq!(
4586            Err(ProgramError::InvalidAccountData),
4587            do_process_instruction(
4588                set_authority(
4589                    &program_id,
4590                    &account_key,
4591                    Some(&owner2_key),
4592                    AuthorityType::AccountOwner,
4593                    &owner_key,
4594                    &[]
4595                )
4596                .unwrap(),
4597                vec![&mut account_account, &mut owner_account],
4598            )
4599        );
4600
4601        // create account
4602        do_process_instruction(
4603            initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
4604            vec![
4605                &mut account_account,
4606                &mut mint_account,
4607                &mut owner_account,
4608                &mut rent_sysvar,
4609            ],
4610        )
4611        .unwrap();
4612
4613        // create another account
4614        do_process_instruction(
4615            initialize_account(&program_id, &account2_key, &mint2_key, &owner_key).unwrap(),
4616            vec![
4617                &mut account2_account,
4618                &mut mint2_account,
4619                &mut owner_account,
4620                &mut rent_sysvar,
4621            ],
4622        )
4623        .unwrap();
4624
4625        // missing owner
4626        assert_eq!(
4627            Err(TokenError::OwnerMismatch.into()),
4628            do_process_instruction(
4629                set_authority(
4630                    &program_id,
4631                    &account_key,
4632                    Some(&owner_key),
4633                    AuthorityType::AccountOwner,
4634                    &owner2_key,
4635                    &[]
4636                )
4637                .unwrap(),
4638                vec![&mut account_account, &mut owner2_account],
4639            )
4640        );
4641
4642        // owner did not sign
4643        let mut instruction = set_authority(
4644            &program_id,
4645            &account_key,
4646            Some(&owner2_key),
4647            AuthorityType::AccountOwner,
4648            &owner_key,
4649            &[],
4650        )
4651        .unwrap();
4652        instruction.accounts[1].is_signer = false;
4653        assert_eq!(
4654            Err(ProgramError::MissingRequiredSignature),
4655            do_process_instruction(instruction, vec![&mut account_account, &mut owner_account,],)
4656        );
4657
4658        // wrong authority type
4659        assert_eq!(
4660            Err(TokenError::AuthorityTypeNotSupported.into()),
4661            do_process_instruction(
4662                set_authority(
4663                    &program_id,
4664                    &account_key,
4665                    Some(&owner2_key),
4666                    AuthorityType::FreezeAccount,
4667                    &owner_key,
4668                    &[],
4669                )
4670                .unwrap(),
4671                vec![&mut account_account, &mut owner_account],
4672            )
4673        );
4674
4675        // account owner may not be set to None
4676        assert_eq!(
4677            Err(TokenError::InvalidInstruction.into()),
4678            do_process_instruction(
4679                set_authority(
4680                    &program_id,
4681                    &account_key,
4682                    None,
4683                    AuthorityType::AccountOwner,
4684                    &owner_key,
4685                    &[],
4686                )
4687                .unwrap(),
4688                vec![&mut account_account, &mut owner_account],
4689            )
4690        );
4691
4692        // set delegate
4693        do_process_instruction(
4694            approve(
4695                &program_id,
4696                &account_key,
4697                &owner2_key,
4698                &owner_key,
4699                &[],
4700                u64::MAX,
4701            )
4702            .unwrap(),
4703            vec![
4704                &mut account_account,
4705                &mut owner2_account,
4706                &mut owner_account,
4707            ],
4708        )
4709        .unwrap();
4710        let account = Account::unpack_unchecked(&account_account.data).unwrap();
4711        assert_eq!(account.delegate, COption::Some(owner2_key));
4712        assert_eq!(account.delegated_amount, u64::MAX);
4713
4714        // set owner
4715        do_process_instruction(
4716            set_authority(
4717                &program_id,
4718                &account_key,
4719                Some(&owner3_key),
4720                AuthorityType::AccountOwner,
4721                &owner_key,
4722                &[],
4723            )
4724            .unwrap(),
4725            vec![&mut account_account, &mut owner_account],
4726        )
4727        .unwrap();
4728
4729        // check delegate cleared
4730        let account = Account::unpack_unchecked(&account_account.data).unwrap();
4731        assert_eq!(account.delegate, COption::None);
4732        assert_eq!(account.delegated_amount, 0);
4733
4734        // set owner without existing delegate
4735        do_process_instruction(
4736            set_authority(
4737                &program_id,
4738                &account_key,
4739                Some(&owner2_key),
4740                AuthorityType::AccountOwner,
4741                &owner3_key,
4742                &[],
4743            )
4744            .unwrap(),
4745            vec![&mut account_account, &mut owner3_account],
4746        )
4747        .unwrap();
4748
4749        // set close_authority
4750        do_process_instruction(
4751            set_authority(
4752                &program_id,
4753                &account_key,
4754                Some(&owner2_key),
4755                AuthorityType::CloseAccount,
4756                &owner2_key,
4757                &[],
4758            )
4759            .unwrap(),
4760            vec![&mut account_account, &mut owner2_account],
4761        )
4762        .unwrap();
4763
4764        // close_authority may be set to None
4765        do_process_instruction(
4766            set_authority(
4767                &program_id,
4768                &account_key,
4769                None,
4770                AuthorityType::CloseAccount,
4771                &owner2_key,
4772                &[],
4773            )
4774            .unwrap(),
4775            vec![&mut account_account, &mut owner2_account],
4776        )
4777        .unwrap();
4778
4779        // wrong owner
4780        assert_eq!(
4781            Err(TokenError::OwnerMismatch.into()),
4782            do_process_instruction(
4783                set_authority(
4784                    &program_id,
4785                    &mint_key,
4786                    Some(&owner3_key),
4787                    AuthorityType::MintTokens,
4788                    &owner2_key,
4789                    &[]
4790                )
4791                .unwrap(),
4792                vec![&mut mint_account, &mut owner2_account],
4793            )
4794        );
4795
4796        // owner did not sign
4797        let mut instruction = set_authority(
4798            &program_id,
4799            &mint_key,
4800            Some(&owner2_key),
4801            AuthorityType::MintTokens,
4802            &owner_key,
4803            &[],
4804        )
4805        .unwrap();
4806        instruction.accounts[1].is_signer = false;
4807        assert_eq!(
4808            Err(ProgramError::MissingRequiredSignature),
4809            do_process_instruction(instruction, vec![&mut mint_account, &mut owner_account],)
4810        );
4811
4812        // cannot freeze
4813        assert_eq!(
4814            Err(TokenError::MintCannotFreeze.into()),
4815            do_process_instruction(
4816                set_authority(
4817                    &program_id,
4818                    &mint_key,
4819                    Some(&owner2_key),
4820                    AuthorityType::FreezeAccount,
4821                    &owner_key,
4822                    &[],
4823                )
4824                .unwrap(),
4825                vec![&mut mint_account, &mut owner_account],
4826            )
4827        );
4828
4829        // set owner
4830        do_process_instruction(
4831            set_authority(
4832                &program_id,
4833                &mint_key,
4834                Some(&owner2_key),
4835                AuthorityType::MintTokens,
4836                &owner_key,
4837                &[],
4838            )
4839            .unwrap(),
4840            vec![&mut mint_account, &mut owner_account],
4841        )
4842        .unwrap();
4843
4844        // set owner to None
4845        do_process_instruction(
4846            set_authority(
4847                &program_id,
4848                &mint_key,
4849                None,
4850                AuthorityType::MintTokens,
4851                &owner2_key,
4852                &[],
4853            )
4854            .unwrap(),
4855            vec![&mut mint_account, &mut owner2_account],
4856        )
4857        .unwrap();
4858
4859        // test unsetting mint_authority is one-way operation
4860        assert_eq!(
4861            Err(TokenError::FixedSupply.into()),
4862            do_process_instruction(
4863                set_authority(
4864                    &program_id,
4865                    &mint2_key,
4866                    Some(&owner2_key),
4867                    AuthorityType::MintTokens,
4868                    &owner_key,
4869                    &[]
4870                )
4871                .unwrap(),
4872                vec![&mut mint_account, &mut owner_account],
4873            )
4874        );
4875
4876        // set freeze_authority
4877        do_process_instruction(
4878            set_authority(
4879                &program_id,
4880                &mint2_key,
4881                Some(&owner2_key),
4882                AuthorityType::FreezeAccount,
4883                &owner_key,
4884                &[],
4885            )
4886            .unwrap(),
4887            vec![&mut mint2_account, &mut owner_account],
4888        )
4889        .unwrap();
4890
4891        // test unsetting freeze_authority is one-way operation
4892        do_process_instruction(
4893            set_authority(
4894                &program_id,
4895                &mint2_key,
4896                None,
4897                AuthorityType::FreezeAccount,
4898                &owner2_key,
4899                &[],
4900            )
4901            .unwrap(),
4902            vec![&mut mint2_account, &mut owner2_account],
4903        )
4904        .unwrap();
4905
4906        assert_eq!(
4907            Err(TokenError::MintCannotFreeze.into()),
4908            do_process_instruction(
4909                set_authority(
4910                    &program_id,
4911                    &mint2_key,
4912                    Some(&owner2_key),
4913                    AuthorityType::FreezeAccount,
4914                    &owner_key,
4915                    &[],
4916                )
4917                .unwrap(),
4918                vec![&mut mint2_account, &mut owner2_account],
4919            )
4920        );
4921    }
4922
4923    #[test]
4924    fn test_set_authority_with_immutable_owner_extension() {
4925        let program_id = crate::id();
4926        let account_key = Pubkey::new_unique();
4927
4928        let account_len =
4929            ExtensionType::try_calculate_account_len::<Account>(&[ExtensionType::ImmutableOwner])
4930                .unwrap();
4931        let mut account_account = SolanaAccount::new(
4932            Rent::default().minimum_balance(account_len),
4933            account_len,
4934            &program_id,
4935        );
4936        let owner_key = Pubkey::new_unique();
4937        let mut owner_account = SolanaAccount::default();
4938        let owner2_key = Pubkey::new_unique();
4939
4940        let mint_key = Pubkey::new_unique();
4941        let mut mint_account =
4942            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
4943        let mut rent_sysvar = rent_sysvar();
4944
4945        // create mint
4946        assert_eq!(
4947            Ok(()),
4948            do_process_instruction(
4949                initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
4950                vec![&mut mint_account, &mut rent_sysvar],
4951            )
4952        );
4953
4954        // create account
4955        assert_eq!(
4956            Ok(()),
4957            do_process_instruction(
4958                initialize_immutable_owner(&program_id, &account_key).unwrap(),
4959                vec![&mut account_account],
4960            )
4961        );
4962        assert_eq!(
4963            Ok(()),
4964            do_process_instruction(
4965                initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
4966                vec![
4967                    &mut account_account,
4968                    &mut mint_account,
4969                    &mut owner_account,
4970                    &mut rent_sysvar,
4971                ],
4972            )
4973        );
4974
4975        // Immutable Owner extension blocks account owner authority changes
4976        assert_eq!(
4977            Err(TokenError::ImmutableOwner.into()),
4978            do_process_instruction(
4979                set_authority(
4980                    &program_id,
4981                    &account_key,
4982                    Some(&owner2_key),
4983                    AuthorityType::AccountOwner,
4984                    &owner_key,
4985                    &[],
4986                )
4987                .unwrap(),
4988                vec![&mut account_account, &mut owner_account],
4989            )
4990        );
4991    }
4992
4993    #[test]
4994    fn test_mint_to_dups() {
4995        let program_id = crate::id();
4996        let account1_key = Pubkey::new_unique();
4997        let mut account1_account = SolanaAccount::new(
4998            account_minimum_balance(),
4999            Account::get_packed_len(),
5000            &program_id,
5001        );
5002        let account1_info: AccountInfo<'_> = (&account1_key, true, &mut account1_account).into();
5003        let owner_key = Pubkey::new_unique();
5004        let mut owner_account = SolanaAccount::default();
5005        let owner_info: AccountInfo<'_> = (&owner_key, true, &mut owner_account).into();
5006        let mint_key = Pubkey::new_unique();
5007        let mut mint_account =
5008            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
5009        let mint_info: AccountInfo<'_> = (&mint_key, true, &mut mint_account).into();
5010        let rent_key = rent::id();
5011        let mut rent_sysvar = rent_sysvar();
5012        let rent_info: AccountInfo<'_> = (&rent_key, false, &mut rent_sysvar).into();
5013
5014        // create mint
5015        do_process_instruction_dups(
5016            initialize_mint(&program_id, &mint_key, &mint_key, None, 2).unwrap(),
5017            vec![mint_info.clone(), rent_info.clone()],
5018        )
5019        .unwrap();
5020
5021        // create account
5022        do_process_instruction_dups(
5023            initialize_account(&program_id, &account1_key, &mint_key, &owner_key).unwrap(),
5024            vec![
5025                account1_info.clone(),
5026                mint_info.clone(),
5027                owner_info.clone(),
5028                rent_info.clone(),
5029            ],
5030        )
5031        .unwrap();
5032
5033        // mint_to when mint_authority is self
5034        do_process_instruction_dups(
5035            mint_to(&program_id, &mint_key, &account1_key, &mint_key, &[], 42).unwrap(),
5036            vec![mint_info.clone(), account1_info.clone(), mint_info.clone()],
5037        )
5038        .unwrap();
5039
5040        // mint_to_checked when mint_authority is self
5041        do_process_instruction_dups(
5042            mint_to_checked(&program_id, &mint_key, &account1_key, &mint_key, &[], 42, 2).unwrap(),
5043            vec![mint_info.clone(), account1_info.clone(), mint_info.clone()],
5044        )
5045        .unwrap();
5046
5047        // mint_to when mint_authority is account owner
5048        let mut mint = Mint::unpack_unchecked(&mint_info.data.borrow()).unwrap();
5049        mint.mint_authority = COption::Some(account1_key);
5050        Mint::pack(mint, &mut mint_info.data.borrow_mut()).unwrap();
5051        do_process_instruction_dups(
5052            mint_to(
5053                &program_id,
5054                &mint_key,
5055                &account1_key,
5056                &account1_key,
5057                &[],
5058                42,
5059            )
5060            .unwrap(),
5061            vec![
5062                mint_info.clone(),
5063                account1_info.clone(),
5064                account1_info.clone(),
5065            ],
5066        )
5067        .unwrap();
5068
5069        // mint_to_checked when mint_authority is account owner
5070        do_process_instruction_dups(
5071            mint_to(
5072                &program_id,
5073                &mint_key,
5074                &account1_key,
5075                &account1_key,
5076                &[],
5077                42,
5078            )
5079            .unwrap(),
5080            vec![
5081                mint_info.clone(),
5082                account1_info.clone(),
5083                account1_info.clone(),
5084            ],
5085        )
5086        .unwrap();
5087    }
5088
5089    #[test]
5090    fn test_mint_to() {
5091        let program_id = crate::id();
5092        let account_key = Pubkey::new_unique();
5093        let mut account_account = SolanaAccount::new(
5094            account_minimum_balance(),
5095            Account::get_packed_len(),
5096            &program_id,
5097        );
5098        let account2_key = Pubkey::new_unique();
5099        let mut account2_account = SolanaAccount::new(
5100            account_minimum_balance(),
5101            Account::get_packed_len(),
5102            &program_id,
5103        );
5104        let account3_key = Pubkey::new_unique();
5105        let mut account3_account = SolanaAccount::new(
5106            account_minimum_balance(),
5107            Account::get_packed_len(),
5108            &program_id,
5109        );
5110        let mismatch_key = Pubkey::new_unique();
5111        let mut mismatch_account = SolanaAccount::new(
5112            account_minimum_balance(),
5113            Account::get_packed_len(),
5114            &program_id,
5115        );
5116        let owner_key = Pubkey::new_unique();
5117        let mut owner_account = SolanaAccount::default();
5118        let owner2_key = Pubkey::new_unique();
5119        let mut owner2_account = SolanaAccount::default();
5120        let mint_key = Pubkey::new_unique();
5121        let mut mint_account =
5122            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
5123        let mint2_key = Pubkey::new_unique();
5124        let uninitialized_key = Pubkey::new_unique();
5125        let mut uninitialized_account = SolanaAccount::new(
5126            account_minimum_balance(),
5127            Account::get_packed_len(),
5128            &program_id,
5129        );
5130        let mut rent_sysvar = rent_sysvar();
5131
5132        // create new mint with owner
5133        do_process_instruction(
5134            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
5135            vec![&mut mint_account, &mut rent_sysvar],
5136        )
5137        .unwrap();
5138
5139        // create account
5140        do_process_instruction(
5141            initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
5142            vec![
5143                &mut account_account,
5144                &mut mint_account,
5145                &mut owner_account,
5146                &mut rent_sysvar,
5147            ],
5148        )
5149        .unwrap();
5150
5151        // create another account
5152        do_process_instruction(
5153            initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(),
5154            vec![
5155                &mut account2_account,
5156                &mut mint_account,
5157                &mut owner_account,
5158                &mut rent_sysvar,
5159            ],
5160        )
5161        .unwrap();
5162
5163        // create another account
5164        do_process_instruction(
5165            initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(),
5166            vec![
5167                &mut account3_account,
5168                &mut mint_account,
5169                &mut owner_account,
5170                &mut rent_sysvar,
5171            ],
5172        )
5173        .unwrap();
5174
5175        // create mismatch account
5176        do_process_instruction(
5177            initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(),
5178            vec![
5179                &mut mismatch_account,
5180                &mut mint_account,
5181                &mut owner_account,
5182                &mut rent_sysvar,
5183            ],
5184        )
5185        .unwrap();
5186        let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap();
5187        account.mint = mint2_key;
5188        Account::pack(account, &mut mismatch_account.data).unwrap();
5189
5190        // mint to
5191        do_process_instruction(
5192            mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(),
5193            vec![&mut mint_account, &mut account_account, &mut owner_account],
5194        )
5195        .unwrap();
5196
5197        let mint = Mint::unpack_unchecked(&mint_account.data).unwrap();
5198        assert_eq!(mint.supply, 42);
5199        let account = Account::unpack_unchecked(&account_account.data).unwrap();
5200        assert_eq!(account.amount, 42);
5201
5202        // mint to another account to test supply accumulation
5203        do_process_instruction(
5204            mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(),
5205            vec![&mut mint_account, &mut account2_account, &mut owner_account],
5206        )
5207        .unwrap();
5208
5209        let mint = Mint::unpack_unchecked(&mint_account.data).unwrap();
5210        assert_eq!(mint.supply, 84);
5211        let account = Account::unpack_unchecked(&account2_account.data).unwrap();
5212        assert_eq!(account.amount, 42);
5213
5214        // missing signer
5215        let mut instruction =
5216            mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap();
5217        instruction.accounts[2].is_signer = false;
5218        assert_eq!(
5219            Err(ProgramError::MissingRequiredSignature),
5220            do_process_instruction(
5221                instruction,
5222                vec![&mut mint_account, &mut account2_account, &mut owner_account],
5223            )
5224        );
5225
5226        // mismatch account
5227        assert_eq!(
5228            Err(TokenError::MintMismatch.into()),
5229            do_process_instruction(
5230                mint_to(&program_id, &mint_key, &mismatch_key, &owner_key, &[], 42).unwrap(),
5231                vec![&mut mint_account, &mut mismatch_account, &mut owner_account],
5232            )
5233        );
5234
5235        // missing owner
5236        assert_eq!(
5237            Err(TokenError::OwnerMismatch.into()),
5238            do_process_instruction(
5239                mint_to(&program_id, &mint_key, &account2_key, &owner2_key, &[], 42).unwrap(),
5240                vec![
5241                    &mut mint_account,
5242                    &mut account2_account,
5243                    &mut owner2_account,
5244                ],
5245            )
5246        );
5247
5248        // mint not owned by program
5249        let not_program_id = Pubkey::new_unique();
5250        mint_account.owner = not_program_id;
5251        assert_eq!(
5252            Err(ProgramError::IncorrectProgramId),
5253            do_process_instruction(
5254                mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 0).unwrap(),
5255                vec![&mut mint_account, &mut account_account, &mut owner_account],
5256            )
5257        );
5258        mint_account.owner = program_id;
5259
5260        // account not owned by program
5261        let not_program_id = Pubkey::new_unique();
5262        account_account.owner = not_program_id;
5263        assert_eq!(
5264            Err(ProgramError::IncorrectProgramId),
5265            do_process_instruction(
5266                mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 0).unwrap(),
5267                vec![&mut mint_account, &mut account_account, &mut owner_account],
5268            )
5269        );
5270        account_account.owner = program_id;
5271
5272        // uninitialized destination account
5273        assert_eq!(
5274            Err(ProgramError::UninitializedAccount),
5275            do_process_instruction(
5276                mint_to(
5277                    &program_id,
5278                    &mint_key,
5279                    &uninitialized_key,
5280                    &owner_key,
5281                    &[],
5282                    42
5283                )
5284                .unwrap(),
5285                vec![
5286                    &mut mint_account,
5287                    &mut uninitialized_account,
5288                    &mut owner_account,
5289                ],
5290            )
5291        );
5292
5293        // unset mint_authority and test minting fails
5294        do_process_instruction(
5295            set_authority(
5296                &program_id,
5297                &mint_key,
5298                None,
5299                AuthorityType::MintTokens,
5300                &owner_key,
5301                &[],
5302            )
5303            .unwrap(),
5304            vec![&mut mint_account, &mut owner_account],
5305        )
5306        .unwrap();
5307        assert_eq!(
5308            Err(TokenError::FixedSupply.into()),
5309            do_process_instruction(
5310                mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(),
5311                vec![&mut mint_account, &mut account2_account, &mut owner_account],
5312            )
5313        );
5314    }
5315
5316    #[test]
5317    fn test_burn_dups() {
5318        let program_id = crate::id();
5319        let account1_key = Pubkey::new_unique();
5320        let mut account1_account = SolanaAccount::new(
5321            account_minimum_balance(),
5322            Account::get_packed_len(),
5323            &program_id,
5324        );
5325        let account1_info: AccountInfo<'_> = (&account1_key, true, &mut account1_account).into();
5326        let owner_key = Pubkey::new_unique();
5327        let mut owner_account = SolanaAccount::default();
5328        let owner_info: AccountInfo<'_> = (&owner_key, true, &mut owner_account).into();
5329        let mint_key = Pubkey::new_unique();
5330        let mut mint_account =
5331            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
5332        let mint_info: AccountInfo<'_> = (&mint_key, true, &mut mint_account).into();
5333        let rent_key = rent::id();
5334        let mut rent_sysvar = rent_sysvar();
5335        let rent_info: AccountInfo<'_> = (&rent_key, false, &mut rent_sysvar).into();
5336
5337        // create mint
5338        do_process_instruction_dups(
5339            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
5340            vec![mint_info.clone(), rent_info.clone()],
5341        )
5342        .unwrap();
5343
5344        // create account
5345        do_process_instruction_dups(
5346            initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(),
5347            vec![
5348                account1_info.clone(),
5349                mint_info.clone(),
5350                account1_info.clone(),
5351                rent_info.clone(),
5352            ],
5353        )
5354        .unwrap();
5355
5356        // mint to account
5357        do_process_instruction_dups(
5358            mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(),
5359            vec![mint_info.clone(), account1_info.clone(), owner_info.clone()],
5360        )
5361        .unwrap();
5362
5363        // source-owner burn
5364        do_process_instruction_dups(
5365            burn(
5366                &program_id,
5367                &mint_key,
5368                &account1_key,
5369                &account1_key,
5370                &[],
5371                500,
5372            )
5373            .unwrap(),
5374            vec![
5375                account1_info.clone(),
5376                mint_info.clone(),
5377                account1_info.clone(),
5378            ],
5379        )
5380        .unwrap();
5381
5382        // source-owner burn_checked
5383        do_process_instruction_dups(
5384            burn_checked(
5385                &program_id,
5386                &account1_key,
5387                &mint_key,
5388                &account1_key,
5389                &[],
5390                500,
5391                2,
5392            )
5393            .unwrap(),
5394            vec![
5395                account1_info.clone(),
5396                mint_info.clone(),
5397                account1_info.clone(),
5398            ],
5399        )
5400        .unwrap();
5401
5402        // mint-owner burn
5403        do_process_instruction_dups(
5404            mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(),
5405            vec![mint_info.clone(), account1_info.clone(), owner_info.clone()],
5406        )
5407        .unwrap();
5408        let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap();
5409        account.owner = mint_key;
5410        Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap();
5411        do_process_instruction_dups(
5412            burn(&program_id, &account1_key, &mint_key, &mint_key, &[], 500).unwrap(),
5413            vec![account1_info.clone(), mint_info.clone(), mint_info.clone()],
5414        )
5415        .unwrap();
5416
5417        // mint-owner burn_checked
5418        do_process_instruction_dups(
5419            burn_checked(
5420                &program_id,
5421                &account1_key,
5422                &mint_key,
5423                &mint_key,
5424                &[],
5425                500,
5426                2,
5427            )
5428            .unwrap(),
5429            vec![account1_info.clone(), mint_info.clone(), mint_info.clone()],
5430        )
5431        .unwrap();
5432
5433        // source-delegate burn
5434        do_process_instruction_dups(
5435            mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(),
5436            vec![mint_info.clone(), account1_info.clone(), owner_info.clone()],
5437        )
5438        .unwrap();
5439        let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap();
5440        account.delegated_amount = 1000;
5441        account.delegate = COption::Some(account1_key);
5442        account.owner = owner_key;
5443        Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap();
5444        do_process_instruction_dups(
5445            burn(
5446                &program_id,
5447                &account1_key,
5448                &mint_key,
5449                &account1_key,
5450                &[],
5451                500,
5452            )
5453            .unwrap(),
5454            vec![
5455                account1_info.clone(),
5456                mint_info.clone(),
5457                account1_info.clone(),
5458            ],
5459        )
5460        .unwrap();
5461
5462        // source-delegate burn_checked
5463        do_process_instruction_dups(
5464            burn_checked(
5465                &program_id,
5466                &account1_key,
5467                &mint_key,
5468                &account1_key,
5469                &[],
5470                500,
5471                2,
5472            )
5473            .unwrap(),
5474            vec![
5475                account1_info.clone(),
5476                mint_info.clone(),
5477                account1_info.clone(),
5478            ],
5479        )
5480        .unwrap();
5481
5482        // mint-delegate burn
5483        do_process_instruction_dups(
5484            mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(),
5485            vec![mint_info.clone(), account1_info.clone(), owner_info.clone()],
5486        )
5487        .unwrap();
5488        let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap();
5489        account.delegated_amount = 1000;
5490        account.delegate = COption::Some(mint_key);
5491        account.owner = owner_key;
5492        Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap();
5493        do_process_instruction_dups(
5494            burn(&program_id, &account1_key, &mint_key, &mint_key, &[], 500).unwrap(),
5495            vec![account1_info.clone(), mint_info.clone(), mint_info.clone()],
5496        )
5497        .unwrap();
5498
5499        // mint-delegate burn_checked
5500        do_process_instruction_dups(
5501            burn_checked(
5502                &program_id,
5503                &account1_key,
5504                &mint_key,
5505                &mint_key,
5506                &[],
5507                500,
5508                2,
5509            )
5510            .unwrap(),
5511            vec![account1_info.clone(), mint_info.clone(), mint_info.clone()],
5512        )
5513        .unwrap();
5514    }
5515
5516    #[test]
5517    fn test_burn() {
5518        let program_id = crate::id();
5519        let account_key = Pubkey::new_unique();
5520        let mut account_account = SolanaAccount::new(
5521            account_minimum_balance(),
5522            Account::get_packed_len(),
5523            &program_id,
5524        );
5525        let account2_key = Pubkey::new_unique();
5526        let mut account2_account = SolanaAccount::new(
5527            account_minimum_balance(),
5528            Account::get_packed_len(),
5529            &program_id,
5530        );
5531        let account3_key = Pubkey::new_unique();
5532        let mut account3_account = SolanaAccount::new(
5533            account_minimum_balance(),
5534            Account::get_packed_len(),
5535            &program_id,
5536        );
5537        let delegate_key = Pubkey::new_unique();
5538        let mut delegate_account = SolanaAccount::default();
5539        let mismatch_key = Pubkey::new_unique();
5540        let mut mismatch_account = SolanaAccount::new(
5541            account_minimum_balance(),
5542            Account::get_packed_len(),
5543            &program_id,
5544        );
5545        let owner_key = Pubkey::new_unique();
5546        let mut owner_account = SolanaAccount::default();
5547        let owner2_key = Pubkey::new_unique();
5548        let mut owner2_account = SolanaAccount::default();
5549        let mint_key = Pubkey::new_unique();
5550        let mut mint_account =
5551            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
5552        let mint2_key = Pubkey::new_unique();
5553        let mut rent_sysvar = rent_sysvar();
5554
5555        // create new mint
5556        do_process_instruction(
5557            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
5558            vec![&mut mint_account, &mut rent_sysvar],
5559        )
5560        .unwrap();
5561
5562        // create account
5563        do_process_instruction(
5564            initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
5565            vec![
5566                &mut account_account,
5567                &mut mint_account,
5568                &mut owner_account,
5569                &mut rent_sysvar,
5570            ],
5571        )
5572        .unwrap();
5573
5574        // create another account
5575        do_process_instruction(
5576            initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(),
5577            vec![
5578                &mut account2_account,
5579                &mut mint_account,
5580                &mut owner_account,
5581                &mut rent_sysvar,
5582            ],
5583        )
5584        .unwrap();
5585
5586        // create another account
5587        do_process_instruction(
5588            initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(),
5589            vec![
5590                &mut account3_account,
5591                &mut mint_account,
5592                &mut owner_account,
5593                &mut rent_sysvar,
5594            ],
5595        )
5596        .unwrap();
5597
5598        // create mismatch account
5599        do_process_instruction(
5600            initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(),
5601            vec![
5602                &mut mismatch_account,
5603                &mut mint_account,
5604                &mut owner_account,
5605                &mut rent_sysvar,
5606            ],
5607        )
5608        .unwrap();
5609
5610        // mint to account
5611        do_process_instruction(
5612            mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(),
5613            vec![&mut mint_account, &mut account_account, &mut owner_account],
5614        )
5615        .unwrap();
5616
5617        // mint to mismatch account and change mint key
5618        do_process_instruction(
5619            mint_to(&program_id, &mint_key, &mismatch_key, &owner_key, &[], 1000).unwrap(),
5620            vec![&mut mint_account, &mut mismatch_account, &mut owner_account],
5621        )
5622        .unwrap();
5623        let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap();
5624        account.mint = mint2_key;
5625        Account::pack(account, &mut mismatch_account.data).unwrap();
5626
5627        // missing signer
5628        let mut instruction =
5629            burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 42).unwrap();
5630        instruction.accounts[1].is_signer = false;
5631        assert_eq!(
5632            Err(TokenError::OwnerMismatch.into()),
5633            do_process_instruction(
5634                instruction,
5635                vec![
5636                    &mut account_account,
5637                    &mut mint_account,
5638                    &mut delegate_account
5639                ],
5640            )
5641        );
5642
5643        // missing owner
5644        assert_eq!(
5645            Err(TokenError::OwnerMismatch.into()),
5646            do_process_instruction(
5647                burn(&program_id, &account_key, &mint_key, &owner2_key, &[], 42).unwrap(),
5648                vec![&mut account_account, &mut mint_account, &mut owner2_account],
5649            )
5650        );
5651
5652        // account not owned by program
5653        let not_program_id = Pubkey::new_unique();
5654        account_account.owner = not_program_id;
5655        assert_eq!(
5656            Err(ProgramError::IncorrectProgramId),
5657            do_process_instruction(
5658                burn(&program_id, &account_key, &mint_key, &owner_key, &[], 0).unwrap(),
5659                vec![&mut account_account, &mut mint_account, &mut owner_account],
5660            )
5661        );
5662        account_account.owner = program_id;
5663
5664        // mint not owned by program
5665        let not_program_id = Pubkey::new_unique();
5666        mint_account.owner = not_program_id;
5667        assert_eq!(
5668            Err(ProgramError::IncorrectProgramId),
5669            do_process_instruction(
5670                burn(&program_id, &account_key, &mint_key, &owner_key, &[], 0).unwrap(),
5671                vec![&mut account_account, &mut mint_account, &mut owner_account],
5672            )
5673        );
5674        mint_account.owner = program_id;
5675
5676        // mint mismatch
5677        assert_eq!(
5678            Err(TokenError::MintMismatch.into()),
5679            do_process_instruction(
5680                burn(&program_id, &mismatch_key, &mint_key, &owner_key, &[], 42).unwrap(),
5681                vec![&mut mismatch_account, &mut mint_account, &mut owner_account],
5682            )
5683        );
5684
5685        // burn
5686        do_process_instruction(
5687            burn(&program_id, &account_key, &mint_key, &owner_key, &[], 21).unwrap(),
5688            vec![&mut account_account, &mut mint_account, &mut owner_account],
5689        )
5690        .unwrap();
5691
5692        // burn_checked, with incorrect decimals
5693        assert_eq!(
5694            Err(TokenError::MintDecimalsMismatch.into()),
5695            do_process_instruction(
5696                burn_checked(&program_id, &account_key, &mint_key, &owner_key, &[], 21, 3).unwrap(),
5697                vec![&mut account_account, &mut mint_account, &mut owner_account],
5698            )
5699        );
5700
5701        // burn_checked
5702        do_process_instruction(
5703            burn_checked(&program_id, &account_key, &mint_key, &owner_key, &[], 21, 2).unwrap(),
5704            vec![&mut account_account, &mut mint_account, &mut owner_account],
5705        )
5706        .unwrap();
5707
5708        let mint = Mint::unpack_unchecked(&mint_account.data).unwrap();
5709        assert_eq!(mint.supply, 2000 - 42);
5710        let account = Account::unpack_unchecked(&account_account.data).unwrap();
5711        assert_eq!(account.amount, 1000 - 42);
5712
5713        // insufficient funds
5714        assert_eq!(
5715            Err(TokenError::InsufficientFunds.into()),
5716            do_process_instruction(
5717                burn(
5718                    &program_id,
5719                    &account_key,
5720                    &mint_key,
5721                    &owner_key,
5722                    &[],
5723                    100_000_000
5724                )
5725                .unwrap(),
5726                vec![&mut account_account, &mut mint_account, &mut owner_account],
5727            )
5728        );
5729
5730        // approve delegate
5731        do_process_instruction(
5732            approve(
5733                &program_id,
5734                &account_key,
5735                &delegate_key,
5736                &owner_key,
5737                &[],
5738                84,
5739            )
5740            .unwrap(),
5741            vec![
5742                &mut account_account,
5743                &mut delegate_account,
5744                &mut owner_account,
5745            ],
5746        )
5747        .unwrap();
5748
5749        // not a delegate of source account
5750        assert_eq!(
5751            Err(TokenError::OwnerMismatch.into()),
5752            do_process_instruction(
5753                burn(
5754                    &program_id,
5755                    &account_key,
5756                    &mint_key,
5757                    &owner2_key, // <-- incorrect owner or delegate
5758                    &[],
5759                    1,
5760                )
5761                .unwrap(),
5762                vec![&mut account_account, &mut mint_account, &mut owner2_account],
5763            )
5764        );
5765
5766        // insufficient funds approved via delegate
5767        assert_eq!(
5768            Err(TokenError::InsufficientFunds.into()),
5769            do_process_instruction(
5770                burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 85).unwrap(),
5771                vec![
5772                    &mut account_account,
5773                    &mut mint_account,
5774                    &mut delegate_account
5775                ],
5776            )
5777        );
5778
5779        // burn via delegate
5780        do_process_instruction(
5781            burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 84).unwrap(),
5782            vec![
5783                &mut account_account,
5784                &mut mint_account,
5785                &mut delegate_account,
5786            ],
5787        )
5788        .unwrap();
5789
5790        // match
5791        let mint = Mint::unpack_unchecked(&mint_account.data).unwrap();
5792        assert_eq!(mint.supply, 2000 - 42 - 84);
5793        let account = Account::unpack_unchecked(&account_account.data).unwrap();
5794        assert_eq!(account.amount, 1000 - 42 - 84);
5795
5796        // insufficient funds approved via delegate
5797        assert_eq!(
5798            Err(TokenError::OwnerMismatch.into()),
5799            do_process_instruction(
5800                burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 1).unwrap(),
5801                vec![
5802                    &mut account_account,
5803                    &mut mint_account,
5804                    &mut delegate_account
5805                ],
5806            )
5807        );
5808    }
5809
5810    #[test]
5811    fn test_multisig() {
5812        let program_id = crate::id();
5813        let mint_key = Pubkey::new_unique();
5814        let mut mint_account =
5815            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
5816        let account_key = Pubkey::new_unique();
5817        let mut account = SolanaAccount::new(
5818            account_minimum_balance(),
5819            Account::get_packed_len(),
5820            &program_id,
5821        );
5822        let account2_key = Pubkey::new_unique();
5823        let mut account2_account = SolanaAccount::new(
5824            account_minimum_balance(),
5825            Account::get_packed_len(),
5826            &program_id,
5827        );
5828        let owner_key = Pubkey::new_unique();
5829        let mut owner_account = SolanaAccount::default();
5830        let multisig_key = Pubkey::new_unique();
5831        let mut multisig_account = SolanaAccount::new(42, Multisig::get_packed_len(), &program_id);
5832        let multisig_delegate_key = Pubkey::new_unique();
5833        let mut multisig_delegate_account = SolanaAccount::new(
5834            multisig_minimum_balance(),
5835            Multisig::get_packed_len(),
5836            &program_id,
5837        );
5838        let signer_keys = vec![Pubkey::new_unique(); MAX_SIGNERS];
5839        let signer_key_refs: Vec<&Pubkey> = signer_keys.iter().collect();
5840        let mut signer_accounts = vec![SolanaAccount::new(0, 0, &program_id); MAX_SIGNERS];
5841        let mut rent_sysvar = rent_sysvar();
5842
5843        // multisig is not rent exempt
5844        let account_info_iter = &mut signer_accounts.iter_mut();
5845        assert_eq!(
5846            Err(TokenError::NotRentExempt.into()),
5847            do_process_instruction(
5848                initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(),
5849                vec![
5850                    &mut multisig_account,
5851                    &mut rent_sysvar,
5852                    account_info_iter.next().unwrap(),
5853                ],
5854            )
5855        );
5856
5857        multisig_account.kelvins = multisig_minimum_balance();
5858        let mut multisig_account2 = multisig_account.clone();
5859
5860        // single signer
5861        let account_info_iter = &mut signer_accounts.iter_mut();
5862        do_process_instruction(
5863            initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(),
5864            vec![
5865                &mut multisig_account,
5866                &mut rent_sysvar,
5867                account_info_iter.next().unwrap(),
5868            ],
5869        )
5870        .unwrap();
5871
5872        // single signer using `initialize_multisig2`
5873        let account_info_iter = &mut signer_accounts.iter_mut();
5874        do_process_instruction(
5875            initialize_multisig2(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(),
5876            vec![&mut multisig_account2, account_info_iter.next().unwrap()],
5877        )
5878        .unwrap();
5879
5880        // multiple signer
5881        let account_info_iter = &mut signer_accounts.iter_mut();
5882        do_process_instruction(
5883            initialize_multisig(
5884                &program_id,
5885                &multisig_delegate_key,
5886                &signer_key_refs,
5887                MAX_SIGNERS as u8,
5888            )
5889            .unwrap(),
5890            vec![
5891                &mut multisig_delegate_account,
5892                &mut rent_sysvar,
5893                account_info_iter.next().unwrap(),
5894                account_info_iter.next().unwrap(),
5895                account_info_iter.next().unwrap(),
5896                account_info_iter.next().unwrap(),
5897                account_info_iter.next().unwrap(),
5898                account_info_iter.next().unwrap(),
5899                account_info_iter.next().unwrap(),
5900                account_info_iter.next().unwrap(),
5901                account_info_iter.next().unwrap(),
5902                account_info_iter.next().unwrap(),
5903                account_info_iter.next().unwrap(),
5904            ],
5905        )
5906        .unwrap();
5907
5908        // create new mint with multisig owner
5909        do_process_instruction(
5910            initialize_mint(&program_id, &mint_key, &multisig_key, None, 2).unwrap(),
5911            vec![&mut mint_account, &mut rent_sysvar],
5912        )
5913        .unwrap();
5914
5915        // create account with multisig owner
5916        do_process_instruction(
5917            initialize_account(&program_id, &account_key, &mint_key, &multisig_key).unwrap(),
5918            vec![
5919                &mut account,
5920                &mut mint_account,
5921                &mut multisig_account,
5922                &mut rent_sysvar,
5923            ],
5924        )
5925        .unwrap();
5926
5927        // create another account with multisig owner
5928        do_process_instruction(
5929            initialize_account(
5930                &program_id,
5931                &account2_key,
5932                &mint_key,
5933                &multisig_delegate_key,
5934            )
5935            .unwrap(),
5936            vec![
5937                &mut account2_account,
5938                &mut mint_account,
5939                &mut multisig_account,
5940                &mut rent_sysvar,
5941            ],
5942        )
5943        .unwrap();
5944
5945        // mint to account
5946        let account_info_iter = &mut signer_accounts.iter_mut();
5947        do_process_instruction(
5948            mint_to(
5949                &program_id,
5950                &mint_key,
5951                &account_key,
5952                &multisig_key,
5953                &[&signer_keys[0]],
5954                1000,
5955            )
5956            .unwrap(),
5957            vec![
5958                &mut mint_account,
5959                &mut account,
5960                &mut multisig_account,
5961                account_info_iter.next().unwrap(),
5962            ],
5963        )
5964        .unwrap();
5965
5966        // approve
5967        let account_info_iter = &mut signer_accounts.iter_mut();
5968        do_process_instruction(
5969            approve(
5970                &program_id,
5971                &account_key,
5972                &multisig_delegate_key,
5973                &multisig_key,
5974                &[&signer_keys[0]],
5975                100,
5976            )
5977            .unwrap(),
5978            vec![
5979                &mut account,
5980                &mut multisig_delegate_account,
5981                &mut multisig_account,
5982                account_info_iter.next().unwrap(),
5983            ],
5984        )
5985        .unwrap();
5986
5987        // transfer
5988        let account_info_iter = &mut signer_accounts.iter_mut();
5989        do_process_instruction(
5990            #[allow(deprecated)]
5991            transfer(
5992                &program_id,
5993                &account_key,
5994                &account2_key,
5995                &multisig_key,
5996                &[&signer_keys[0]],
5997                42,
5998            )
5999            .unwrap(),
6000            vec![
6001                &mut account,
6002                &mut account2_account,
6003                &mut multisig_account,
6004                account_info_iter.next().unwrap(),
6005            ],
6006        )
6007        .unwrap();
6008
6009        // transfer via delegate
6010        let account_info_iter = &mut signer_accounts.iter_mut();
6011        do_process_instruction(
6012            #[allow(deprecated)]
6013            transfer(
6014                &program_id,
6015                &account_key,
6016                &account2_key,
6017                &multisig_delegate_key,
6018                &signer_key_refs,
6019                42,
6020            )
6021            .unwrap(),
6022            vec![
6023                &mut account,
6024                &mut account2_account,
6025                &mut multisig_delegate_account,
6026                account_info_iter.next().unwrap(),
6027                account_info_iter.next().unwrap(),
6028                account_info_iter.next().unwrap(),
6029                account_info_iter.next().unwrap(),
6030                account_info_iter.next().unwrap(),
6031                account_info_iter.next().unwrap(),
6032                account_info_iter.next().unwrap(),
6033                account_info_iter.next().unwrap(),
6034                account_info_iter.next().unwrap(),
6035                account_info_iter.next().unwrap(),
6036                account_info_iter.next().unwrap(),
6037            ],
6038        )
6039        .unwrap();
6040
6041        // mint to
6042        let account_info_iter = &mut signer_accounts.iter_mut();
6043        do_process_instruction(
6044            mint_to(
6045                &program_id,
6046                &mint_key,
6047                &account2_key,
6048                &multisig_key,
6049                &[&signer_keys[0]],
6050                42,
6051            )
6052            .unwrap(),
6053            vec![
6054                &mut mint_account,
6055                &mut account2_account,
6056                &mut multisig_account,
6057                account_info_iter.next().unwrap(),
6058            ],
6059        )
6060        .unwrap();
6061
6062        // burn
6063        let account_info_iter = &mut signer_accounts.iter_mut();
6064        do_process_instruction(
6065            burn(
6066                &program_id,
6067                &account_key,
6068                &mint_key,
6069                &multisig_key,
6070                &[&signer_keys[0]],
6071                42,
6072            )
6073            .unwrap(),
6074            vec![
6075                &mut account,
6076                &mut mint_account,
6077                &mut multisig_account,
6078                account_info_iter.next().unwrap(),
6079            ],
6080        )
6081        .unwrap();
6082
6083        // burn via delegate
6084        let account_info_iter = &mut signer_accounts.iter_mut();
6085        do_process_instruction(
6086            burn(
6087                &program_id,
6088                &account_key,
6089                &mint_key,
6090                &multisig_delegate_key,
6091                &signer_key_refs,
6092                42,
6093            )
6094            .unwrap(),
6095            vec![
6096                &mut account,
6097                &mut mint_account,
6098                &mut multisig_delegate_account,
6099                account_info_iter.next().unwrap(),
6100                account_info_iter.next().unwrap(),
6101                account_info_iter.next().unwrap(),
6102                account_info_iter.next().unwrap(),
6103                account_info_iter.next().unwrap(),
6104                account_info_iter.next().unwrap(),
6105                account_info_iter.next().unwrap(),
6106                account_info_iter.next().unwrap(),
6107                account_info_iter.next().unwrap(),
6108                account_info_iter.next().unwrap(),
6109                account_info_iter.next().unwrap(),
6110            ],
6111        )
6112        .unwrap();
6113
6114        // freeze account
6115        let account3_key = Pubkey::new_unique();
6116        let mut account3_account = SolanaAccount::new(
6117            account_minimum_balance(),
6118            Account::get_packed_len(),
6119            &program_id,
6120        );
6121        let mint2_key = Pubkey::new_unique();
6122        let mut mint2_account =
6123            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
6124        do_process_instruction(
6125            initialize_mint(
6126                &program_id,
6127                &mint2_key,
6128                &multisig_key,
6129                Some(&multisig_key),
6130                2,
6131            )
6132            .unwrap(),
6133            vec![&mut mint2_account, &mut rent_sysvar],
6134        )
6135        .unwrap();
6136        do_process_instruction(
6137            initialize_account(&program_id, &account3_key, &mint2_key, &owner_key).unwrap(),
6138            vec![
6139                &mut account3_account,
6140                &mut mint2_account,
6141                &mut owner_account,
6142                &mut rent_sysvar,
6143            ],
6144        )
6145        .unwrap();
6146        let account_info_iter = &mut signer_accounts.iter_mut();
6147        do_process_instruction(
6148            mint_to(
6149                &program_id,
6150                &mint2_key,
6151                &account3_key,
6152                &multisig_key,
6153                &[&signer_keys[0]],
6154                1000,
6155            )
6156            .unwrap(),
6157            vec![
6158                &mut mint2_account,
6159                &mut account3_account,
6160                &mut multisig_account,
6161                account_info_iter.next().unwrap(),
6162            ],
6163        )
6164        .unwrap();
6165        let account_info_iter = &mut signer_accounts.iter_mut();
6166        do_process_instruction(
6167            freeze_account(
6168                &program_id,
6169                &account3_key,
6170                &mint2_key,
6171                &multisig_key,
6172                &[&signer_keys[0]],
6173            )
6174            .unwrap(),
6175            vec![
6176                &mut account3_account,
6177                &mut mint2_account,
6178                &mut multisig_account,
6179                account_info_iter.next().unwrap(),
6180            ],
6181        )
6182        .unwrap();
6183
6184        // do SetAuthority on mint
6185        let account_info_iter = &mut signer_accounts.iter_mut();
6186        do_process_instruction(
6187            set_authority(
6188                &program_id,
6189                &mint_key,
6190                Some(&owner_key),
6191                AuthorityType::MintTokens,
6192                &multisig_key,
6193                &[&signer_keys[0]],
6194            )
6195            .unwrap(),
6196            vec![
6197                &mut mint_account,
6198                &mut multisig_account,
6199                account_info_iter.next().unwrap(),
6200            ],
6201        )
6202        .unwrap();
6203
6204        // do SetAuthority on account
6205        let account_info_iter = &mut signer_accounts.iter_mut();
6206        do_process_instruction(
6207            set_authority(
6208                &program_id,
6209                &account_key,
6210                Some(&owner_key),
6211                AuthorityType::AccountOwner,
6212                &multisig_key,
6213                &[&signer_keys[0]],
6214            )
6215            .unwrap(),
6216            vec![
6217                &mut account,
6218                &mut multisig_account,
6219                account_info_iter.next().unwrap(),
6220            ],
6221        )
6222        .unwrap();
6223    }
6224
6225    #[test]
6226    fn test_validate_owner() {
6227        let program_id = crate::id();
6228        let owner_key = Pubkey::new_unique();
6229        let account_to_validate = Pubkey::new_unique();
6230        let mut signer_keys = [Pubkey::default(); MAX_SIGNERS];
6231        for signer_key in signer_keys.iter_mut().take(MAX_SIGNERS) {
6232            *signer_key = Pubkey::new_unique();
6233        }
6234        let mut signer_kelvins = 0;
6235        let mut signer_data = vec![];
6236        let mut signers = vec![
6237            AccountInfo::new(
6238                &owner_key,
6239                true,
6240                false,
6241                &mut signer_kelvins,
6242                &mut signer_data,
6243                &program_id,
6244                false,
6245                Epoch::default(),
6246            );
6247            MAX_SIGNERS + 1
6248        ];
6249        for (signer, key) in signers.iter_mut().zip(&signer_keys) {
6250            signer.key = key;
6251        }
6252        let mut kelvins = 0;
6253        let mut data = vec![0; Multisig::get_packed_len()];
6254        let mut multisig = Multisig::unpack_unchecked(&data).unwrap();
6255        multisig.m = MAX_SIGNERS as u8;
6256        multisig.n = MAX_SIGNERS as u8;
6257        multisig.signers = signer_keys;
6258        multisig.is_initialized = true;
6259        Multisig::pack(multisig, &mut data).unwrap();
6260        let owner_account_info = AccountInfo::new(
6261            &owner_key,
6262            false,
6263            false,
6264            &mut kelvins,
6265            &mut data,
6266            &program_id,
6267            false,
6268            Epoch::default(),
6269        );
6270
6271        // no multisig, but the account is its own authority, and data is mutably
6272        // borrowed
6273        {
6274            let mut kelvins = 0;
6275            let mut data = vec![0; Account::get_packed_len()];
6276            let mut account = Account::unpack_unchecked(&data).unwrap();
6277            account.owner = account_to_validate;
6278            Account::pack(account, &mut data).unwrap();
6279            let account_info = AccountInfo::new(
6280                &account_to_validate,
6281                true,
6282                false,
6283                &mut kelvins,
6284                &mut data,
6285                &program_id,
6286                false,
6287                Epoch::default(),
6288            );
6289            let account_info_data_len = account_info.data_len();
6290            let mut borrowed_data = account_info.try_borrow_mut_data().unwrap();
6291            Processor::validate_owner(
6292                &program_id,
6293                &account_to_validate,
6294                &account_info,
6295                account_info_data_len,
6296                &[],
6297            )
6298            .unwrap();
6299            // modify the data to be sure that it wasn't silently dropped by the compiler
6300            borrowed_data[0] = 1;
6301        }
6302
6303        // full 11 of 11
6304        Processor::validate_owner(
6305            &program_id,
6306            &owner_key,
6307            &owner_account_info,
6308            owner_account_info.data_len(),
6309            &signers,
6310        )
6311        .unwrap();
6312
6313        // 1 of 11
6314        {
6315            let mut multisig =
6316                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
6317            multisig.m = 1;
6318            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
6319        }
6320        Processor::validate_owner(
6321            &program_id,
6322            &owner_key,
6323            &owner_account_info,
6324            owner_account_info.data_len(),
6325            &signers,
6326        )
6327        .unwrap();
6328
6329        // 2:1
6330        {
6331            let mut multisig =
6332                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
6333            multisig.m = 2;
6334            multisig.n = 1;
6335            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
6336        }
6337        assert_eq!(
6338            Err(ProgramError::MissingRequiredSignature),
6339            Processor::validate_owner(
6340                &program_id,
6341                &owner_key,
6342                &owner_account_info,
6343                owner_account_info.data_len(),
6344                &signers
6345            )
6346        );
6347
6348        // 0:11
6349        {
6350            let mut multisig =
6351                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
6352            multisig.m = 0;
6353            multisig.n = 11;
6354            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
6355        }
6356        Processor::validate_owner(
6357            &program_id,
6358            &owner_key,
6359            &owner_account_info,
6360            owner_account_info.data_len(),
6361            &signers,
6362        )
6363        .unwrap();
6364
6365        // 2:11 but 0 provided
6366        {
6367            let mut multisig =
6368                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
6369            multisig.m = 2;
6370            multisig.n = 11;
6371            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
6372        }
6373        assert_eq!(
6374            Err(ProgramError::MissingRequiredSignature),
6375            Processor::validate_owner(
6376                &program_id,
6377                &owner_key,
6378                &owner_account_info,
6379                owner_account_info.data_len(),
6380                &[]
6381            )
6382        );
6383        // 2:11 but 1 provided
6384        {
6385            let mut multisig =
6386                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
6387            multisig.m = 2;
6388            multisig.n = 11;
6389            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
6390        }
6391        assert_eq!(
6392            Err(ProgramError::MissingRequiredSignature),
6393            Processor::validate_owner(
6394                &program_id,
6395                &owner_key,
6396                &owner_account_info,
6397                owner_account_info.data_len(),
6398                &signers[0..1]
6399            )
6400        );
6401
6402        // 2:11, 2 from middle provided
6403        {
6404            let mut multisig =
6405                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
6406            multisig.m = 2;
6407            multisig.n = 11;
6408            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
6409        }
6410        Processor::validate_owner(
6411            &program_id,
6412            &owner_key,
6413            &owner_account_info,
6414            owner_account_info.data_len(),
6415            &signers[5..7],
6416        )
6417        .unwrap();
6418
6419        // 11:11, one is not a signer
6420        {
6421            let mut multisig =
6422                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
6423            multisig.m = 11;
6424            multisig.n = 11;
6425            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
6426        }
6427        signers[5].is_signer = false;
6428        assert_eq!(
6429            Err(ProgramError::MissingRequiredSignature),
6430            Processor::validate_owner(
6431                &program_id,
6432                &owner_key,
6433                &owner_account_info,
6434                owner_account_info.data_len(),
6435                &signers
6436            )
6437        );
6438        signers[5].is_signer = true;
6439
6440        // 11:11, single signer signs multiple times
6441        {
6442            let mut signer_kelvins = 0;
6443            let mut signer_data = vec![];
6444            let signers = vec![
6445                AccountInfo::new(
6446                    &signer_keys[5],
6447                    true,
6448                    false,
6449                    &mut signer_kelvins,
6450                    &mut signer_data,
6451                    &program_id,
6452                    false,
6453                    Epoch::default(),
6454                );
6455                MAX_SIGNERS + 1
6456            ];
6457            let mut multisig =
6458                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
6459            multisig.m = 11;
6460            multisig.n = 11;
6461            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
6462            assert_eq!(
6463                Err(ProgramError::MissingRequiredSignature),
6464                Processor::validate_owner(
6465                    &program_id,
6466                    &owner_key,
6467                    &owner_account_info,
6468                    owner_account_info.data_len(),
6469                    &signers
6470                )
6471            );
6472        }
6473    }
6474
6475    #[test]
6476    fn test_owner_close_account_dups() {
6477        let program_id = crate::id();
6478        let owner_key = Pubkey::new_unique();
6479        let mint_key = Pubkey::new_unique();
6480        let mut mint_account =
6481            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
6482        let mint_info: AccountInfo<'_> = (&mint_key, false, &mut mint_account).into();
6483        let rent_key = rent::id();
6484        let mut rent_sysvar = rent_sysvar();
6485        let rent_info: AccountInfo<'_> = (&rent_key, false, &mut rent_sysvar).into();
6486
6487        // create mint
6488        do_process_instruction_dups(
6489            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
6490            vec![mint_info.clone(), rent_info.clone()],
6491        )
6492        .unwrap();
6493
6494        let to_close_key = Pubkey::new_unique();
6495        let mut to_close_account = SolanaAccount::new(
6496            account_minimum_balance(),
6497            Account::get_packed_len(),
6498            &program_id,
6499        );
6500        let to_close_account_info: AccountInfo<'_> =
6501            (&to_close_key, true, &mut to_close_account).into();
6502        let destination_account_key = Pubkey::new_unique();
6503        let mut destination_account = SolanaAccount::new(
6504            account_minimum_balance(),
6505            Account::get_packed_len(),
6506            &program_id,
6507        );
6508        let destination_account_info: AccountInfo<'_> =
6509            (&destination_account_key, true, &mut destination_account).into();
6510        // create account
6511        do_process_instruction_dups(
6512            initialize_account(&program_id, &to_close_key, &mint_key, &to_close_key).unwrap(),
6513            vec![
6514                to_close_account_info.clone(),
6515                mint_info.clone(),
6516                to_close_account_info.clone(),
6517                rent_info.clone(),
6518            ],
6519        )
6520        .unwrap();
6521
6522        // source-owner close
6523        do_process_instruction_dups(
6524            close_account(
6525                &program_id,
6526                &to_close_key,
6527                &destination_account_key,
6528                &to_close_key,
6529                &[],
6530            )
6531            .unwrap(),
6532            vec![
6533                to_close_account_info.clone(),
6534                destination_account_info.clone(),
6535                to_close_account_info.clone(),
6536            ],
6537        )
6538        .unwrap();
6539        assert_eq!(*to_close_account_info.data.borrow(), &[0u8; Account::LEN]);
6540    }
6541
6542    #[test]
6543    fn test_close_authority_close_account_dups() {
6544        let program_id = crate::id();
6545        let owner_key = Pubkey::new_unique();
6546        let mint_key = Pubkey::new_unique();
6547        let mut mint_account =
6548            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
6549        let mint_info: AccountInfo<'_> = (&mint_key, false, &mut mint_account).into();
6550        let rent_key = rent::id();
6551        let mut rent_sysvar = rent_sysvar();
6552        let rent_info: AccountInfo<'_> = (&rent_key, false, &mut rent_sysvar).into();
6553
6554        // create mint
6555        do_process_instruction_dups(
6556            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
6557            vec![mint_info.clone(), rent_info.clone()],
6558        )
6559        .unwrap();
6560
6561        let to_close_key = Pubkey::new_unique();
6562        let mut to_close_account = SolanaAccount::new(
6563            account_minimum_balance(),
6564            Account::get_packed_len(),
6565            &program_id,
6566        );
6567        let to_close_account_info: AccountInfo<'_> =
6568            (&to_close_key, true, &mut to_close_account).into();
6569        let destination_account_key = Pubkey::new_unique();
6570        let mut destination_account = SolanaAccount::new(
6571            account_minimum_balance(),
6572            Account::get_packed_len(),
6573            &program_id,
6574        );
6575        let destination_account_info: AccountInfo<'_> =
6576            (&destination_account_key, true, &mut destination_account).into();
6577        // create account
6578        do_process_instruction_dups(
6579            initialize_account(&program_id, &to_close_key, &mint_key, &to_close_key).unwrap(),
6580            vec![
6581                to_close_account_info.clone(),
6582                mint_info.clone(),
6583                to_close_account_info.clone(),
6584                rent_info.clone(),
6585            ],
6586        )
6587        .unwrap();
6588        let mut account = Account::unpack_unchecked(&to_close_account_info.data.borrow()).unwrap();
6589        account.close_authority = COption::Some(to_close_key);
6590        account.owner = owner_key;
6591        Account::pack(account, &mut to_close_account_info.data.borrow_mut()).unwrap();
6592        do_process_instruction_dups(
6593            close_account(
6594                &program_id,
6595                &to_close_key,
6596                &destination_account_key,
6597                &to_close_key,
6598                &[],
6599            )
6600            .unwrap(),
6601            vec![
6602                to_close_account_info.clone(),
6603                destination_account_info.clone(),
6604                to_close_account_info.clone(),
6605            ],
6606        )
6607        .unwrap();
6608        assert_eq!(*to_close_account_info.data.borrow(), &[0u8; Account::LEN]);
6609    }
6610
6611    #[test]
6612    fn test_close_account() {
6613        let program_id = crate::id();
6614        let mint_key = Pubkey::new_unique();
6615        let mut mint_account =
6616            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
6617        let account_key = Pubkey::new_unique();
6618        let mut account_account = SolanaAccount::new(
6619            account_minimum_balance(),
6620            Account::get_packed_len(),
6621            &program_id,
6622        );
6623        let account2_key = Pubkey::new_unique();
6624        let mut account2_account = SolanaAccount::new(
6625            account_minimum_balance() + 42,
6626            Account::get_packed_len(),
6627            &program_id,
6628        );
6629        let account3_key = Pubkey::new_unique();
6630        let mut account3_account = SolanaAccount::new(
6631            account_minimum_balance(),
6632            Account::get_packed_len(),
6633            &program_id,
6634        );
6635        let owner_key = Pubkey::new_unique();
6636        let mut owner_account = SolanaAccount::default();
6637        let owner2_key = Pubkey::new_unique();
6638        let mut owner2_account = SolanaAccount::default();
6639        let mut rent_sysvar = rent_sysvar();
6640
6641        // uninitialized
6642        assert_eq!(
6643            Err(ProgramError::UninitializedAccount),
6644            do_process_instruction(
6645                close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(),
6646                vec![
6647                    &mut account_account,
6648                    &mut account3_account,
6649                    &mut owner2_account,
6650                ],
6651            )
6652        );
6653
6654        // initialize and mint to non-native account
6655        do_process_instruction(
6656            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
6657            vec![&mut mint_account, &mut rent_sysvar],
6658        )
6659        .unwrap();
6660        do_process_instruction(
6661            initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
6662            vec![
6663                &mut account_account,
6664                &mut mint_account,
6665                &mut owner_account,
6666                &mut rent_sysvar,
6667            ],
6668        )
6669        .unwrap();
6670        do_process_instruction(
6671            mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(),
6672            vec![
6673                &mut mint_account,
6674                &mut account_account,
6675                &mut owner_account,
6676                &mut rent_sysvar,
6677            ],
6678        )
6679        .unwrap();
6680        let account = Account::unpack_unchecked(&account_account.data).unwrap();
6681        assert_eq!(account.amount, 42);
6682
6683        // initialize native account
6684        do_process_instruction(
6685            initialize_account(
6686                &program_id,
6687                &account2_key,
6688                &crate::native_mint::id(),
6689                &owner_key,
6690            )
6691            .unwrap(),
6692            vec![
6693                &mut account2_account,
6694                &mut mint_account,
6695                &mut owner_account,
6696                &mut rent_sysvar,
6697            ],
6698        )
6699        .unwrap();
6700        let account = Account::unpack_unchecked(&account2_account.data).unwrap();
6701        assert!(account.is_native());
6702        assert_eq!(account.amount, 42);
6703
6704        // close non-native account with balance
6705        assert_eq!(
6706            Err(TokenError::NonNativeHasBalance.into()),
6707            do_process_instruction(
6708                close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(),
6709                vec![
6710                    &mut account_account,
6711                    &mut account3_account,
6712                    &mut owner_account,
6713                ],
6714            )
6715        );
6716        assert_eq!(account_account.kelvins, account_minimum_balance());
6717
6718        // empty account
6719        do_process_instruction(
6720            burn(&program_id, &account_key, &mint_key, &owner_key, &[], 42).unwrap(),
6721            vec![&mut account_account, &mut mint_account, &mut owner_account],
6722        )
6723        .unwrap();
6724
6725        // wrong owner
6726        assert_eq!(
6727            Err(TokenError::OwnerMismatch.into()),
6728            do_process_instruction(
6729                close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(),
6730                vec![
6731                    &mut account_account,
6732                    &mut account3_account,
6733                    &mut owner2_account,
6734                ],
6735            )
6736        );
6737
6738        // close account
6739        do_process_instruction(
6740            close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(),
6741            vec![
6742                &mut account_account,
6743                &mut account3_account,
6744                &mut owner_account,
6745            ],
6746        )
6747        .unwrap();
6748        assert_eq!(account_account.kelvins, 0);
6749        assert_eq!(account3_account.kelvins, 2 * account_minimum_balance());
6750        let account = Account::unpack_unchecked(&account_account.data).unwrap();
6751        assert_eq!(account.amount, 0);
6752
6753        // fund and initialize new non-native account to test close authority
6754        let account_key = Pubkey::new_unique();
6755        let mut account_account = SolanaAccount::new(
6756            account_minimum_balance(),
6757            Account::get_packed_len(),
6758            &program_id,
6759        );
6760        let owner2_key = Pubkey::new_unique();
6761        let mut owner2_account = SolanaAccount::new(
6762            account_minimum_balance(),
6763            Account::get_packed_len(),
6764            &program_id,
6765        );
6766        do_process_instruction(
6767            initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
6768            vec![
6769                &mut account_account,
6770                &mut mint_account,
6771                &mut owner_account,
6772                &mut rent_sysvar,
6773            ],
6774        )
6775        .unwrap();
6776        account_account.kelvins = 2;
6777
6778        do_process_instruction(
6779            set_authority(
6780                &program_id,
6781                &account_key,
6782                Some(&owner2_key),
6783                AuthorityType::CloseAccount,
6784                &owner_key,
6785                &[],
6786            )
6787            .unwrap(),
6788            vec![&mut account_account, &mut owner_account],
6789        )
6790        .unwrap();
6791
6792        // account owner cannot authorize close if close_authority is set
6793        assert_eq!(
6794            Err(TokenError::OwnerMismatch.into()),
6795            do_process_instruction(
6796                close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(),
6797                vec![
6798                    &mut account_account,
6799                    &mut account3_account,
6800                    &mut owner_account,
6801                ],
6802            )
6803        );
6804
6805        // close non-native account with close_authority
6806        do_process_instruction(
6807            close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(),
6808            vec![
6809                &mut account_account,
6810                &mut account3_account,
6811                &mut owner2_account,
6812            ],
6813        )
6814        .unwrap();
6815        assert_eq!(account_account.kelvins, 0);
6816        assert_eq!(account3_account.kelvins, 2 * account_minimum_balance() + 2);
6817        let account = Account::unpack_unchecked(&account_account.data).unwrap();
6818        assert_eq!(account.amount, 0);
6819
6820        // close native account
6821        do_process_instruction(
6822            close_account(&program_id, &account2_key, &account3_key, &owner_key, &[]).unwrap(),
6823            vec![
6824                &mut account2_account,
6825                &mut account3_account,
6826                &mut owner_account,
6827            ],
6828        )
6829        .unwrap();
6830        assert_eq!(account2_account.data, [0u8; Account::LEN]);
6831        assert_eq!(
6832            account3_account.kelvins,
6833            3 * account_minimum_balance() + 2 + 42
6834        );
6835    }
6836
6837    #[test]
6838    fn test_native_token() {
6839        let program_id = crate::id();
6840        let mut mint_account = native_mint();
6841        let account_key = Pubkey::new_unique();
6842        let mut account_account = SolanaAccount::new(
6843            account_minimum_balance() + 40,
6844            Account::get_packed_len(),
6845            &program_id,
6846        );
6847        let account2_key = Pubkey::new_unique();
6848        let mut account2_account = SolanaAccount::new(
6849            account_minimum_balance(),
6850            Account::get_packed_len(),
6851            &program_id,
6852        );
6853        let account3_key = Pubkey::new_unique();
6854        let mut account3_account = SolanaAccount::new(account_minimum_balance(), 0, &program_id);
6855        let owner_key = Pubkey::new_unique();
6856        let mut owner_account = SolanaAccount::default();
6857        let owner2_key = Pubkey::new_unique();
6858        let mut owner2_account = SolanaAccount::default();
6859        let owner3_key = Pubkey::new_unique();
6860        let mut rent_sysvar = rent_sysvar();
6861
6862        // initialize native account
6863        do_process_instruction(
6864            initialize_account(
6865                &program_id,
6866                &account_key,
6867                &crate::native_mint::id(),
6868                &owner_key,
6869            )
6870            .unwrap(),
6871            vec![
6872                &mut account_account,
6873                &mut mint_account,
6874                &mut owner_account,
6875                &mut rent_sysvar,
6876            ],
6877        )
6878        .unwrap();
6879        let account = Account::unpack_unchecked(&account_account.data).unwrap();
6880        assert!(account.is_native());
6881        assert_eq!(account.amount, 40);
6882
6883        // initialize native account
6884        do_process_instruction(
6885            initialize_account(
6886                &program_id,
6887                &account2_key,
6888                &crate::native_mint::id(),
6889                &owner_key,
6890            )
6891            .unwrap(),
6892            vec![
6893                &mut account2_account,
6894                &mut mint_account,
6895                &mut owner_account,
6896                &mut rent_sysvar,
6897            ],
6898        )
6899        .unwrap();
6900        let account = Account::unpack_unchecked(&account2_account.data).unwrap();
6901        assert!(account.is_native());
6902        assert_eq!(account.amount, 0);
6903
6904        // mint_to unsupported
6905        assert_eq!(
6906            Err(TokenError::NativeNotSupported.into()),
6907            do_process_instruction(
6908                mint_to(
6909                    &program_id,
6910                    &crate::native_mint::id(),
6911                    &account_key,
6912                    &owner_key,
6913                    &[],
6914                    42
6915                )
6916                .unwrap(),
6917                vec![&mut mint_account, &mut account_account, &mut owner_account],
6918            )
6919        );
6920
6921        // burn unsupported
6922        let bogus_mint_key = Pubkey::new_unique();
6923        let mut bogus_mint_account =
6924            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
6925        do_process_instruction(
6926            initialize_mint(&program_id, &bogus_mint_key, &owner_key, None, 2).unwrap(),
6927            vec![&mut bogus_mint_account, &mut rent_sysvar],
6928        )
6929        .unwrap();
6930
6931        assert_eq!(
6932            Err(TokenError::NativeNotSupported.into()),
6933            do_process_instruction(
6934                burn(
6935                    &program_id,
6936                    &account_key,
6937                    &bogus_mint_key,
6938                    &owner_key,
6939                    &[],
6940                    42
6941                )
6942                .unwrap(),
6943                vec![
6944                    &mut account_account,
6945                    &mut bogus_mint_account,
6946                    &mut owner_account
6947                ],
6948            )
6949        );
6950
6951        // ensure can't transfer below rent-exempt reserve
6952        assert_eq!(
6953            Err(TokenError::InsufficientFunds.into()),
6954            do_process_instruction(
6955                #[allow(deprecated)]
6956                transfer(
6957                    &program_id,
6958                    &account_key,
6959                    &account2_key,
6960                    &owner_key,
6961                    &[],
6962                    50,
6963                )
6964                .unwrap(),
6965                vec![
6966                    &mut account_account,
6967                    &mut account2_account,
6968                    &mut owner_account,
6969                ],
6970            )
6971        );
6972
6973        // transfer between native accounts
6974        do_process_instruction(
6975            #[allow(deprecated)]
6976            transfer(
6977                &program_id,
6978                &account_key,
6979                &account2_key,
6980                &owner_key,
6981                &[],
6982                40,
6983            )
6984            .unwrap(),
6985            vec![
6986                &mut account_account,
6987                &mut account2_account,
6988                &mut owner_account,
6989            ],
6990        )
6991        .unwrap();
6992        assert_eq!(account_account.kelvins, account_minimum_balance());
6993        let account = Account::unpack_unchecked(&account_account.data).unwrap();
6994        assert!(account.is_native());
6995        assert_eq!(account.amount, 0);
6996        assert_eq!(account2_account.kelvins, account_minimum_balance() + 40);
6997        let account = Account::unpack_unchecked(&account2_account.data).unwrap();
6998        assert!(account.is_native());
6999        assert_eq!(account.amount, 40);
7000
7001        // set close authority
7002        do_process_instruction(
7003            set_authority(
7004                &program_id,
7005                &account_key,
7006                Some(&owner3_key),
7007                AuthorityType::CloseAccount,
7008                &owner_key,
7009                &[],
7010            )
7011            .unwrap(),
7012            vec![&mut account_account, &mut owner_account],
7013        )
7014        .unwrap();
7015        let account = Account::unpack_unchecked(&account_account.data).unwrap();
7016        assert_eq!(account.close_authority, COption::Some(owner3_key));
7017
7018        // set new account owner
7019        do_process_instruction(
7020            set_authority(
7021                &program_id,
7022                &account_key,
7023                Some(&owner2_key),
7024                AuthorityType::AccountOwner,
7025                &owner_key,
7026                &[],
7027            )
7028            .unwrap(),
7029            vec![&mut account_account, &mut owner_account],
7030        )
7031        .unwrap();
7032
7033        // close authority cleared
7034        let account = Account::unpack_unchecked(&account_account.data).unwrap();
7035        assert_eq!(account.close_authority, COption::None);
7036
7037        // close native account
7038        do_process_instruction(
7039            close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(),
7040            vec![
7041                &mut account_account,
7042                &mut account3_account,
7043                &mut owner2_account,
7044            ],
7045        )
7046        .unwrap();
7047        assert_eq!(account_account.kelvins, 0);
7048        assert_eq!(account3_account.kelvins, 2 * account_minimum_balance());
7049        assert_eq!(account_account.data, [0u8; Account::LEN]);
7050    }
7051
7052    #[test]
7053    fn test_overflow() {
7054        let program_id = crate::id();
7055        let account_key = Pubkey::new_unique();
7056        let mut account_account = SolanaAccount::new(
7057            account_minimum_balance(),
7058            Account::get_packed_len(),
7059            &program_id,
7060        );
7061        let account2_key = Pubkey::new_unique();
7062        let mut account2_account = SolanaAccount::new(
7063            account_minimum_balance(),
7064            Account::get_packed_len(),
7065            &program_id,
7066        );
7067        let owner_key = Pubkey::new_unique();
7068        let mut owner_account = SolanaAccount::default();
7069        let owner2_key = Pubkey::new_unique();
7070        let mut owner2_account = SolanaAccount::default();
7071        let mint_owner_key = Pubkey::new_unique();
7072        let mut mint_owner_account = SolanaAccount::default();
7073        let mint_key = Pubkey::new_unique();
7074        let mut mint_account =
7075            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
7076        let mut rent_sysvar = rent_sysvar();
7077
7078        // create new mint with owner
7079        do_process_instruction(
7080            initialize_mint(&program_id, &mint_key, &mint_owner_key, None, 2).unwrap(),
7081            vec![&mut mint_account, &mut rent_sysvar],
7082        )
7083        .unwrap();
7084
7085        // create an account
7086        do_process_instruction(
7087            initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
7088            vec![
7089                &mut account_account,
7090                &mut mint_account,
7091                &mut owner_account,
7092                &mut rent_sysvar,
7093            ],
7094        )
7095        .unwrap();
7096
7097        // create another account
7098        do_process_instruction(
7099            initialize_account(&program_id, &account2_key, &mint_key, &owner2_key).unwrap(),
7100            vec![
7101                &mut account2_account,
7102                &mut mint_account,
7103                &mut owner2_account,
7104                &mut rent_sysvar,
7105            ],
7106        )
7107        .unwrap();
7108
7109        // mint the max to an account
7110        do_process_instruction(
7111            mint_to(
7112                &program_id,
7113                &mint_key,
7114                &account_key,
7115                &mint_owner_key,
7116                &[],
7117                u64::MAX,
7118            )
7119            .unwrap(),
7120            vec![
7121                &mut mint_account,
7122                &mut account_account,
7123                &mut mint_owner_account,
7124            ],
7125        )
7126        .unwrap();
7127        let account = Account::unpack_unchecked(&account_account.data).unwrap();
7128        assert_eq!(account.amount, u64::MAX);
7129
7130        // attempt to mint one more to account
7131        assert_eq!(
7132            Err(TokenError::Overflow.into()),
7133            do_process_instruction(
7134                mint_to(
7135                    &program_id,
7136                    &mint_key,
7137                    &account_key,
7138                    &mint_owner_key,
7139                    &[],
7140                    1,
7141                )
7142                .unwrap(),
7143                vec![
7144                    &mut mint_account,
7145                    &mut account_account,
7146                    &mut mint_owner_account,
7147                ],
7148            )
7149        );
7150        let account = Account::unpack_unchecked(&account_account.data).unwrap();
7151        assert_eq!(account.amount, u64::MAX);
7152
7153        // attempt to mint one more to the other account
7154        assert_eq!(
7155            Err(TokenError::Overflow.into()),
7156            do_process_instruction(
7157                mint_to(
7158                    &program_id,
7159                    &mint_key,
7160                    &account2_key,
7161                    &mint_owner_key,
7162                    &[],
7163                    1,
7164                )
7165                .unwrap(),
7166                vec![
7167                    &mut mint_account,
7168                    &mut account2_account,
7169                    &mut mint_owner_account,
7170                ],
7171            )
7172        );
7173
7174        // burn some of the supply
7175        do_process_instruction(
7176            burn(&program_id, &account_key, &mint_key, &owner_key, &[], 100).unwrap(),
7177            vec![&mut account_account, &mut mint_account, &mut owner_account],
7178        )
7179        .unwrap();
7180        let account = Account::unpack_unchecked(&account_account.data).unwrap();
7181        assert_eq!(account.amount, u64::MAX - 100);
7182
7183        do_process_instruction(
7184            mint_to(
7185                &program_id,
7186                &mint_key,
7187                &account_key,
7188                &mint_owner_key,
7189                &[],
7190                100,
7191            )
7192            .unwrap(),
7193            vec![
7194                &mut mint_account,
7195                &mut account_account,
7196                &mut mint_owner_account,
7197            ],
7198        )
7199        .unwrap();
7200        let account = Account::unpack_unchecked(&account_account.data).unwrap();
7201        assert_eq!(account.amount, u64::MAX);
7202
7203        // manipulate account balance to attempt overflow transfer
7204        let mut account = Account::unpack_unchecked(&account2_account.data).unwrap();
7205        account.amount = 1;
7206        Account::pack(account, &mut account2_account.data).unwrap();
7207
7208        assert_eq!(
7209            Err(TokenError::Overflow.into()),
7210            do_process_instruction(
7211                #[allow(deprecated)]
7212                transfer(
7213                    &program_id,
7214                    &account2_key,
7215                    &account_key,
7216                    &owner2_key,
7217                    &[],
7218                    1,
7219                )
7220                .unwrap(),
7221                vec![
7222                    &mut account2_account,
7223                    &mut account_account,
7224                    &mut owner2_account,
7225                ],
7226            )
7227        );
7228    }
7229
7230    #[test]
7231    fn test_frozen() {
7232        let program_id = crate::id();
7233        let account_key = Pubkey::new_unique();
7234        let mut account_account = SolanaAccount::new(
7235            account_minimum_balance(),
7236            Account::get_packed_len(),
7237            &program_id,
7238        );
7239        let account2_key = Pubkey::new_unique();
7240        let mut account2_account = SolanaAccount::new(
7241            account_minimum_balance(),
7242            Account::get_packed_len(),
7243            &program_id,
7244        );
7245        let owner_key = Pubkey::new_unique();
7246        let mut owner_account = SolanaAccount::default();
7247        let mint_key = Pubkey::new_unique();
7248        let mut mint_account =
7249            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
7250        let mut rent_sysvar = rent_sysvar();
7251
7252        // create new mint and fund first account
7253        do_process_instruction(
7254            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
7255            vec![&mut mint_account, &mut rent_sysvar],
7256        )
7257        .unwrap();
7258
7259        // create account
7260        do_process_instruction(
7261            initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
7262            vec![
7263                &mut account_account,
7264                &mut mint_account,
7265                &mut owner_account,
7266                &mut rent_sysvar,
7267            ],
7268        )
7269        .unwrap();
7270
7271        // create another account
7272        do_process_instruction(
7273            initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(),
7274            vec![
7275                &mut account2_account,
7276                &mut mint_account,
7277                &mut owner_account,
7278                &mut rent_sysvar,
7279            ],
7280        )
7281        .unwrap();
7282
7283        // fund first account
7284        do_process_instruction(
7285            mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(),
7286            vec![&mut mint_account, &mut account_account, &mut owner_account],
7287        )
7288        .unwrap();
7289
7290        // no transfer if either account is frozen
7291        let mut account = Account::unpack_unchecked(&account2_account.data).unwrap();
7292        account.state = AccountState::Frozen;
7293        Account::pack(account, &mut account2_account.data).unwrap();
7294        assert_eq!(
7295            Err(TokenError::AccountFrozen.into()),
7296            do_process_instruction(
7297                #[allow(deprecated)]
7298                transfer(
7299                    &program_id,
7300                    &account_key,
7301                    &account2_key,
7302                    &owner_key,
7303                    &[],
7304                    500,
7305                )
7306                .unwrap(),
7307                vec![
7308                    &mut account_account,
7309                    &mut account2_account,
7310                    &mut owner_account,
7311                ],
7312            )
7313        );
7314
7315        let mut account = Account::unpack_unchecked(&account_account.data).unwrap();
7316        account.state = AccountState::Initialized;
7317        Account::pack(account, &mut account_account.data).unwrap();
7318        let mut account = Account::unpack_unchecked(&account2_account.data).unwrap();
7319        account.state = AccountState::Frozen;
7320        Account::pack(account, &mut account2_account.data).unwrap();
7321        assert_eq!(
7322            Err(TokenError::AccountFrozen.into()),
7323            do_process_instruction(
7324                #[allow(deprecated)]
7325                transfer(
7326                    &program_id,
7327                    &account_key,
7328                    &account2_key,
7329                    &owner_key,
7330                    &[],
7331                    500,
7332                )
7333                .unwrap(),
7334                vec![
7335                    &mut account_account,
7336                    &mut account2_account,
7337                    &mut owner_account,
7338                ],
7339            )
7340        );
7341
7342        // no approve if account is frozen
7343        let mut account = Account::unpack_unchecked(&account_account.data).unwrap();
7344        account.state = AccountState::Frozen;
7345        Account::pack(account, &mut account_account.data).unwrap();
7346        let delegate_key = Pubkey::new_unique();
7347        let mut delegate_account = SolanaAccount::default();
7348        assert_eq!(
7349            Err(TokenError::AccountFrozen.into()),
7350            do_process_instruction(
7351                approve(
7352                    &program_id,
7353                    &account_key,
7354                    &delegate_key,
7355                    &owner_key,
7356                    &[],
7357                    100
7358                )
7359                .unwrap(),
7360                vec![
7361                    &mut account_account,
7362                    &mut delegate_account,
7363                    &mut owner_account,
7364                ],
7365            )
7366        );
7367
7368        // no revoke if account is frozen
7369        let mut account = Account::unpack_unchecked(&account_account.data).unwrap();
7370        account.delegate = COption::Some(delegate_key);
7371        account.delegated_amount = 100;
7372        Account::pack(account, &mut account_account.data).unwrap();
7373        assert_eq!(
7374            Err(TokenError::AccountFrozen.into()),
7375            do_process_instruction(
7376                revoke(&program_id, &account_key, &owner_key, &[]).unwrap(),
7377                vec![&mut account_account, &mut owner_account],
7378            )
7379        );
7380
7381        // no set authority if account is frozen
7382        let new_owner_key = Pubkey::new_unique();
7383        assert_eq!(
7384            Err(TokenError::AccountFrozen.into()),
7385            do_process_instruction(
7386                set_authority(
7387                    &program_id,
7388                    &account_key,
7389                    Some(&new_owner_key),
7390                    AuthorityType::AccountOwner,
7391                    &owner_key,
7392                    &[]
7393                )
7394                .unwrap(),
7395                vec![&mut account_account, &mut owner_account,],
7396            )
7397        );
7398
7399        // no mint_to if destination account is frozen
7400        assert_eq!(
7401            Err(TokenError::AccountFrozen.into()),
7402            do_process_instruction(
7403                mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 100).unwrap(),
7404                vec![&mut mint_account, &mut account_account, &mut owner_account,],
7405            )
7406        );
7407
7408        // no burn if account is frozen
7409        assert_eq!(
7410            Err(TokenError::AccountFrozen.into()),
7411            do_process_instruction(
7412                burn(&program_id, &account_key, &mint_key, &owner_key, &[], 100).unwrap(),
7413                vec![&mut account_account, &mut mint_account, &mut owner_account],
7414            )
7415        );
7416    }
7417
7418    #[test]
7419    fn test_freeze_thaw_dups() {
7420        let program_id = crate::id();
7421        let account1_key = Pubkey::new_unique();
7422        let mut account1_account = SolanaAccount::new(
7423            account_minimum_balance(),
7424            Account::get_packed_len(),
7425            &program_id,
7426        );
7427        let account1_info: AccountInfo<'_> = (&account1_key, true, &mut account1_account).into();
7428        let owner_key = Pubkey::new_unique();
7429        let mint_key = Pubkey::new_unique();
7430        let mut mint_account =
7431            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
7432        let mint_info: AccountInfo<'_> = (&mint_key, true, &mut mint_account).into();
7433        let rent_key = rent::id();
7434        let mut rent_sysvar = rent_sysvar();
7435        let rent_info: AccountInfo<'_> = (&rent_key, false, &mut rent_sysvar).into();
7436
7437        // create mint
7438        do_process_instruction_dups(
7439            initialize_mint(&program_id, &mint_key, &owner_key, Some(&account1_key), 2).unwrap(),
7440            vec![mint_info.clone(), rent_info.clone()],
7441        )
7442        .unwrap();
7443
7444        // create account
7445        do_process_instruction_dups(
7446            initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(),
7447            vec![
7448                account1_info.clone(),
7449                mint_info.clone(),
7450                account1_info.clone(),
7451                rent_info.clone(),
7452            ],
7453        )
7454        .unwrap();
7455
7456        // freeze where mint freeze_authority is account
7457        do_process_instruction_dups(
7458            freeze_account(&program_id, &account1_key, &mint_key, &account1_key, &[]).unwrap(),
7459            vec![
7460                account1_info.clone(),
7461                mint_info.clone(),
7462                account1_info.clone(),
7463            ],
7464        )
7465        .unwrap();
7466
7467        // thaw where mint freeze_authority is account
7468        let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap();
7469        account.state = AccountState::Frozen;
7470        Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap();
7471        do_process_instruction_dups(
7472            thaw_account(&program_id, &account1_key, &mint_key, &account1_key, &[]).unwrap(),
7473            vec![
7474                account1_info.clone(),
7475                mint_info.clone(),
7476                account1_info.clone(),
7477            ],
7478        )
7479        .unwrap();
7480    }
7481
7482    #[test]
7483    fn test_freeze_account() {
7484        let program_id = crate::id();
7485        let account_key = Pubkey::new_unique();
7486        let mut account_account = SolanaAccount::new(
7487            account_minimum_balance(),
7488            Account::get_packed_len(),
7489            &program_id,
7490        );
7491        let account_owner_key = Pubkey::new_unique();
7492        let mut account_owner_account = SolanaAccount::default();
7493        let owner_key = Pubkey::new_unique();
7494        let mut owner_account = SolanaAccount::default();
7495        let owner2_key = Pubkey::new_unique();
7496        let mut owner2_account = SolanaAccount::default();
7497        let mint_key = Pubkey::new_unique();
7498        let mut mint_account =
7499            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
7500        let mut rent_sysvar = rent_sysvar();
7501
7502        // create new mint with owner different from account owner
7503        do_process_instruction(
7504            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
7505            vec![&mut mint_account, &mut rent_sysvar],
7506        )
7507        .unwrap();
7508
7509        // create account
7510        do_process_instruction(
7511            initialize_account(&program_id, &account_key, &mint_key, &account_owner_key).unwrap(),
7512            vec![
7513                &mut account_account,
7514                &mut mint_account,
7515                &mut account_owner_account,
7516                &mut rent_sysvar,
7517            ],
7518        )
7519        .unwrap();
7520
7521        // mint to account
7522        do_process_instruction(
7523            mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(),
7524            vec![&mut mint_account, &mut account_account, &mut owner_account],
7525        )
7526        .unwrap();
7527
7528        // mint cannot freeze
7529        assert_eq!(
7530            Err(TokenError::MintCannotFreeze.into()),
7531            do_process_instruction(
7532                freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(),
7533                vec![&mut account_account, &mut mint_account, &mut owner_account],
7534            )
7535        );
7536
7537        // missing freeze_authority
7538        let mut mint = Mint::unpack_unchecked(&mint_account.data).unwrap();
7539        mint.freeze_authority = COption::Some(owner_key);
7540        Mint::pack(mint, &mut mint_account.data).unwrap();
7541        assert_eq!(
7542            Err(TokenError::OwnerMismatch.into()),
7543            do_process_instruction(
7544                freeze_account(&program_id, &account_key, &mint_key, &owner2_key, &[]).unwrap(),
7545                vec![&mut account_account, &mut mint_account, &mut owner2_account],
7546            )
7547        );
7548
7549        // check explicit thaw
7550        assert_eq!(
7551            Err(TokenError::InvalidState.into()),
7552            do_process_instruction(
7553                thaw_account(&program_id, &account_key, &mint_key, &owner2_key, &[]).unwrap(),
7554                vec![&mut account_account, &mut mint_account, &mut owner2_account],
7555            )
7556        );
7557
7558        // freeze
7559        do_process_instruction(
7560            freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(),
7561            vec![&mut account_account, &mut mint_account, &mut owner_account],
7562        )
7563        .unwrap();
7564        let account = Account::unpack_unchecked(&account_account.data).unwrap();
7565        assert_eq!(account.state, AccountState::Frozen);
7566
7567        // check explicit freeze
7568        assert_eq!(
7569            Err(TokenError::InvalidState.into()),
7570            do_process_instruction(
7571                freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(),
7572                vec![&mut account_account, &mut mint_account, &mut owner_account],
7573            )
7574        );
7575
7576        // check thaw authority
7577        assert_eq!(
7578            Err(TokenError::OwnerMismatch.into()),
7579            do_process_instruction(
7580                thaw_account(&program_id, &account_key, &mint_key, &owner2_key, &[]).unwrap(),
7581                vec![&mut account_account, &mut mint_account, &mut owner2_account],
7582            )
7583        );
7584
7585        // thaw
7586        do_process_instruction(
7587            thaw_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(),
7588            vec![&mut account_account, &mut mint_account, &mut owner_account],
7589        )
7590        .unwrap();
7591        let account = Account::unpack_unchecked(&account_account.data).unwrap();
7592        assert_eq!(account.state, AccountState::Initialized);
7593    }
7594
7595    #[test]
7596    fn test_initialize_account2_and_3() {
7597        let program_id = crate::id();
7598        let account_key = Pubkey::new_unique();
7599        let mut account_account = SolanaAccount::new(
7600            account_minimum_balance(),
7601            Account::get_packed_len(),
7602            &program_id,
7603        );
7604        let mut account2_account = SolanaAccount::new(
7605            account_minimum_balance(),
7606            Account::get_packed_len(),
7607            &program_id,
7608        );
7609        let mut account3_account = SolanaAccount::new(
7610            account_minimum_balance(),
7611            Account::get_packed_len(),
7612            &program_id,
7613        );
7614        let owner_key = Pubkey::new_unique();
7615        let mut owner_account = SolanaAccount::default();
7616        let mint_key = Pubkey::new_unique();
7617        let mut mint_account =
7618            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
7619        let mut rent_sysvar = rent_sysvar();
7620
7621        // create mint
7622        do_process_instruction(
7623            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
7624            vec![&mut mint_account, &mut rent_sysvar],
7625        )
7626        .unwrap();
7627
7628        do_process_instruction(
7629            initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
7630            vec![
7631                &mut account_account,
7632                &mut mint_account,
7633                &mut owner_account,
7634                &mut rent_sysvar,
7635            ],
7636        )
7637        .unwrap();
7638
7639        do_process_instruction(
7640            initialize_account2(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
7641            vec![&mut account2_account, &mut mint_account, &mut rent_sysvar],
7642        )
7643        .unwrap();
7644
7645        assert_eq!(account_account, account2_account);
7646
7647        do_process_instruction(
7648            initialize_account3(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
7649            vec![&mut account3_account, &mut mint_account],
7650        )
7651        .unwrap();
7652
7653        assert_eq!(account_account, account3_account);
7654    }
7655
7656    #[test]
7657    fn initialize_account_on_non_transferable_mint() {
7658        let program_id = crate::id();
7659        let account = Pubkey::new_unique();
7660        let account_len = ExtensionType::try_calculate_account_len::<Mint>(&[
7661            ExtensionType::NonTransferableAccount,
7662        ])
7663        .unwrap();
7664        let mut account_without_enough_length = SolanaAccount::new(
7665            Rent::default().minimum_balance(account_len),
7666            account_len,
7667            &program_id,
7668        );
7669
7670        let account2 = Pubkey::new_unique();
7671        let account2_len = ExtensionType::try_calculate_account_len::<Mint>(&[
7672            ExtensionType::NonTransferableAccount,
7673            ExtensionType::ImmutableOwner,
7674        ])
7675        .unwrap();
7676        let mut account_with_enough_length = SolanaAccount::new(
7677            Rent::default().minimum_balance(account2_len),
7678            account2_len,
7679            &program_id,
7680        );
7681
7682        let owner_key = Pubkey::new_unique();
7683        let mut owner_account = SolanaAccount::default();
7684        let mint_key = Pubkey::new_unique();
7685        let mint_len =
7686            ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::NonTransferable])
7687                .unwrap();
7688        let mut mint_account = SolanaAccount::new(
7689            Rent::default().minimum_balance(mint_len),
7690            mint_len,
7691            &program_id,
7692        );
7693        let mut rent_sysvar = rent_sysvar();
7694
7695        // create a non-transferable mint
7696        assert_eq!(
7697            Ok(()),
7698            do_process_instruction(
7699                initialize_non_transferable_mint(&program_id, &mint_key).unwrap(),
7700                vec![&mut mint_account],
7701            )
7702        );
7703        assert_eq!(
7704            Ok(()),
7705            do_process_instruction(
7706                initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
7707                vec![&mut mint_account, &mut rent_sysvar]
7708            )
7709        );
7710
7711        //fail when account space is not enough for adding the immutable ownership
7712        // extension
7713        assert_eq!(
7714            Err(ProgramError::InvalidAccountData),
7715            do_process_instruction(
7716                initialize_account(&program_id, &account, &mint_key, &owner_key).unwrap(),
7717                vec![
7718                    &mut account_without_enough_length,
7719                    &mut mint_account,
7720                    &mut owner_account,
7721                    &mut rent_sysvar,
7722                ]
7723            )
7724        );
7725
7726        //success to initialize an account with enough data space
7727        assert_eq!(
7728            Ok(()),
7729            do_process_instruction(
7730                initialize_account(&program_id, &account2, &mint_key, &owner_key).unwrap(),
7731                vec![
7732                    &mut account_with_enough_length,
7733                    &mut mint_account,
7734                    &mut owner_account,
7735                    &mut rent_sysvar,
7736                ]
7737            )
7738        );
7739    }
7740
7741    #[test]
7742    fn test_sync_native() {
7743        let program_id = crate::id();
7744        let mint_key = Pubkey::new_unique();
7745        let mut mint_account =
7746            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
7747        let native_account_key = Pubkey::new_unique();
7748        let kelvins = 40;
7749        let mut native_account = SolanaAccount::new(
7750            account_minimum_balance() + kelvins,
7751            Account::get_packed_len(),
7752            &program_id,
7753        );
7754        let non_native_account_key = Pubkey::new_unique();
7755        let mut non_native_account = SolanaAccount::new(
7756            account_minimum_balance() + 50,
7757            Account::get_packed_len(),
7758            &program_id,
7759        );
7760
7761        let owner_key = Pubkey::new_unique();
7762        let mut owner_account = SolanaAccount::default();
7763        let mut rent_sysvar = rent_sysvar();
7764
7765        // initialize non-native mint
7766        do_process_instruction(
7767            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
7768            vec![&mut mint_account, &mut rent_sysvar],
7769        )
7770        .unwrap();
7771
7772        // initialize non-native account
7773        do_process_instruction(
7774            initialize_account(&program_id, &non_native_account_key, &mint_key, &owner_key)
7775                .unwrap(),
7776            vec![
7777                &mut non_native_account,
7778                &mut mint_account,
7779                &mut owner_account,
7780                &mut rent_sysvar,
7781            ],
7782        )
7783        .unwrap();
7784
7785        let account = Account::unpack_unchecked(&non_native_account.data).unwrap();
7786        assert!(!account.is_native());
7787        assert_eq!(account.amount, 0);
7788
7789        // fail sync non-native
7790        assert_eq!(
7791            Err(TokenError::NonNativeNotSupported.into()),
7792            do_process_instruction(
7793                sync_native(&program_id, &non_native_account_key,).unwrap(),
7794                vec![&mut non_native_account],
7795            )
7796        );
7797
7798        // fail sync uninitialized
7799        assert_eq!(
7800            Err(ProgramError::UninitializedAccount),
7801            do_process_instruction(
7802                sync_native(&program_id, &native_account_key,).unwrap(),
7803                vec![&mut native_account],
7804            )
7805        );
7806
7807        // wrap native account
7808        do_process_instruction(
7809            initialize_account(
7810                &program_id,
7811                &native_account_key,
7812                &crate::native_mint::id(),
7813                &owner_key,
7814            )
7815            .unwrap(),
7816            vec![
7817                &mut native_account,
7818                &mut mint_account,
7819                &mut owner_account,
7820                &mut rent_sysvar,
7821            ],
7822        )
7823        .unwrap();
7824
7825        // fail sync, not owned by program
7826        let not_program_id = Pubkey::new_unique();
7827        native_account.owner = not_program_id;
7828        assert_eq!(
7829            Err(ProgramError::IncorrectProgramId),
7830            do_process_instruction(
7831                sync_native(&program_id, &native_account_key,).unwrap(),
7832                vec![&mut native_account],
7833            )
7834        );
7835        native_account.owner = program_id;
7836
7837        let account = Account::unpack_unchecked(&native_account.data).unwrap();
7838        assert!(account.is_native());
7839        assert_eq!(account.amount, kelvins);
7840
7841        // sync, no change
7842        do_process_instruction(
7843            sync_native(&program_id, &native_account_key).unwrap(),
7844            vec![&mut native_account],
7845        )
7846        .unwrap();
7847        let account = Account::unpack_unchecked(&native_account.data).unwrap();
7848        assert_eq!(account.amount, kelvins);
7849
7850        // transfer rlo
7851        let new_kelvins = kelvins + 50;
7852        native_account.kelvins = account_minimum_balance() + new_kelvins;
7853
7854        // success sync
7855        do_process_instruction(
7856            sync_native(&program_id, &native_account_key).unwrap(),
7857            vec![&mut native_account],
7858        )
7859        .unwrap();
7860        let account = Account::unpack_unchecked(&native_account.data).unwrap();
7861        assert_eq!(account.amount, new_kelvins);
7862
7863        // reduce rlo
7864        native_account.kelvins -= 1;
7865
7866        // fail sync
7867        assert_eq!(
7868            Err(TokenError::InvalidState.into()),
7869            do_process_instruction(
7870                sync_native(&program_id, &native_account_key,).unwrap(),
7871                vec![&mut native_account],
7872            )
7873        );
7874    }
7875
7876    #[test]
7877    #[serial]
7878    fn test_get_account_data_size() {
7879        // see integration tests for return-data validity
7880        let program_id = crate::id();
7881        let owner_key = Pubkey::new_unique();
7882        let mut owner_account = SolanaAccount::default();
7883        let mut rent_sysvar = rent_sysvar();
7884
7885        // Base mint
7886        let mut mint_account =
7887            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
7888        let mint_key = Pubkey::new_unique();
7889        do_process_instruction(
7890            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
7891            vec![&mut mint_account, &mut rent_sysvar],
7892        )
7893        .unwrap();
7894
7895        set_expected_data(
7896            ExtensionType::try_calculate_account_len::<Account>(&[])
7897                .unwrap()
7898                .to_le_bytes()
7899                .to_vec(),
7900        );
7901        do_process_instruction(
7902            get_account_data_size(&program_id, &mint_key, &[]).unwrap(),
7903            vec![&mut mint_account],
7904        )
7905        .unwrap();
7906
7907        set_expected_data(
7908            ExtensionType::try_calculate_account_len::<Account>(&[
7909                ExtensionType::TransferFeeAmount,
7910            ])
7911            .unwrap()
7912            .to_le_bytes()
7913            .to_vec(),
7914        );
7915        do_process_instruction(
7916            get_account_data_size(
7917                &program_id,
7918                &mint_key,
7919                &[
7920                    ExtensionType::TransferFeeAmount,
7921                    ExtensionType::TransferFeeAmount, // Duplicate user input ignored...
7922                ],
7923            )
7924            .unwrap(),
7925            vec![&mut mint_account],
7926        )
7927        .unwrap();
7928
7929        // Native mint
7930        let mut mint_account = native_mint();
7931        set_expected_data(
7932            ExtensionType::try_calculate_account_len::<Account>(&[])
7933                .unwrap()
7934                .to_le_bytes()
7935                .to_vec(),
7936        );
7937        do_process_instruction(
7938            get_account_data_size(&program_id, &mint_key, &[]).unwrap(),
7939            vec![&mut mint_account],
7940        )
7941        .unwrap();
7942
7943        // Extended mint
7944        let mint_len =
7945            ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::TransferFeeConfig])
7946                .unwrap();
7947        let mut extended_mint_account = SolanaAccount::new(
7948            Rent::default().minimum_balance(mint_len),
7949            mint_len,
7950            &program_id,
7951        );
7952        let extended_mint_key = Pubkey::new_unique();
7953        do_process_instruction(
7954            initialize_transfer_fee_config(&program_id, &extended_mint_key, None, None, 10, 4242)
7955                .unwrap(),
7956            vec![&mut extended_mint_account],
7957        )
7958        .unwrap();
7959        do_process_instruction(
7960            initialize_mint(&program_id, &extended_mint_key, &owner_key, None, 2).unwrap(),
7961            vec![&mut extended_mint_account, &mut rent_sysvar],
7962        )
7963        .unwrap();
7964
7965        set_expected_data(
7966            ExtensionType::try_calculate_account_len::<Account>(&[
7967                ExtensionType::TransferFeeAmount,
7968            ])
7969            .unwrap()
7970            .to_le_bytes()
7971            .to_vec(),
7972        );
7973        do_process_instruction(
7974            get_account_data_size(&program_id, &mint_key, &[]).unwrap(),
7975            vec![&mut extended_mint_account],
7976        )
7977        .unwrap();
7978
7979        do_process_instruction(
7980            get_account_data_size(
7981                &program_id,
7982                &mint_key,
7983                // User extension that's also added by the mint ignored...
7984                &[ExtensionType::TransferFeeAmount],
7985            )
7986            .unwrap(),
7987            vec![&mut extended_mint_account],
7988        )
7989        .unwrap();
7990
7991        // Invalid mint
7992        let mut invalid_mint_account = SolanaAccount::new(
7993            account_minimum_balance(),
7994            Account::get_packed_len(),
7995            &program_id,
7996        );
7997        let invalid_mint_key = Pubkey::new_unique();
7998        do_process_instruction(
7999            initialize_account(&program_id, &invalid_mint_key, &mint_key, &owner_key).unwrap(),
8000            vec![
8001                &mut invalid_mint_account,
8002                &mut mint_account,
8003                &mut owner_account,
8004                &mut rent_sysvar,
8005            ],
8006        )
8007        .unwrap();
8008
8009        assert_eq!(
8010            do_process_instruction(
8011                get_account_data_size(&program_id, &invalid_mint_key, &[]).unwrap(),
8012                vec![&mut invalid_mint_account],
8013            ),
8014            Err(TokenError::InvalidMint.into())
8015        );
8016
8017        // Invalid mint owner
8018        let invalid_program_id = Pubkey::new_unique();
8019        let mut invalid_mint_account = SolanaAccount::new(
8020            mint_minimum_balance(),
8021            Mint::get_packed_len(),
8022            &invalid_program_id,
8023        );
8024        let invalid_mint_key = Pubkey::new_unique();
8025        let mut instruction =
8026            initialize_mint(&program_id, &invalid_mint_key, &owner_key, None, 2).unwrap();
8027        instruction.program_id = invalid_program_id;
8028        do_process_instruction(
8029            instruction,
8030            vec![&mut invalid_mint_account, &mut rent_sysvar],
8031        )
8032        .unwrap();
8033
8034        assert_eq!(
8035            do_process_instruction(
8036                get_account_data_size(&program_id, &invalid_mint_key, &[]).unwrap(),
8037                vec![&mut invalid_mint_account],
8038            ),
8039            Err(ProgramError::IncorrectProgramId)
8040        );
8041
8042        // Invalid Extension Type for mint and uninitialized account
8043        assert_eq!(
8044            do_process_instruction(
8045                get_account_data_size(&program_id, &mint_key, &[ExtensionType::Uninitialized])
8046                    .unwrap(),
8047                vec![&mut mint_account],
8048            ),
8049            Err(TokenError::ExtensionTypeMismatch.into())
8050        );
8051        assert_eq!(
8052            do_process_instruction(
8053                get_account_data_size(
8054                    &program_id,
8055                    &mint_key,
8056                    &[
8057                        ExtensionType::MemoTransfer,
8058                        ExtensionType::MintCloseAuthority
8059                    ]
8060                )
8061                .unwrap(),
8062                vec![&mut mint_account],
8063            ),
8064            Err(TokenError::ExtensionTypeMismatch.into())
8065        );
8066    }
8067
8068    #[test]
8069    #[serial]
8070    fn test_amount_to_ui_amount() {
8071        let program_id = crate::id();
8072        let owner_key = Pubkey::new_unique();
8073        let mint_key = Pubkey::new_unique();
8074        let mut mint_account =
8075            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
8076        let mut rent_sysvar = rent_sysvar();
8077
8078        // fail if an invalid mint is passed in
8079        assert_eq!(
8080            Err(TokenError::InvalidMint.into()),
8081            do_process_instruction(
8082                amount_to_ui_amount(&program_id, &mint_key, 110).unwrap(),
8083                vec![&mut mint_account],
8084            )
8085        );
8086
8087        // create mint
8088        do_process_instruction(
8089            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
8090            vec![&mut mint_account, &mut rent_sysvar],
8091        )
8092        .unwrap();
8093
8094        set_expected_data("0.23".as_bytes().to_vec());
8095        do_process_instruction(
8096            amount_to_ui_amount(&program_id, &mint_key, 23).unwrap(),
8097            vec![&mut mint_account],
8098        )
8099        .unwrap();
8100
8101        set_expected_data("1.1".as_bytes().to_vec());
8102        do_process_instruction(
8103            amount_to_ui_amount(&program_id, &mint_key, 110).unwrap(),
8104            vec![&mut mint_account],
8105        )
8106        .unwrap();
8107
8108        set_expected_data("42".as_bytes().to_vec());
8109        do_process_instruction(
8110            amount_to_ui_amount(&program_id, &mint_key, 4200).unwrap(),
8111            vec![&mut mint_account],
8112        )
8113        .unwrap();
8114
8115        set_expected_data("0".as_bytes().to_vec());
8116        do_process_instruction(
8117            amount_to_ui_amount(&program_id, &mint_key, 0).unwrap(),
8118            vec![&mut mint_account],
8119        )
8120        .unwrap();
8121    }
8122
8123    #[test]
8124    #[serial]
8125    fn test_ui_amount_to_amount() {
8126        let program_id = crate::id();
8127        let owner_key = Pubkey::new_unique();
8128        let mint_key = Pubkey::new_unique();
8129        let mut mint_account =
8130            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
8131        let mut rent_sysvar = rent_sysvar();
8132
8133        // fail if an invalid mint is passed in
8134        assert_eq!(
8135            Err(TokenError::InvalidMint.into()),
8136            do_process_instruction(
8137                ui_amount_to_amount(&program_id, &mint_key, "1.1").unwrap(),
8138                vec![&mut mint_account],
8139            )
8140        );
8141
8142        // create mint
8143        do_process_instruction(
8144            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
8145            vec![&mut mint_account, &mut rent_sysvar],
8146        )
8147        .unwrap();
8148
8149        set_expected_data(23u64.to_le_bytes().to_vec());
8150        do_process_instruction(
8151            ui_amount_to_amount(&program_id, &mint_key, "0.23").unwrap(),
8152            vec![&mut mint_account],
8153        )
8154        .unwrap();
8155
8156        set_expected_data(20u64.to_le_bytes().to_vec());
8157        do_process_instruction(
8158            ui_amount_to_amount(&program_id, &mint_key, "0.20").unwrap(),
8159            vec![&mut mint_account],
8160        )
8161        .unwrap();
8162
8163        set_expected_data(20u64.to_le_bytes().to_vec());
8164        do_process_instruction(
8165            ui_amount_to_amount(&program_id, &mint_key, "0.2000").unwrap(),
8166            vec![&mut mint_account],
8167        )
8168        .unwrap();
8169
8170        set_expected_data(20u64.to_le_bytes().to_vec());
8171        do_process_instruction(
8172            ui_amount_to_amount(&program_id, &mint_key, ".20").unwrap(),
8173            vec![&mut mint_account],
8174        )
8175        .unwrap();
8176
8177        set_expected_data(110u64.to_le_bytes().to_vec());
8178        do_process_instruction(
8179            ui_amount_to_amount(&program_id, &mint_key, "1.1").unwrap(),
8180            vec![&mut mint_account],
8181        )
8182        .unwrap();
8183
8184        set_expected_data(110u64.to_le_bytes().to_vec());
8185        do_process_instruction(
8186            ui_amount_to_amount(&program_id, &mint_key, "1.10").unwrap(),
8187            vec![&mut mint_account],
8188        )
8189        .unwrap();
8190
8191        set_expected_data(4200u64.to_le_bytes().to_vec());
8192        do_process_instruction(
8193            ui_amount_to_amount(&program_id, &mint_key, "42").unwrap(),
8194            vec![&mut mint_account],
8195        )
8196        .unwrap();
8197
8198        set_expected_data(4200u64.to_le_bytes().to_vec());
8199        do_process_instruction(
8200            ui_amount_to_amount(&program_id, &mint_key, "42.").unwrap(),
8201            vec![&mut mint_account],
8202        )
8203        .unwrap();
8204
8205        set_expected_data(0u64.to_le_bytes().to_vec());
8206        do_process_instruction(
8207            ui_amount_to_amount(&program_id, &mint_key, "0").unwrap(),
8208            vec![&mut mint_account],
8209        )
8210        .unwrap();
8211
8212        // fail if invalid ui_amount passed in
8213        assert_eq!(
8214            Err(ProgramError::InvalidArgument),
8215            do_process_instruction(
8216                ui_amount_to_amount(&program_id, &mint_key, "").unwrap(),
8217                vec![&mut mint_account],
8218            )
8219        );
8220        assert_eq!(
8221            Err(ProgramError::InvalidArgument),
8222            do_process_instruction(
8223                ui_amount_to_amount(&program_id, &mint_key, ".").unwrap(),
8224                vec![&mut mint_account],
8225            )
8226        );
8227        assert_eq!(
8228            Err(ProgramError::InvalidArgument),
8229            do_process_instruction(
8230                ui_amount_to_amount(&program_id, &mint_key, "0.111").unwrap(),
8231                vec![&mut mint_account],
8232            )
8233        );
8234        assert_eq!(
8235            Err(ProgramError::InvalidArgument),
8236            do_process_instruction(
8237                ui_amount_to_amount(&program_id, &mint_key, "0.t").unwrap(),
8238                vec![&mut mint_account],
8239            )
8240        );
8241    }
8242
8243    #[test]
8244    #[serial]
8245    fn test_withdraw_excess_kelvins_from_multisig() {
8246        {
8247            use std::sync::Once;
8248            static ONCE: Once = Once::new();
8249
8250            ONCE.call_once(|| {
8251                rialo_s_sysvar::program_stubs::set_syscall_stubs(Box::new(SyscallStubs {}));
8252            });
8253        }
8254        let program_id = crate::id();
8255
8256        let mut kelvins = 0;
8257        let mut destination_data = vec![];
8258        let system_program_id = system_program::id();
8259        let destination_key = Pubkey::new_unique();
8260        let destination_info = AccountInfo::new(
8261            &destination_key,
8262            true,
8263            false,
8264            &mut kelvins,
8265            &mut destination_data,
8266            &system_program_id,
8267            false,
8268            Epoch::default(),
8269        );
8270
8271        let multisig_key = Pubkey::new_unique();
8272        let mut multisig_account = SolanaAccount::new(0, Multisig::get_packed_len(), &program_id);
8273        let excess_kelvins = 4_000_000_000_000;
8274        multisig_account.kelvins = excess_kelvins + multisig_minimum_balance();
8275        let mut signer_keys = [Pubkey::default(); MAX_SIGNERS];
8276
8277        for signer_key in signer_keys.iter_mut().take(MAX_SIGNERS) {
8278            *signer_key = Pubkey::new_unique();
8279        }
8280        let signer_refs: Vec<&Pubkey> = signer_keys.iter().collect();
8281        let mut signer_kelvins = 0;
8282        let mut signer_data = vec![];
8283        let mut signers: Vec<AccountInfo<'_>> = vec![
8284            AccountInfo::new(
8285                &destination_key,
8286                true,
8287                false,
8288                &mut signer_kelvins,
8289                &mut signer_data,
8290                &program_id,
8291                false,
8292                Epoch::default(),
8293            );
8294            MAX_SIGNERS + 1
8295        ];
8296        for (signer, key) in signers.iter_mut().zip(&signer_keys) {
8297            signer.key = key;
8298        }
8299
8300        let mut multisig =
8301            Multisig::unpack_unchecked(&vec![0; Multisig::get_packed_len()]).unwrap();
8302        multisig.m = MAX_SIGNERS as u8;
8303        multisig.n = MAX_SIGNERS as u8;
8304        multisig.signers = signer_keys;
8305        multisig.is_initialized = true;
8306        Multisig::pack(multisig, &mut multisig_account.data).unwrap();
8307
8308        let multisig_info: AccountInfo<'_> = (&multisig_key, true, &mut multisig_account).into();
8309
8310        let mut signers_infos = vec![
8311            multisig_info.clone(),
8312            destination_info.clone(),
8313            multisig_info.clone(),
8314        ];
8315        signers_infos.extend(signers);
8316        do_process_instruction_dups(
8317            withdraw_excess_kelvins(
8318                &program_id,
8319                &multisig_key,
8320                &destination_key,
8321                &multisig_key,
8322                &signer_refs,
8323            )
8324            .unwrap(),
8325            signers_infos,
8326        )
8327        .unwrap();
8328
8329        assert_eq!(destination_info.kelvins(), excess_kelvins);
8330    }
8331
8332    #[test]
8333    #[serial]
8334    fn test_withdraw_excess_kelvins_from_account() {
8335        let excess_kelvins = 4_000_000_000_000;
8336
8337        let program_id = crate::id();
8338        let account_key = Pubkey::new_unique();
8339        let mut account_account = SolanaAccount::new(
8340            excess_kelvins + account_minimum_balance(),
8341            Account::get_packed_len(),
8342            &program_id,
8343        );
8344
8345        let system_program_id = system_program::id();
8346        let owner_key = Pubkey::new_unique();
8347
8348        let mut destination_kelvins = 0;
8349        let mut destination_data = vec![];
8350        let destination_key = Pubkey::new_unique();
8351        let destination_info = AccountInfo::new(
8352            &destination_key,
8353            true,
8354            false,
8355            &mut destination_kelvins,
8356            &mut destination_data,
8357            &system_program_id,
8358            false,
8359            Epoch::default(),
8360        );
8361        let mint_key = Pubkey::new_unique();
8362        let mut mint_account =
8363            SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
8364
8365        let mut rent_sysvar = rent_sysvar();
8366        do_process_instruction(
8367            initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
8368            vec![&mut mint_account, &mut rent_sysvar],
8369        )
8370        .unwrap();
8371
8372        let mint_info = AccountInfo::new(
8373            &mint_key,
8374            true,
8375            false,
8376            &mut mint_account.kelvins,
8377            &mut mint_account.data,
8378            &program_id,
8379            false,
8380            Epoch::default(),
8381        );
8382
8383        let account_info: AccountInfo<'_> = (&account_key, true, &mut account_account).into();
8384
8385        do_process_instruction_dups(
8386            initialize_account3(&program_id, &account_key, &mint_key, &account_key).unwrap(),
8387            vec![account_info.clone(), mint_info.clone()],
8388        )
8389        .unwrap();
8390
8391        do_process_instruction_dups(
8392            withdraw_excess_kelvins(
8393                &program_id,
8394                &account_key,
8395                &destination_key,
8396                &account_key,
8397                &[],
8398            )
8399            .unwrap(),
8400            vec![
8401                account_info.clone(),
8402                destination_info.clone(),
8403                account_info.clone(),
8404            ],
8405        )
8406        .unwrap();
8407
8408        assert_eq!(destination_info.kelvins(), excess_kelvins);
8409    }
8410
8411    #[test]
8412    #[serial]
8413    fn test_withdraw_excess_kelvins_from_mint() {
8414        let excess_kelvins = 4_000_000_000_000;
8415
8416        let program_id = crate::id();
8417        let system_program_id = system_program::id();
8418
8419        let mut destination_kelvins = 0;
8420        let mut destination_data = vec![];
8421        let destination_key = Pubkey::new_unique();
8422        let destination_info = AccountInfo::new(
8423            &destination_key,
8424            true,
8425            false,
8426            &mut destination_kelvins,
8427            &mut destination_data,
8428            &system_program_id,
8429            false,
8430            Epoch::default(),
8431        );
8432        let mint_key = Pubkey::new_unique();
8433        let mut mint_account = SolanaAccount::new(
8434            excess_kelvins + mint_minimum_balance(),
8435            Mint::get_packed_len(),
8436            &program_id,
8437        );
8438        let mut rent_sysvar = rent_sysvar();
8439
8440        do_process_instruction(
8441            initialize_mint(&program_id, &mint_key, &mint_key, None, 2).unwrap(),
8442            vec![&mut mint_account, &mut rent_sysvar],
8443        )
8444        .unwrap();
8445
8446        let mint_info: AccountInfo<'_> = (&mint_key, true, &mut mint_account).into();
8447
8448        do_process_instruction_dups(
8449            withdraw_excess_kelvins(&program_id, &mint_key, &destination_key, &mint_key, &[])
8450                .unwrap(),
8451            vec![
8452                mint_info.clone(),
8453                destination_info.clone(),
8454                mint_info.clone(),
8455            ],
8456        )
8457        .unwrap();
8458
8459        assert_eq!(destination_info.kelvins(), excess_kelvins);
8460    }
8461
8462    #[test]
8463    #[serial]
8464    fn test_withdraw_excess_kelvins_from_mint_with_no_mint_authority() {
8465        let excess_kelvins = 4_000_000_000_000;
8466
8467        let program_id = crate::id();
8468        let system_program_id = system_program::id();
8469
8470        let mut destination_kelvins = 0;
8471        let mut destination_data = vec![];
8472        let destination_key = Pubkey::new_unique();
8473        let destination_info = AccountInfo::new(
8474            &destination_key,
8475            true,
8476            false,
8477            &mut destination_kelvins,
8478            &mut destination_data,
8479            &system_program_id,
8480            false,
8481            Epoch::default(),
8482        );
8483        let mint_key = Pubkey::new_unique();
8484        let mut mint_account = SolanaAccount::new(
8485            excess_kelvins + mint_minimum_balance(),
8486            Mint::get_packed_len(),
8487            &program_id,
8488        );
8489        let mut mint_authority_kelvins = 0;
8490        let mut mint_authority_data = vec![];
8491        let mint_authority_key = Pubkey::new_unique();
8492        let mint_authority_info = AccountInfo::new(
8493            &mint_authority_key,
8494            true,
8495            false,
8496            &mut mint_authority_kelvins,
8497            &mut mint_authority_data,
8498            &system_program_id,
8499            false,
8500            Epoch::default(),
8501        );
8502        let mut rent_sysvar = rent_sysvar();
8503
8504        do_process_instruction(
8505            initialize_mint(&program_id, &mint_key, &mint_authority_key, None, 2).unwrap(),
8506            vec![&mut mint_account, &mut rent_sysvar],
8507        )
8508        .unwrap();
8509
8510        let mint_info: AccountInfo<'_> = (&mint_key, true, &mut mint_account).into();
8511
8512        // fail when withdrawing with the mint as authority when there is
8513        // a mint authority set
8514        assert_eq!(
8515            Err(TokenError::OwnerMismatch.into()),
8516            do_process_instruction_dups(
8517                withdraw_excess_kelvins(&program_id, &mint_key, &destination_key, &mint_key, &[])
8518                    .unwrap(),
8519                vec![
8520                    mint_info.clone(),
8521                    destination_info.clone(),
8522                    mint_info.clone(),
8523                ],
8524            )
8525        );
8526
8527        do_process_instruction_dups(
8528            set_authority(
8529                &program_id,
8530                &mint_key,
8531                None,
8532                AuthorityType::MintTokens,
8533                &mint_authority_key,
8534                &[],
8535            )
8536            .unwrap(),
8537            vec![mint_info.clone(), mint_authority_info.clone()],
8538        )
8539        .unwrap();
8540
8541        // fail when withdrawing with the previous mint authority
8542        assert_eq!(
8543            Err(TokenError::AuthorityTypeNotSupported.into()),
8544            do_process_instruction_dups(
8545                withdraw_excess_kelvins(
8546                    &program_id,
8547                    &mint_key,
8548                    &destination_key,
8549                    &mint_authority_key,
8550                    &[]
8551                )
8552                .unwrap(),
8553                vec![
8554                    mint_info.clone(),
8555                    destination_info.clone(),
8556                    mint_authority_info.clone(),
8557                ],
8558            )
8559        );
8560
8561        do_process_instruction_dups(
8562            withdraw_excess_kelvins(&program_id, &mint_key, &destination_key, &mint_key, &[])
8563                .unwrap(),
8564            vec![
8565                mint_info.clone(),
8566                destination_info.clone(),
8567                mint_info.clone(),
8568            ],
8569        )
8570        .unwrap();
8571
8572        assert_eq!(destination_info.kelvins(), excess_kelvins);
8573    }
8574}