Skip to main content

mpl_token_metadata/assertions/
misc.rs

1use mpl_utils::cmp_pubkeys;
2use solana_program::{
3    account_info::AccountInfo,
4    entrypoint::ProgramResult,
5    program_error::ProgramError,
6    program_option::COption,
7    program_pack::{IsInitialized, Pack},
8    pubkey::Pubkey,
9    rent::Rent,
10};
11use spl_token::state::Account;
12
13use crate::{
14    error::MetadataError,
15    state::{TokenDelegateRole, TokenRecord},
16};
17
18pub fn assert_keys_equal(key1: &Pubkey, key2: &Pubkey) -> Result<(), ProgramError> {
19    if !cmp_pubkeys(key1, key2) {
20        Err(MetadataError::KeyMismatch.into())
21    } else {
22        Ok(())
23    }
24}
25
26pub fn assert_keys_equal_with_error(
27    key1: &Pubkey,
28    key2: &Pubkey,
29    err: MetadataError,
30) -> Result<(), ProgramError> {
31    if !cmp_pubkeys(key1, key2) {
32        Err(err.into())
33    } else {
34        Ok(())
35    }
36}
37
38/// assert initialized account
39pub fn assert_initialized<T: Pack + IsInitialized>(
40    account_info: &AccountInfo,
41) -> Result<T, ProgramError> {
42    mpl_utils::assert_initialized(account_info, MetadataError::Uninitialized)
43}
44
45pub fn assert_mint_authority_matches_mint(
46    mint_authority: &COption<Pubkey>,
47    mint_authority_info: &AccountInfo,
48) -> ProgramResult {
49    match mint_authority {
50        COption::None => {
51            return Err(MetadataError::InvalidMintAuthority.into());
52        }
53        COption::Some(key) => {
54            if mint_authority_info.key != key {
55                return Err(MetadataError::InvalidMintAuthority.into());
56            }
57        }
58    }
59
60    if !mint_authority_info.is_signer {
61        return Err(MetadataError::NotMintAuthority.into());
62    }
63
64    Ok(())
65}
66
67pub fn assert_freeze_authority_matches_mint(
68    freeze_authority: &COption<Pubkey>,
69    freeze_authority_info: &AccountInfo,
70) -> ProgramResult {
71    match freeze_authority {
72        COption::None => {
73            return Err(MetadataError::InvalidFreezeAuthority.into());
74        }
75        COption::Some(key) => {
76            if freeze_authority_info.key != key {
77                return Err(MetadataError::InvalidFreezeAuthority.into());
78            }
79        }
80    }
81    Ok(())
82}
83
84pub fn assert_delegated_tokens(
85    delegate: &AccountInfo,
86    mint_info: &AccountInfo,
87    token_account_info: &AccountInfo,
88) -> ProgramResult {
89    assert_owned_by(mint_info, &spl_token::ID)?;
90
91    let token_account: Account = assert_initialized(token_account_info)?;
92
93    assert_owned_by(token_account_info, &spl_token::ID)?;
94
95    if token_account.mint != *mint_info.key {
96        return Err(MetadataError::MintMismatch.into());
97    }
98
99    if token_account.amount < 1 {
100        return Err(MetadataError::InsufficientTokenBalance.into());
101    }
102
103    if token_account.delegate == COption::None
104        || token_account.delegated_amount != token_account.amount
105        || token_account.delegate.unwrap() != *delegate.key
106    {
107        return Err(MetadataError::InvalidDelegate.into());
108    }
109    Ok(())
110}
111
112pub fn assert_derivation(
113    program_id: &Pubkey,
114    account: &AccountInfo,
115    path: &[&[u8]],
116) -> Result<u8, ProgramError> {
117    mpl_utils::assert_derivation(program_id, account, path, MetadataError::DerivedKeyInvalid)
118}
119
120pub fn assert_owned_by(account: &AccountInfo, owner: &Pubkey) -> ProgramResult {
121    mpl_utils::assert_owned_by(account, owner, MetadataError::IncorrectOwner)
122}
123
124pub fn assert_token_program_matches_package(token_program_info: &AccountInfo) -> ProgramResult {
125    mpl_utils::token::assert_token_program_matches_package(
126        token_program_info,
127        MetadataError::InvalidTokenProgram,
128    )
129}
130
131pub fn assert_rent_exempt(rent: &Rent, account_info: &AccountInfo) -> ProgramResult {
132    mpl_utils::assert_rent_exempt(rent, account_info, MetadataError::NotRentExempt)
133}
134
135pub fn assert_delegate(
136    delegate: &Pubkey,
137    role: TokenDelegateRole,
138    token_record: &TokenRecord,
139) -> ProgramResult {
140    if let TokenRecord {
141        delegate: Some(token_delegate),
142        delegate_role: Some(delegate_role),
143        ..
144    } = token_record
145    {
146        if cmp_pubkeys(delegate, token_delegate) && role == *delegate_role {
147            return Ok(());
148        }
149    }
150
151    Err(MetadataError::InvalidDelegate.into())
152}
153
154pub fn assert_token_matches_owner_and_mint(
155    token_info: &AccountInfo,
156    owner: &Pubkey,
157    mint: &Pubkey,
158) -> ProgramResult {
159    let token_account: Account = assert_initialized(token_info)?;
160
161    if token_account.owner != *owner {
162        return Err(MetadataError::InvalidOwner.into());
163    }
164
165    if token_account.mint != *mint {
166        return Err(MetadataError::MintMismatch.into());
167    }
168
169    Ok(())
170}