mpl_token_metadata/assertions/
misc.rs1use 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
38pub 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}