atlas_token/
processor.rs

1//! Program state processor
2
3use {
4    crate::{
5        amount_to_ui_amount_string_trimmed,
6        error::TokenError,
7        instruction::{is_valid_signer_index, AuthorityType, TokenInstruction, MAX_SIGNERS},
8        state::{Account, AccountState, Mint, Multisig},
9        try_ui_amount_into_amount,
10    },
11    atlas_account_info::{next_account_info, AccountInfo},
12    atlas_cpi::set_return_data,
13    atlas_msg::msg,
14    atlas_program_error::{ProgramError, ProgramResult},
15    atlas_program_memory,
16    atlas_program_option::COption,
17    atlas_program_pack::{IsInitialized, Pack},
18    atlas_pubkey::{Pubkey, PUBKEY_BYTES},
19    atlas_rent::Rent,
20    atlas_sdk_ids::system_program,
21    atlas_sysvar::{Sysvar, SysvarSerialize},
22};
23
24/// Program state handler.
25pub struct Processor {}
26impl Processor {
27    fn _process_initialize_mint(
28        accounts: &[AccountInfo],
29        decimals: u8,
30        mint_authority: Pubkey,
31        freeze_authority: COption<Pubkey>,
32        rent_sysvar_account: bool,
33    ) -> ProgramResult {
34        let account_info_iter = &mut accounts.iter();
35        let mint_info = next_account_info(account_info_iter)?;
36        let mint_data_len = mint_info.data_len();
37        let rent = if rent_sysvar_account {
38            Rent::from_account_info(next_account_info(account_info_iter)?)?
39        } else {
40            Rent::get()?
41        };
42
43        let mut mint = Mint::unpack_unchecked(&mint_info.data.borrow())?;
44        if mint.is_initialized {
45            return Err(TokenError::AlreadyInUse.into());
46        }
47
48        if !rent.is_exempt(mint_info.lamports(), mint_data_len) {
49            return Err(TokenError::NotRentExempt.into());
50        }
51
52        mint.mint_authority = COption::Some(mint_authority);
53        mint.decimals = decimals;
54        mint.is_initialized = true;
55        mint.freeze_authority = freeze_authority;
56
57        Mint::pack(mint, &mut mint_info.data.borrow_mut())?;
58
59        Ok(())
60    }
61
62    /// Processes an [`InitializeMint`](enum.TokenInstruction.html) instruction.
63    pub fn process_initialize_mint(
64        accounts: &[AccountInfo],
65        decimals: u8,
66        mint_authority: Pubkey,
67        freeze_authority: COption<Pubkey>,
68    ) -> ProgramResult {
69        Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority, true)
70    }
71
72    /// Processes an [`InitializeMint2`](enum.TokenInstruction.html)
73    /// instruction.
74    pub fn process_initialize_mint2(
75        accounts: &[AccountInfo],
76        decimals: u8,
77        mint_authority: Pubkey,
78        freeze_authority: COption<Pubkey>,
79    ) -> ProgramResult {
80        Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority, false)
81    }
82
83    fn _process_initialize_account(
84        program_id: &Pubkey,
85        accounts: &[AccountInfo],
86        owner: Option<&Pubkey>,
87        rent_sysvar_account: bool,
88    ) -> ProgramResult {
89        let account_info_iter = &mut accounts.iter();
90        let new_account_info = next_account_info(account_info_iter)?;
91        let mint_info = next_account_info(account_info_iter)?;
92        let owner = if let Some(owner) = owner {
93            owner
94        } else {
95            next_account_info(account_info_iter)?.key
96        };
97        let new_account_info_data_len = new_account_info.data_len();
98        let rent = if rent_sysvar_account {
99            Rent::from_account_info(next_account_info(account_info_iter)?)?
100        } else {
101            Rent::get()?
102        };
103
104        let mut account = Account::unpack_unchecked(&new_account_info.data.borrow())?;
105        if account.is_initialized() {
106            return Err(TokenError::AlreadyInUse.into());
107        }
108
109        if !rent.is_exempt(new_account_info.lamports(), new_account_info_data_len) {
110            return Err(TokenError::NotRentExempt.into());
111        }
112
113        let is_native_mint = Self::cmp_pubkeys(mint_info.key, &crate::native_mint::id());
114        if !is_native_mint {
115            Self::check_account_owner(program_id, mint_info)?;
116            let _ = Mint::unpack(&mint_info.data.borrow_mut())
117                .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
118        }
119
120        account.mint = *mint_info.key;
121        account.owner = *owner;
122        account.close_authority = COption::None;
123        account.delegate = COption::None;
124        account.delegated_amount = 0;
125        account.state = AccountState::Initialized;
126        if is_native_mint {
127            let rent_exempt_reserve = rent.minimum_balance(new_account_info_data_len);
128            account.is_native = COption::Some(rent_exempt_reserve);
129            account.amount = new_account_info
130                .lamports()
131                .checked_sub(rent_exempt_reserve)
132                .ok_or(TokenError::Overflow)?;
133        } else {
134            account.is_native = COption::None;
135            account.amount = 0;
136        };
137
138        Account::pack(account, &mut new_account_info.data.borrow_mut())?;
139
140        Ok(())
141    }
142
143    /// Processes an [`InitializeAccount`](enum.TokenInstruction.html)
144    /// instruction.
145    pub fn process_initialize_account(
146        program_id: &Pubkey,
147        accounts: &[AccountInfo],
148    ) -> ProgramResult {
149        Self::_process_initialize_account(program_id, accounts, None, true)
150    }
151
152    /// Processes an [`InitializeAccount2`](enum.TokenInstruction.html)
153    /// instruction.
154    pub fn process_initialize_account2(
155        program_id: &Pubkey,
156        accounts: &[AccountInfo],
157        owner: Pubkey,
158    ) -> ProgramResult {
159        Self::_process_initialize_account(program_id, accounts, Some(&owner), true)
160    }
161
162    /// Processes an [`InitializeAccount3`](enum.TokenInstruction.html)
163    /// instruction.
164    pub fn process_initialize_account3(
165        program_id: &Pubkey,
166        accounts: &[AccountInfo],
167        owner: Pubkey,
168    ) -> ProgramResult {
169        Self::_process_initialize_account(program_id, accounts, Some(&owner), false)
170    }
171
172    fn _process_initialize_multisig(
173        accounts: &[AccountInfo],
174        m: u8,
175        rent_sysvar_account: bool,
176    ) -> ProgramResult {
177        let account_info_iter = &mut accounts.iter();
178        let multisig_info = next_account_info(account_info_iter)?;
179        let multisig_info_data_len = multisig_info.data_len();
180        let rent = if rent_sysvar_account {
181            Rent::from_account_info(next_account_info(account_info_iter)?)?
182        } else {
183            Rent::get()?
184        };
185
186        let mut multisig = Multisig::unpack_unchecked(&multisig_info.data.borrow())?;
187        if multisig.is_initialized {
188            return Err(TokenError::AlreadyInUse.into());
189        }
190
191        if !rent.is_exempt(multisig_info.lamports(), multisig_info_data_len) {
192            return Err(TokenError::NotRentExempt.into());
193        }
194
195        let signer_infos = account_info_iter.as_slice();
196        multisig.m = m;
197        multisig.n = signer_infos.len() as u8;
198        if !is_valid_signer_index(multisig.n as usize) {
199            return Err(TokenError::InvalidNumberOfProvidedSigners.into());
200        }
201        if !is_valid_signer_index(multisig.m as usize) {
202            return Err(TokenError::InvalidNumberOfRequiredSigners.into());
203        }
204        for (i, signer_info) in signer_infos.iter().enumerate() {
205            multisig.signers[i] = *signer_info.key;
206        }
207        multisig.is_initialized = true;
208
209        Multisig::pack(multisig, &mut multisig_info.data.borrow_mut())?;
210
211        Ok(())
212    }
213
214    /// Processes a [`InitializeMultisig`](enum.TokenInstruction.html)
215    /// instruction.
216    pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult {
217        Self::_process_initialize_multisig(accounts, m, true)
218    }
219
220    /// Processes a [`InitializeMultisig2`](enum.TokenInstruction.html)
221    /// instruction.
222    pub fn process_initialize_multisig2(accounts: &[AccountInfo], m: u8) -> ProgramResult {
223        Self::_process_initialize_multisig(accounts, m, false)
224    }
225
226    /// Processes a [`Transfer`](enum.TokenInstruction.html) instruction.
227    pub fn process_transfer(
228        program_id: &Pubkey,
229        accounts: &[AccountInfo],
230        amount: u64,
231        expected_decimals: Option<u8>,
232    ) -> ProgramResult {
233        let account_info_iter = &mut accounts.iter();
234
235        let source_account_info = next_account_info(account_info_iter)?;
236
237        let expected_mint_info = if let Some(expected_decimals) = expected_decimals {
238            Some((next_account_info(account_info_iter)?, expected_decimals))
239        } else {
240            None
241        };
242
243        let destination_account_info = next_account_info(account_info_iter)?;
244        let authority_info = next_account_info(account_info_iter)?;
245
246        let mut source_account = Account::unpack(&source_account_info.data.borrow())?;
247        let mut destination_account = Account::unpack(&destination_account_info.data.borrow())?;
248
249        if source_account.is_frozen() || destination_account.is_frozen() {
250            return Err(TokenError::AccountFrozen.into());
251        }
252        if source_account.amount < amount {
253            return Err(TokenError::InsufficientFunds.into());
254        }
255        if !Self::cmp_pubkeys(&source_account.mint, &destination_account.mint) {
256            return Err(TokenError::MintMismatch.into());
257        }
258
259        if let Some((mint_info, expected_decimals)) = expected_mint_info {
260            if !Self::cmp_pubkeys(mint_info.key, &source_account.mint) {
261                return Err(TokenError::MintMismatch.into());
262            }
263
264            let mint = Mint::unpack(&mint_info.data.borrow_mut())?;
265            if expected_decimals != mint.decimals {
266                return Err(TokenError::MintDecimalsMismatch.into());
267            }
268        }
269
270        let self_transfer =
271            Self::cmp_pubkeys(source_account_info.key, destination_account_info.key);
272
273        match source_account.delegate {
274            COption::Some(ref delegate) if Self::cmp_pubkeys(authority_info.key, delegate) => {
275                Self::validate_owner(
276                    program_id,
277                    delegate,
278                    authority_info,
279                    account_info_iter.as_slice(),
280                )?;
281                if source_account.delegated_amount < amount {
282                    return Err(TokenError::InsufficientFunds.into());
283                }
284                if !self_transfer {
285                    source_account.delegated_amount = source_account
286                        .delegated_amount
287                        .checked_sub(amount)
288                        .ok_or(TokenError::Overflow)?;
289                    if source_account.delegated_amount == 0 {
290                        source_account.delegate = COption::None;
291                    }
292                }
293            }
294            _ => Self::validate_owner(
295                program_id,
296                &source_account.owner,
297                authority_info,
298                account_info_iter.as_slice(),
299            )?,
300        };
301
302        if self_transfer || amount == 0 {
303            Self::check_account_owner(program_id, source_account_info)?;
304            Self::check_account_owner(program_id, destination_account_info)?;
305        }
306
307        // This check MUST occur just before the amounts are manipulated
308        // to ensure self-transfers are fully validated
309        if self_transfer {
310            return Ok(());
311        }
312
313        source_account.amount = source_account
314            .amount
315            .checked_sub(amount)
316            .ok_or(TokenError::Overflow)?;
317        destination_account.amount = destination_account
318            .amount
319            .checked_add(amount)
320            .ok_or(TokenError::Overflow)?;
321
322        if source_account.is_native() {
323            let source_starting_lamports = source_account_info.lamports();
324            **source_account_info.lamports.borrow_mut() = source_starting_lamports
325                .checked_sub(amount)
326                .ok_or(TokenError::Overflow)?;
327
328            let destination_starting_lamports = destination_account_info.lamports();
329            **destination_account_info.lamports.borrow_mut() = destination_starting_lamports
330                .checked_add(amount)
331                .ok_or(TokenError::Overflow)?;
332        }
333
334        Account::pack(source_account, &mut source_account_info.data.borrow_mut())?;
335        Account::pack(
336            destination_account,
337            &mut destination_account_info.data.borrow_mut(),
338        )?;
339
340        Ok(())
341    }
342
343    /// Processes an [`Approve`](enum.TokenInstruction.html) instruction.
344    pub fn process_approve(
345        program_id: &Pubkey,
346        accounts: &[AccountInfo],
347        amount: u64,
348        expected_decimals: Option<u8>,
349    ) -> ProgramResult {
350        let account_info_iter = &mut accounts.iter();
351
352        let source_account_info = next_account_info(account_info_iter)?;
353
354        let expected_mint_info = if let Some(expected_decimals) = expected_decimals {
355            Some((next_account_info(account_info_iter)?, expected_decimals))
356        } else {
357            None
358        };
359        let delegate_info = next_account_info(account_info_iter)?;
360        let owner_info = next_account_info(account_info_iter)?;
361
362        let mut source_account = Account::unpack(&source_account_info.data.borrow())?;
363
364        if source_account.is_frozen() {
365            return Err(TokenError::AccountFrozen.into());
366        }
367
368        if let Some((mint_info, expected_decimals)) = expected_mint_info {
369            if !Self::cmp_pubkeys(mint_info.key, &source_account.mint) {
370                return Err(TokenError::MintMismatch.into());
371            }
372
373            let mint = Mint::unpack(&mint_info.data.borrow_mut())?;
374            if expected_decimals != mint.decimals {
375                return Err(TokenError::MintDecimalsMismatch.into());
376            }
377        }
378
379        Self::validate_owner(
380            program_id,
381            &source_account.owner,
382            owner_info,
383            account_info_iter.as_slice(),
384        )?;
385
386        source_account.delegate = COption::Some(*delegate_info.key);
387        source_account.delegated_amount = amount;
388
389        Account::pack(source_account, &mut source_account_info.data.borrow_mut())?;
390
391        Ok(())
392    }
393
394    /// Processes an [`Revoke`](enum.TokenInstruction.html) instruction.
395    pub fn process_revoke(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
396        let account_info_iter = &mut accounts.iter();
397        let source_account_info = next_account_info(account_info_iter)?;
398
399        let mut source_account = Account::unpack(&source_account_info.data.borrow())?;
400
401        let owner_info = next_account_info(account_info_iter)?;
402
403        if source_account.is_frozen() {
404            return Err(TokenError::AccountFrozen.into());
405        }
406
407        Self::validate_owner(
408            program_id,
409            &source_account.owner,
410            owner_info,
411            account_info_iter.as_slice(),
412        )?;
413
414        source_account.delegate = COption::None;
415        source_account.delegated_amount = 0;
416
417        Account::pack(source_account, &mut source_account_info.data.borrow_mut())?;
418
419        Ok(())
420    }
421
422    /// Processes a [`SetAuthority`](enum.TokenInstruction.html) instruction.
423    pub fn process_set_authority(
424        program_id: &Pubkey,
425        accounts: &[AccountInfo],
426        authority_type: AuthorityType,
427        new_authority: COption<Pubkey>,
428    ) -> ProgramResult {
429        let account_info_iter = &mut accounts.iter();
430        let account_info = next_account_info(account_info_iter)?;
431        let authority_info = next_account_info(account_info_iter)?;
432
433        if account_info.data_len() == Account::get_packed_len() {
434            let mut account = Account::unpack(&account_info.data.borrow())?;
435
436            if account.is_frozen() {
437                return Err(TokenError::AccountFrozen.into());
438            }
439
440            match authority_type {
441                AuthorityType::AccountOwner => {
442                    Self::validate_owner(
443                        program_id,
444                        &account.owner,
445                        authority_info,
446                        account_info_iter.as_slice(),
447                    )?;
448
449                    if let COption::Some(authority) = new_authority {
450                        account.owner = authority;
451                    } else {
452                        return Err(TokenError::InvalidInstruction.into());
453                    }
454
455                    account.delegate = COption::None;
456                    account.delegated_amount = 0;
457
458                    if account.is_native() {
459                        account.close_authority = COption::None;
460                    }
461                }
462                AuthorityType::CloseAccount => {
463                    let authority = account.close_authority.unwrap_or(account.owner);
464                    Self::validate_owner(
465                        program_id,
466                        &authority,
467                        authority_info,
468                        account_info_iter.as_slice(),
469                    )?;
470                    account.close_authority = new_authority;
471                }
472                _ => {
473                    return Err(TokenError::AuthorityTypeNotSupported.into());
474                }
475            }
476            Account::pack(account, &mut account_info.data.borrow_mut())?;
477        } else if account_info.data_len() == Mint::get_packed_len() {
478            let mut mint = Mint::unpack(&account_info.data.borrow())?;
479            match authority_type {
480                AuthorityType::MintTokens => {
481                    // Once a mint's supply is fixed, it cannot be undone by setting a new
482                    // mint_authority
483                    let mint_authority = mint
484                        .mint_authority
485                        .ok_or(Into::<ProgramError>::into(TokenError::FixedSupply))?;
486                    Self::validate_owner(
487                        program_id,
488                        &mint_authority,
489                        authority_info,
490                        account_info_iter.as_slice(),
491                    )?;
492                    mint.mint_authority = new_authority;
493                }
494                AuthorityType::FreezeAccount => {
495                    // Once a mint's freeze authority is disabled, it cannot be re-enabled by
496                    // setting a new freeze_authority
497                    let freeze_authority = mint
498                        .freeze_authority
499                        .ok_or(Into::<ProgramError>::into(TokenError::MintCannotFreeze))?;
500                    Self::validate_owner(
501                        program_id,
502                        &freeze_authority,
503                        authority_info,
504                        account_info_iter.as_slice(),
505                    )?;
506                    mint.freeze_authority = new_authority;
507                }
508                _ => {
509                    return Err(TokenError::AuthorityTypeNotSupported.into());
510                }
511            }
512            Mint::pack(mint, &mut account_info.data.borrow_mut())?;
513        } else {
514            return Err(ProgramError::InvalidArgument);
515        }
516
517        Ok(())
518    }
519
520    /// Processes a [`MintTo`](enum.TokenInstruction.html) instruction.
521    pub fn process_mint_to(
522        program_id: &Pubkey,
523        accounts: &[AccountInfo],
524        amount: u64,
525        expected_decimals: Option<u8>,
526    ) -> ProgramResult {
527        let account_info_iter = &mut accounts.iter();
528        let mint_info = next_account_info(account_info_iter)?;
529        let destination_account_info = next_account_info(account_info_iter)?;
530        let owner_info = next_account_info(account_info_iter)?;
531
532        let mut destination_account = Account::unpack(&destination_account_info.data.borrow())?;
533        if destination_account.is_frozen() {
534            return Err(TokenError::AccountFrozen.into());
535        }
536
537        if destination_account.is_native() {
538            return Err(TokenError::NativeNotSupported.into());
539        }
540        if !Self::cmp_pubkeys(mint_info.key, &destination_account.mint) {
541            return Err(TokenError::MintMismatch.into());
542        }
543
544        let mut mint = Mint::unpack(&mint_info.data.borrow())?;
545        if let Some(expected_decimals) = expected_decimals {
546            if expected_decimals != mint.decimals {
547                return Err(TokenError::MintDecimalsMismatch.into());
548            }
549        }
550
551        match mint.mint_authority {
552            COption::Some(mint_authority) => Self::validate_owner(
553                program_id,
554                &mint_authority,
555                owner_info,
556                account_info_iter.as_slice(),
557            )?,
558            COption::None => return Err(TokenError::FixedSupply.into()),
559        }
560
561        if amount == 0 {
562            Self::check_account_owner(program_id, mint_info)?;
563            Self::check_account_owner(program_id, destination_account_info)?;
564        }
565
566        destination_account.amount = destination_account
567            .amount
568            .checked_add(amount)
569            .ok_or(TokenError::Overflow)?;
570
571        mint.supply = mint
572            .supply
573            .checked_add(amount)
574            .ok_or(TokenError::Overflow)?;
575
576        Account::pack(
577            destination_account,
578            &mut destination_account_info.data.borrow_mut(),
579        )?;
580        Mint::pack(mint, &mut mint_info.data.borrow_mut())?;
581
582        Ok(())
583    }
584
585    /// Processes a [`Burn`](enum.TokenInstruction.html) instruction.
586    pub fn process_burn(
587        program_id: &Pubkey,
588        accounts: &[AccountInfo],
589        amount: u64,
590        expected_decimals: Option<u8>,
591    ) -> ProgramResult {
592        let account_info_iter = &mut accounts.iter();
593
594        let source_account_info = next_account_info(account_info_iter)?;
595        let mint_info = next_account_info(account_info_iter)?;
596        let authority_info = next_account_info(account_info_iter)?;
597
598        let mut source_account = Account::unpack(&source_account_info.data.borrow())?;
599        let mut mint = Mint::unpack(&mint_info.data.borrow())?;
600
601        if source_account.is_frozen() {
602            return Err(TokenError::AccountFrozen.into());
603        }
604        if source_account.is_native() {
605            return Err(TokenError::NativeNotSupported.into());
606        }
607        if source_account.amount < amount {
608            return Err(TokenError::InsufficientFunds.into());
609        }
610        if !Self::cmp_pubkeys(mint_info.key, &source_account.mint) {
611            return Err(TokenError::MintMismatch.into());
612        }
613
614        if let Some(expected_decimals) = expected_decimals {
615            if expected_decimals != mint.decimals {
616                return Err(TokenError::MintDecimalsMismatch.into());
617            }
618        }
619
620        if !source_account.is_owned_by_system_program_or_incinerator() {
621            match source_account.delegate {
622                COption::Some(ref delegate) if Self::cmp_pubkeys(authority_info.key, delegate) => {
623                    Self::validate_owner(
624                        program_id,
625                        delegate,
626                        authority_info,
627                        account_info_iter.as_slice(),
628                    )?;
629
630                    if source_account.delegated_amount < amount {
631                        return Err(TokenError::InsufficientFunds.into());
632                    }
633                    source_account.delegated_amount = source_account
634                        .delegated_amount
635                        .checked_sub(amount)
636                        .ok_or(TokenError::Overflow)?;
637                    if source_account.delegated_amount == 0 {
638                        source_account.delegate = COption::None;
639                    }
640                }
641                _ => Self::validate_owner(
642                    program_id,
643                    &source_account.owner,
644                    authority_info,
645                    account_info_iter.as_slice(),
646                )?,
647            }
648        }
649
650        if amount == 0 {
651            Self::check_account_owner(program_id, source_account_info)?;
652            Self::check_account_owner(program_id, mint_info)?;
653        }
654
655        source_account.amount = source_account
656            .amount
657            .checked_sub(amount)
658            .ok_or(TokenError::Overflow)?;
659        mint.supply = mint
660            .supply
661            .checked_sub(amount)
662            .ok_or(TokenError::Overflow)?;
663
664        Account::pack(source_account, &mut source_account_info.data.borrow_mut())?;
665        Mint::pack(mint, &mut mint_info.data.borrow_mut())?;
666
667        Ok(())
668    }
669
670    /// Processes a [`CloseAccount`](enum.TokenInstruction.html) instruction.
671    pub fn process_close_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
672        let account_info_iter = &mut accounts.iter();
673        let source_account_info = next_account_info(account_info_iter)?;
674        let destination_account_info = next_account_info(account_info_iter)?;
675        let authority_info = next_account_info(account_info_iter)?;
676
677        if Self::cmp_pubkeys(source_account_info.key, destination_account_info.key) {
678            return Err(ProgramError::InvalidAccountData);
679        }
680
681        let source_account = Account::unpack(&source_account_info.data.borrow())?;
682        if !source_account.is_native() && source_account.amount != 0 {
683            return Err(TokenError::NonNativeHasBalance.into());
684        }
685
686        let authority = source_account
687            .close_authority
688            .unwrap_or(source_account.owner);
689        if !source_account.is_owned_by_system_program_or_incinerator() {
690            Self::validate_owner(
691                program_id,
692                &authority,
693                authority_info,
694                account_info_iter.as_slice(),
695            )?;
696        } else if !atlas_sdk_ids::incinerator::check_id(destination_account_info.key) {
697            return Err(ProgramError::InvalidAccountData);
698        }
699
700        let destination_starting_lamports = destination_account_info.lamports();
701        **destination_account_info.lamports.borrow_mut() = destination_starting_lamports
702            .checked_add(source_account_info.lamports())
703            .ok_or(TokenError::Overflow)?;
704
705        **source_account_info.lamports.borrow_mut() = 0;
706        delete_account(source_account_info)?;
707
708        Ok(())
709    }
710
711    /// Processes a [`FreezeAccount`](enum.TokenInstruction.html) or a
712    /// [`ThawAccount`](enum.TokenInstruction.html) instruction.
713    pub fn process_toggle_freeze_account(
714        program_id: &Pubkey,
715        accounts: &[AccountInfo],
716        freeze: bool,
717    ) -> ProgramResult {
718        let account_info_iter = &mut accounts.iter();
719        let source_account_info = next_account_info(account_info_iter)?;
720        let mint_info = next_account_info(account_info_iter)?;
721        let authority_info = next_account_info(account_info_iter)?;
722
723        let mut source_account = Account::unpack(&source_account_info.data.borrow())?;
724        if freeze && source_account.is_frozen() || !freeze && !source_account.is_frozen() {
725            return Err(TokenError::InvalidState.into());
726        }
727        if source_account.is_native() {
728            return Err(TokenError::NativeNotSupported.into());
729        }
730        if !Self::cmp_pubkeys(mint_info.key, &source_account.mint) {
731            return Err(TokenError::MintMismatch.into());
732        }
733
734        let mint = Mint::unpack(&mint_info.data.borrow_mut())?;
735        match mint.freeze_authority {
736            COption::Some(authority) => Self::validate_owner(
737                program_id,
738                &authority,
739                authority_info,
740                account_info_iter.as_slice(),
741            ),
742            COption::None => Err(TokenError::MintCannotFreeze.into()),
743        }?;
744
745        source_account.state = if freeze {
746            AccountState::Frozen
747        } else {
748            AccountState::Initialized
749        };
750
751        Account::pack(source_account, &mut source_account_info.data.borrow_mut())?;
752
753        Ok(())
754    }
755
756    /// Processes a [`SyncNative`](enum.TokenInstruction.html) instruction
757    pub fn process_sync_native(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
758        let account_info_iter = &mut accounts.iter();
759        let native_account_info = next_account_info(account_info_iter)?;
760        Self::check_account_owner(program_id, native_account_info)?;
761
762        let mut native_account = Account::unpack(&native_account_info.data.borrow())?;
763
764        if let COption::Some(rent_exempt_reserve) = native_account.is_native {
765            let new_amount = native_account_info
766                .lamports()
767                .checked_sub(rent_exempt_reserve)
768                .ok_or(TokenError::Overflow)?;
769            if new_amount < native_account.amount {
770                return Err(TokenError::InvalidState.into());
771            }
772            native_account.amount = new_amount;
773        } else {
774            return Err(TokenError::NonNativeNotSupported.into());
775        }
776
777        Account::pack(native_account, &mut native_account_info.data.borrow_mut())?;
778        Ok(())
779    }
780
781    /// Processes a [`GetAccountDataSize`](enum.TokenInstruction.html)
782    /// instruction
783    pub fn process_get_account_data_size(
784        program_id: &Pubkey,
785        accounts: &[AccountInfo],
786    ) -> ProgramResult {
787        let account_info_iter = &mut accounts.iter();
788        // make sure the mint is valid
789        let mint_info = next_account_info(account_info_iter)?;
790        Self::check_account_owner(program_id, mint_info)?;
791        let _ = Mint::unpack(&mint_info.data.borrow())
792            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
793        set_return_data(&Account::LEN.to_le_bytes());
794        Ok(())
795    }
796
797    /// Processes an [`InitializeImmutableOwner`](enum.TokenInstruction.html)
798    /// instruction
799    pub fn process_initialize_immutable_owner(accounts: &[AccountInfo]) -> ProgramResult {
800        let account_info_iter = &mut accounts.iter();
801        let token_account_info = next_account_info(account_info_iter)?;
802        let account = Account::unpack_unchecked(&token_account_info.data.borrow())?;
803        if account.is_initialized() {
804            return Err(TokenError::AlreadyInUse.into());
805        }
806        msg!("Please upgrade to ATLAS Token 2022 for immutable owner support");
807        Ok(())
808    }
809
810    /// Processes an [`AmountToUiAmount`](enum.TokenInstruction.html)
811    /// instruction
812    pub fn process_amount_to_ui_amount(
813        program_id: &Pubkey,
814        accounts: &[AccountInfo],
815        amount: u64,
816    ) -> ProgramResult {
817        let account_info_iter = &mut accounts.iter();
818        let mint_info = next_account_info(account_info_iter)?;
819        Self::check_account_owner(program_id, mint_info)?;
820
821        let mint = Mint::unpack(&mint_info.data.borrow_mut())
822            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
823        let ui_amount = amount_to_ui_amount_string_trimmed(amount, mint.decimals);
824
825        set_return_data(&ui_amount.into_bytes());
826        Ok(())
827    }
828
829    /// Processes an [`AmountToUiAmount`](enum.TokenInstruction.html)
830    /// instruction
831    pub fn process_ui_amount_to_amount(
832        program_id: &Pubkey,
833        accounts: &[AccountInfo],
834        ui_amount: &str,
835    ) -> ProgramResult {
836        let account_info_iter = &mut accounts.iter();
837        let mint_info = next_account_info(account_info_iter)?;
838        Self::check_account_owner(program_id, mint_info)?;
839
840        let mint = Mint::unpack(&mint_info.data.borrow_mut())
841            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
842        let amount = try_ui_amount_into_amount(ui_amount.to_string(), mint.decimals)?;
843
844        set_return_data(&amount.to_le_bytes());
845        Ok(())
846    }
847
848    /// Processes an [`Instruction`](enum.Instruction.html).
849    pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
850        let instruction = TokenInstruction::unpack(input)?;
851
852        match instruction {
853            TokenInstruction::InitializeMint {
854                decimals,
855                mint_authority,
856                freeze_authority,
857            } => {
858                msg!("Instruction: InitializeMint");
859                Self::process_initialize_mint(accounts, decimals, mint_authority, freeze_authority)
860            }
861            TokenInstruction::InitializeMint2 {
862                decimals,
863                mint_authority,
864                freeze_authority,
865            } => {
866                msg!("Instruction: InitializeMint2");
867                Self::process_initialize_mint2(accounts, decimals, mint_authority, freeze_authority)
868            }
869            TokenInstruction::InitializeAccount => {
870                msg!("Instruction: InitializeAccount");
871                Self::process_initialize_account(program_id, accounts)
872            }
873            TokenInstruction::InitializeAccount2 { owner } => {
874                msg!("Instruction: InitializeAccount2");
875                Self::process_initialize_account2(program_id, accounts, owner)
876            }
877            TokenInstruction::InitializeAccount3 { owner } => {
878                msg!("Instruction: InitializeAccount3");
879                Self::process_initialize_account3(program_id, accounts, owner)
880            }
881            TokenInstruction::InitializeMultisig { m } => {
882                msg!("Instruction: InitializeMultisig");
883                Self::process_initialize_multisig(accounts, m)
884            }
885            TokenInstruction::InitializeMultisig2 { m } => {
886                msg!("Instruction: InitializeMultisig2");
887                Self::process_initialize_multisig2(accounts, m)
888            }
889            TokenInstruction::Transfer { amount } => {
890                msg!("Instruction: Transfer");
891                Self::process_transfer(program_id, accounts, amount, None)
892            }
893            TokenInstruction::Approve { amount } => {
894                msg!("Instruction: Approve");
895                Self::process_approve(program_id, accounts, amount, None)
896            }
897            TokenInstruction::Revoke => {
898                msg!("Instruction: Revoke");
899                Self::process_revoke(program_id, accounts)
900            }
901            TokenInstruction::SetAuthority {
902                authority_type,
903                new_authority,
904            } => {
905                msg!("Instruction: SetAuthority");
906                Self::process_set_authority(program_id, accounts, authority_type, new_authority)
907            }
908            TokenInstruction::MintTo { amount } => {
909                msg!("Instruction: MintTo");
910                Self::process_mint_to(program_id, accounts, amount, None)
911            }
912            TokenInstruction::Burn { amount } => {
913                msg!("Instruction: Burn");
914                Self::process_burn(program_id, accounts, amount, None)
915            }
916            TokenInstruction::CloseAccount => {
917                msg!("Instruction: CloseAccount");
918                Self::process_close_account(program_id, accounts)
919            }
920            TokenInstruction::FreezeAccount => {
921                msg!("Instruction: FreezeAccount");
922                Self::process_toggle_freeze_account(program_id, accounts, true)
923            }
924            TokenInstruction::ThawAccount => {
925                msg!("Instruction: ThawAccount");
926                Self::process_toggle_freeze_account(program_id, accounts, false)
927            }
928            TokenInstruction::TransferChecked { amount, decimals } => {
929                msg!("Instruction: TransferChecked");
930                Self::process_transfer(program_id, accounts, amount, Some(decimals))
931            }
932            TokenInstruction::ApproveChecked { amount, decimals } => {
933                msg!("Instruction: ApproveChecked");
934                Self::process_approve(program_id, accounts, amount, Some(decimals))
935            }
936            TokenInstruction::MintToChecked { amount, decimals } => {
937                msg!("Instruction: MintToChecked");
938                Self::process_mint_to(program_id, accounts, amount, Some(decimals))
939            }
940            TokenInstruction::BurnChecked { amount, decimals } => {
941                msg!("Instruction: BurnChecked");
942                Self::process_burn(program_id, accounts, amount, Some(decimals))
943            }
944            TokenInstruction::SyncNative => {
945                msg!("Instruction: SyncNative");
946                Self::process_sync_native(program_id, accounts)
947            }
948            TokenInstruction::GetAccountDataSize => {
949                msg!("Instruction: GetAccountDataSize");
950                Self::process_get_account_data_size(program_id, accounts)
951            }
952            TokenInstruction::InitializeImmutableOwner => {
953                msg!("Instruction: InitializeImmutableOwner");
954                Self::process_initialize_immutable_owner(accounts)
955            }
956            TokenInstruction::AmountToUiAmount { amount } => {
957                msg!("Instruction: AmountToUiAmount");
958                Self::process_amount_to_ui_amount(program_id, accounts, amount)
959            }
960            TokenInstruction::UiAmountToAmount { ui_amount } => {
961                msg!("Instruction: UiAmountToAmount");
962                Self::process_ui_amount_to_amount(program_id, accounts, ui_amount)
963            }
964        }
965    }
966
967    /// Checks that the account is owned by the expected program
968    pub fn check_account_owner(program_id: &Pubkey, account_info: &AccountInfo) -> ProgramResult {
969        if !Self::cmp_pubkeys(program_id, account_info.owner) {
970            Err(ProgramError::IncorrectProgramId)
971        } else {
972            Ok(())
973        }
974    }
975
976    /// Checks two pubkeys for equality in a computationally cheap way
977    pub fn cmp_pubkeys(a: &Pubkey, b: &Pubkey) -> bool {
978        a.as_ref() == b.as_ref()
979    }
980
981    /// Validates owner(s) are present
982    pub fn validate_owner(
983        program_id: &Pubkey,
984        expected_owner: &Pubkey,
985        owner_account_info: &AccountInfo,
986        signers: &[AccountInfo],
987    ) -> ProgramResult {
988        if !Self::cmp_pubkeys(expected_owner, owner_account_info.key) {
989            return Err(TokenError::OwnerMismatch.into());
990        }
991        if Self::cmp_pubkeys(program_id, owner_account_info.owner)
992            && owner_account_info.data_len() == Multisig::get_packed_len()
993        {
994            let multisig = Multisig::unpack(&owner_account_info.data.borrow())?;
995            let mut num_signers = 0;
996            let mut matched = [false; MAX_SIGNERS];
997            for signer in signers.iter() {
998                for (position, key) in multisig.signers[0..multisig.n as usize].iter().enumerate() {
999                    if Self::cmp_pubkeys(key, signer.key) && !matched[position] {
1000                        if !signer.is_signer {
1001                            return Err(ProgramError::MissingRequiredSignature);
1002                        }
1003                        matched[position] = true;
1004                        num_signers += 1;
1005                    }
1006                }
1007            }
1008            if num_signers < multisig.m {
1009                return Err(ProgramError::MissingRequiredSignature);
1010            }
1011            return Ok(());
1012        } else if !owner_account_info.is_signer {
1013            return Err(ProgramError::MissingRequiredSignature);
1014        }
1015        Ok(())
1016    }
1017}
1018
1019/// Helper function to mostly delete an account in a test environment.  We could
1020/// potentially muck around the bytes assuming that a vec is passed in, but that
1021/// would be more trouble than it's worth.
1022#[cfg(not(target_os = "atlas"))]
1023fn delete_account(account_info: &AccountInfo) -> Result<(), ProgramError> {
1024    account_info.assign(&system_program::id());
1025    let mut account_data = account_info.data.borrow_mut();
1026    account_data.fill(0);
1027    Ok(())
1028}
1029
1030/// Helper function to totally delete an account on-chain
1031#[cfg(target_os = "atlas")]
1032fn delete_account(account_info: &AccountInfo) -> Result<(), ProgramError> {
1033    account_info.assign(&system_program::id());
1034    account_info.realloc(0, false)
1035}
1036
1037#[cfg(test)]
1038mod tests {
1039    use {
1040        super::*,
1041        atlas_clock::Epoch,
1042        atlas_program_error::ToStr,
1043        std::sync::{Arc, RwLock},
1044    };
1045
1046    lazy_static::lazy_static! {
1047        static ref EXPECTED_DATA: Arc<RwLock<Vec<u8>>> = Arc::new(RwLock::new(Vec::new()));
1048    }
1049
1050    fn return_token_error_as_program_error() -> ProgramError {
1051        TokenError::MintMismatch.into()
1052    }
1053
1054    #[test]
1055    fn test_print_error() {
1056        let error = return_token_error_as_program_error();
1057        error.to_str::<TokenError>();
1058    }
1059
1060    #[test]
1061    fn test_error_as_custom() {
1062        assert_eq!(
1063            return_token_error_as_program_error(),
1064            ProgramError::Custom(3)
1065        );
1066    }
1067
1068    #[test]
1069    fn test_unique_account_sizes() {
1070        assert_ne!(Mint::get_packed_len(), 0);
1071        assert_ne!(Mint::get_packed_len(), Account::get_packed_len());
1072        assert_ne!(Mint::get_packed_len(), Multisig::get_packed_len());
1073        assert_ne!(Account::get_packed_len(), 0);
1074        assert_ne!(Account::get_packed_len(), Multisig::get_packed_len());
1075        assert_ne!(Multisig::get_packed_len(), 0);
1076    }
1077
1078    #[test]
1079    fn test_pack_unpack() {
1080        // Mint
1081        let check = Mint {
1082            mint_authority: COption::Some(Pubkey::new_from_array([1; 32])),
1083            supply: 42,
1084            decimals: 7,
1085            is_initialized: true,
1086            freeze_authority: COption::Some(Pubkey::new_from_array([2; 32])),
1087        };
1088        let mut packed = vec![0; Mint::get_packed_len() + 1];
1089        assert_eq!(
1090            Err(ProgramError::InvalidAccountData),
1091            Mint::pack(check, &mut packed)
1092        );
1093        let mut packed = vec![0; Mint::get_packed_len() - 1];
1094        assert_eq!(
1095            Err(ProgramError::InvalidAccountData),
1096            Mint::pack(check, &mut packed)
1097        );
1098        let mut packed = vec![0; Mint::get_packed_len()];
1099        Mint::pack(check, &mut packed).unwrap();
1100        let expect = vec![
1101            1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1102            1, 1, 1, 1, 1, 1, 1, 42, 0, 0, 0, 0, 0, 0, 0, 7, 1, 1, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2,
1103            2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1104        ];
1105        assert_eq!(packed, expect);
1106        let unpacked = Mint::unpack(&packed).unwrap();
1107        assert_eq!(unpacked, check);
1108
1109        // Account
1110        let check = Account {
1111            mint: Pubkey::new_from_array([1; 32]),
1112            owner: Pubkey::new_from_array([2; 32]),
1113            amount: 3,
1114            delegate: COption::Some(Pubkey::new_from_array([4; 32])),
1115            state: AccountState::Frozen,
1116            is_native: COption::Some(5),
1117            delegated_amount: 6,
1118            close_authority: COption::Some(Pubkey::new_from_array([7; 32])),
1119        };
1120        let mut packed = vec![0; Account::get_packed_len() + 1];
1121        assert_eq!(
1122            Err(ProgramError::InvalidAccountData),
1123            Account::pack(check, &mut packed)
1124        );
1125        let mut packed = vec![0; Account::get_packed_len() - 1];
1126        assert_eq!(
1127            Err(ProgramError::InvalidAccountData),
1128            Account::pack(check, &mut packed)
1129        );
1130        let mut packed = vec![0; Account::get_packed_len()];
1131        Account::pack(check, &mut packed).unwrap();
1132        let expect = vec![
1133            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1134            1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1135            2, 2, 2, 2, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
1136            4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 1, 0, 0, 0, 5, 0, 0,
1137            0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
1138            7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
1139        ];
1140        assert_eq!(packed, expect);
1141        let unpacked = Account::unpack(&packed).unwrap();
1142        assert_eq!(unpacked, check);
1143
1144        // Multisig
1145        let check = Multisig {
1146            m: 1,
1147            n: 2,
1148            is_initialized: true,
1149            signers: [Pubkey::new_from_array([3; 32]); MAX_SIGNERS],
1150        };
1151        let mut packed = vec![0; Multisig::get_packed_len() + 1];
1152        assert_eq!(
1153            Err(ProgramError::InvalidAccountData),
1154            Multisig::pack(check, &mut packed)
1155        );
1156        let mut packed = vec![0; Multisig::get_packed_len() - 1];
1157        assert_eq!(
1158            Err(ProgramError::InvalidAccountData),
1159            Multisig::pack(check, &mut packed)
1160        );
1161        let mut packed = vec![0; Multisig::get_packed_len()];
1162        Multisig::pack(check, &mut packed).unwrap();
1163        let expect = vec![
1164            1, 2, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1165            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1166            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1167            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1168            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1169            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1170            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1171            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1172            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1173            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1174            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1175            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1176            3, 3, 3, 3, 3, 3, 3,
1177        ];
1178        assert_eq!(packed, expect);
1179        let unpacked = Multisig::unpack(&packed).unwrap();
1180        assert_eq!(unpacked, check);
1181    }
1182
1183    #[test]
1184    fn test_validate_owner() {
1185        let program_id = crate::id();
1186        let owner_key = Pubkey::new_unique();
1187        let mut signer_keys = [Pubkey::default(); MAX_SIGNERS];
1188        for signer_key in signer_keys.iter_mut().take(MAX_SIGNERS) {
1189            *signer_key = Pubkey::new_unique();
1190        }
1191        let mut signer_lamports = 0;
1192        let mut signer_data = vec![];
1193        let mut signers = vec![
1194            AccountInfo::new(
1195                &owner_key,
1196                true,
1197                false,
1198                &mut signer_lamports,
1199                &mut signer_data,
1200                &program_id,
1201                false,
1202                Epoch::default(),
1203            );
1204            MAX_SIGNERS + 1
1205        ];
1206        for (signer, key) in signers.iter_mut().zip(&signer_keys) {
1207            signer.key = key;
1208        }
1209        let mut lamports = 0;
1210        let mut data = vec![0; Multisig::get_packed_len()];
1211        let mut multisig = Multisig::unpack_unchecked(&data).unwrap();
1212        multisig.m = MAX_SIGNERS as u8;
1213        multisig.n = MAX_SIGNERS as u8;
1214        multisig.signers = signer_keys;
1215        multisig.is_initialized = true;
1216        Multisig::pack(multisig, &mut data).unwrap();
1217        let owner_account_info = AccountInfo::new(
1218            &owner_key,
1219            false,
1220            false,
1221            &mut lamports,
1222            &mut data,
1223            &program_id,
1224            false,
1225            Epoch::default(),
1226        );
1227
1228        // full 11 of 11
1229        Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap();
1230
1231        // 1 of 11
1232        {
1233            let mut multisig =
1234                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
1235            multisig.m = 1;
1236            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
1237        }
1238        Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap();
1239
1240        // 2:1
1241        {
1242            let mut multisig =
1243                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
1244            multisig.m = 2;
1245            multisig.n = 1;
1246            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
1247        }
1248        assert_eq!(
1249            Err(ProgramError::MissingRequiredSignature),
1250            Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers)
1251        );
1252
1253        // 0:11
1254        {
1255            let mut multisig =
1256                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
1257            multisig.m = 0;
1258            multisig.n = 11;
1259            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
1260        }
1261        Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap();
1262
1263        // 2:11 but 0 provided
1264        {
1265            let mut multisig =
1266                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
1267            multisig.m = 2;
1268            multisig.n = 11;
1269            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
1270        }
1271        assert_eq!(
1272            Err(ProgramError::MissingRequiredSignature),
1273            Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &[])
1274        );
1275        // 2:11 but 1 provided
1276        {
1277            let mut multisig =
1278                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
1279            multisig.m = 2;
1280            multisig.n = 11;
1281            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
1282        }
1283        assert_eq!(
1284            Err(ProgramError::MissingRequiredSignature),
1285            Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers[0..1])
1286        );
1287
1288        // 2:11, 2 from middle provided
1289        {
1290            let mut multisig =
1291                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
1292            multisig.m = 2;
1293            multisig.n = 11;
1294            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
1295        }
1296        Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers[5..7])
1297            .unwrap();
1298
1299        // 11:11, one is not a signer
1300        {
1301            let mut multisig =
1302                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
1303            multisig.m = 11;
1304            multisig.n = 11;
1305            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
1306        }
1307        signers[5].is_signer = false;
1308        assert_eq!(
1309            Err(ProgramError::MissingRequiredSignature),
1310            Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers)
1311        );
1312        signers[5].is_signer = true;
1313
1314        // 11:11, single signer signs multiple times
1315        {
1316            let mut signer_lamports = 0;
1317            let mut signer_data = vec![];
1318            let signers = vec![
1319                AccountInfo::new(
1320                    &signer_keys[5],
1321                    true,
1322                    false,
1323                    &mut signer_lamports,
1324                    &mut signer_data,
1325                    &program_id,
1326                    false,
1327                    Epoch::default(),
1328                );
1329                MAX_SIGNERS + 1
1330            ];
1331            let mut multisig =
1332                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
1333            multisig.m = 11;
1334            multisig.n = 11;
1335            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
1336            assert_eq!(
1337                Err(ProgramError::MissingRequiredSignature),
1338                Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers)
1339            );
1340        }
1341    }
1342}