gor22_token/extension/token_metadata/
processor.rs1use {
4 crate::{
5 check_program_account,
6 error::TokenError,
7 extension::{
8 alloc_and_serialize_variable_len_extension, metadata_pointer::MetadataPointer,
9 BaseStateWithExtensions, PodStateWithExtensions,
10 },
11 pod::{PodCOption, PodMint},
12 },
13 solana_program::{
14 account_info::{next_account_info, AccountInfo},
15 entrypoint::ProgramResult,
16 msg,
17 program::set_return_data,
18 program_error::ProgramError,
19 pubkey::Pubkey,
20 },
21 spl_pod::optional_keys::OptionalNonZeroPubkey,
22 spl_token_metadata_interface::{
23 error::TokenMetadataError,
24 instruction::{
25 Emit, Initialize, RemoveKey, TokenMetadataInstruction, UpdateAuthority, UpdateField,
26 },
27 state::TokenMetadata,
28 },
29};
30
31fn check_update_authority(
32 update_authority_info: &AccountInfo,
33 expected_update_authority: &OptionalNonZeroPubkey,
34) -> Result<(), ProgramError> {
35 if !update_authority_info.is_signer {
36 return Err(ProgramError::MissingRequiredSignature);
37 }
38 let update_authority = Option::<Pubkey>::from(*expected_update_authority)
39 .ok_or(TokenMetadataError::ImmutableMetadata)?;
40 if update_authority != *update_authority_info.key {
41 return Err(TokenMetadataError::IncorrectUpdateAuthority.into());
42 }
43 Ok(())
44}
45
46pub fn process_initialize(
48 _program_id: &Pubkey,
49 accounts: &[AccountInfo],
50 data: Initialize,
51) -> ProgramResult {
52 let account_info_iter = &mut accounts.iter();
53
54 let metadata_info = next_account_info(account_info_iter)?;
55 let update_authority_info = next_account_info(account_info_iter)?;
56 let mint_info = next_account_info(account_info_iter)?;
57 let mint_authority_info = next_account_info(account_info_iter)?;
58
59 if metadata_info.key != mint_info.key {
62 msg!("Metadata for a mint must be initialized in the mint itself.");
63 return Err(TokenError::MintMismatch.into());
64 }
65
66 {
68 check_program_account(mint_info.owner)?;
71 let mint_data = mint_info.try_borrow_data()?;
72 let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data)?;
73
74 if !mint_authority_info.is_signer {
75 return Err(ProgramError::MissingRequiredSignature);
76 }
77 if mint.base.mint_authority != PodCOption::some(*mint_authority_info.key) {
78 return Err(TokenMetadataError::IncorrectMintAuthority.into());
79 }
80
81 if mint.get_extension::<MetadataPointer>().is_err() {
82 msg!("A mint with metadata must have the metadata-pointer extension initialized");
83 return Err(TokenError::InvalidExtensionCombination.into());
84 }
85 }
86
87 let update_authority = OptionalNonZeroPubkey::try_from(Some(*update_authority_info.key))?;
89 let token_metadata = TokenMetadata {
90 name: data.name,
91 symbol: data.symbol,
92 uri: data.uri,
93 update_authority,
94 mint: *mint_info.key,
95 ..Default::default()
96 };
97
98 alloc_and_serialize_variable_len_extension::<PodMint, _>(
101 metadata_info,
102 &token_metadata,
103 false,
104 )?;
105
106 Ok(())
107}
108
109pub fn process_update_field(
112 _program_id: &Pubkey,
113 accounts: &[AccountInfo],
114 data: UpdateField,
115) -> ProgramResult {
116 let account_info_iter = &mut accounts.iter();
117 let metadata_info = next_account_info(account_info_iter)?;
118 let update_authority_info = next_account_info(account_info_iter)?;
119
120 let mut token_metadata = {
123 let buffer = metadata_info.try_borrow_data()?;
124 let mint = PodStateWithExtensions::<PodMint>::unpack(&buffer)?;
125 mint.get_variable_len_extension::<TokenMetadata>()?
126 };
127
128 check_update_authority(update_authority_info, &token_metadata.update_authority)?;
129
130 token_metadata.update(data.field, data.value);
132
133 alloc_and_serialize_variable_len_extension::<PodMint, _>(metadata_info, &token_metadata, true)?;
135
136 Ok(())
137}
138
139pub fn process_remove_key(
141 _program_id: &Pubkey,
142 accounts: &[AccountInfo],
143 data: RemoveKey,
144) -> ProgramResult {
145 let account_info_iter = &mut accounts.iter();
146 let metadata_info = next_account_info(account_info_iter)?;
147 let update_authority_info = next_account_info(account_info_iter)?;
148
149 let mut token_metadata = {
152 let buffer = metadata_info.try_borrow_data()?;
153 let mint = PodStateWithExtensions::<PodMint>::unpack(&buffer)?;
154 mint.get_variable_len_extension::<TokenMetadata>()?
155 };
156
157 check_update_authority(update_authority_info, &token_metadata.update_authority)?;
158 if !token_metadata.remove_key(&data.key) && !data.idempotent {
159 return Err(TokenMetadataError::KeyNotFound.into());
160 }
161 alloc_and_serialize_variable_len_extension::<PodMint, _>(metadata_info, &token_metadata, true)?;
162 Ok(())
163}
164
165pub fn process_update_authority(
168 _program_id: &Pubkey,
169 accounts: &[AccountInfo],
170 data: UpdateAuthority,
171) -> ProgramResult {
172 let account_info_iter = &mut accounts.iter();
173 let metadata_info = next_account_info(account_info_iter)?;
174 let update_authority_info = next_account_info(account_info_iter)?;
175
176 let mut token_metadata = {
179 let buffer = metadata_info.try_borrow_data()?;
180 let mint = PodStateWithExtensions::<PodMint>::unpack(&buffer)?;
181 mint.get_variable_len_extension::<TokenMetadata>()?
182 };
183
184 check_update_authority(update_authority_info, &token_metadata.update_authority)?;
185 token_metadata.update_authority = data.new_authority;
186 alloc_and_serialize_variable_len_extension::<PodMint, _>(metadata_info, &token_metadata, true)?;
188
189 Ok(())
190}
191
192pub fn process_emit(program_id: &Pubkey, accounts: &[AccountInfo], data: Emit) -> ProgramResult {
194 let account_info_iter = &mut accounts.iter();
195 let metadata_info = next_account_info(account_info_iter)?;
196
197 if metadata_info.owner != program_id {
198 return Err(ProgramError::IllegalOwner);
199 }
200
201 let buffer = metadata_info.try_borrow_data()?;
202 let state = PodStateWithExtensions::<PodMint>::unpack(&buffer)?;
203 let metadata_bytes = state.get_extension_bytes::<TokenMetadata>()?;
204
205 if let Some(range) = TokenMetadata::get_slice(metadata_bytes, data.start, data.end) {
206 set_return_data(range);
207 }
208 Ok(())
209}
210
211pub fn process_instruction(
213 program_id: &Pubkey,
214 accounts: &[AccountInfo],
215 instruction: TokenMetadataInstruction,
216) -> ProgramResult {
217 match instruction {
218 TokenMetadataInstruction::Initialize(data) => {
219 msg!("TokenMetadataInstruction: Initialize");
220 process_initialize(program_id, accounts, data)
221 }
222 TokenMetadataInstruction::UpdateField(data) => {
223 msg!("TokenMetadataInstruction: UpdateField");
224 process_update_field(program_id, accounts, data)
225 }
226 TokenMetadataInstruction::RemoveKey(data) => {
227 msg!("TokenMetadataInstruction: RemoveKey");
228 process_remove_key(program_id, accounts, data)
229 }
230 TokenMetadataInstruction::UpdateAuthority(data) => {
231 msg!("TokenMetadataInstruction: UpdateAuthority");
232 process_update_authority(program_id, accounts, data)
233 }
234 TokenMetadataInstruction::Emit(data) => {
235 msg!("TokenMetadataInstruction: Emit");
236 process_emit(program_id, accounts, data)
237 }
238 }
239}