spl_token_metadata_example/
processor.rs1use {
4 solana_program::{
5 account_info::{next_account_info, AccountInfo},
6 borsh1::get_instance_packed_len,
7 entrypoint::ProgramResult,
8 msg,
9 program::set_return_data,
10 program_error::ProgramError,
11 program_option::COption,
12 pubkey::Pubkey,
13 },
14 spl_pod::optional_keys::OptionalNonZeroPubkey,
15 spl_token_2022::{extension::StateWithExtensions, state::Mint},
16 spl_token_metadata_interface::{
17 error::TokenMetadataError,
18 instruction::{
19 Emit, Initialize, RemoveKey, TokenMetadataInstruction, UpdateAuthority, UpdateField,
20 },
21 state::TokenMetadata,
22 },
23 spl_type_length_value::state::{
24 realloc_and_pack_first_variable_len, TlvState, TlvStateBorrowed, TlvStateMut,
25 },
26};
27
28fn check_update_authority(
29 update_authority_info: &AccountInfo,
30 expected_update_authority: &OptionalNonZeroPubkey,
31) -> Result<(), ProgramError> {
32 if !update_authority_info.is_signer {
33 return Err(ProgramError::MissingRequiredSignature);
34 }
35 let update_authority = Option::<Pubkey>::from(*expected_update_authority)
36 .ok_or(TokenMetadataError::ImmutableMetadata)?;
37 if update_authority != *update_authority_info.key {
38 return Err(TokenMetadataError::IncorrectUpdateAuthority.into());
39 }
40 Ok(())
41}
42
43pub fn process_initialize(
45 _program_id: &Pubkey,
46 accounts: &[AccountInfo],
47 data: Initialize,
48) -> ProgramResult {
49 let account_info_iter = &mut accounts.iter();
50
51 let metadata_info = next_account_info(account_info_iter)?;
52 let update_authority_info = next_account_info(account_info_iter)?;
53 let mint_info = next_account_info(account_info_iter)?;
54 let mint_authority_info = next_account_info(account_info_iter)?;
55
56 {
58 let mint_data = mint_info.try_borrow_data()?;
62 let mint = StateWithExtensions::<Mint>::unpack(&mint_data)?;
63
64 if !mint_authority_info.is_signer {
65 return Err(ProgramError::MissingRequiredSignature);
66 }
67 if mint.base.mint_authority.as_ref() != COption::Some(mint_authority_info.key) {
68 return Err(TokenMetadataError::IncorrectMintAuthority.into());
69 }
70 }
71
72 let update_authority = OptionalNonZeroPubkey::try_from(Some(*update_authority_info.key))?;
74 let token_metadata = TokenMetadata {
75 name: data.name,
76 symbol: data.symbol,
77 uri: data.uri,
78 update_authority,
79 mint: *mint_info.key,
80 ..Default::default()
81 };
82 let instance_size = get_instance_packed_len(&token_metadata)?;
83
84 let mut buffer = metadata_info.try_borrow_mut_data()?;
86 let mut state = TlvStateMut::unpack(&mut buffer)?;
87 state.alloc::<TokenMetadata>(instance_size, false)?;
88 state.pack_first_variable_len_value(&token_metadata)?;
89
90 Ok(())
91}
92
93pub fn process_update_field(
95 _program_id: &Pubkey,
96 accounts: &[AccountInfo],
97 data: UpdateField,
98) -> ProgramResult {
99 let account_info_iter = &mut accounts.iter();
100 let metadata_info = next_account_info(account_info_iter)?;
101 let update_authority_info = next_account_info(account_info_iter)?;
102
103 let mut token_metadata = {
106 let buffer = metadata_info.try_borrow_data()?;
107 let state = TlvStateBorrowed::unpack(&buffer)?;
108 state.get_first_variable_len_value::<TokenMetadata>()?
109 };
110
111 check_update_authority(update_authority_info, &token_metadata.update_authority)?;
112
113 token_metadata.update(data.field, data.value);
115
116 realloc_and_pack_first_variable_len(metadata_info, &token_metadata)?;
118
119 Ok(())
120}
121
122pub fn process_remove_key(
124 _program_id: &Pubkey,
125 accounts: &[AccountInfo],
126 data: RemoveKey,
127) -> ProgramResult {
128 let account_info_iter = &mut accounts.iter();
129 let metadata_info = next_account_info(account_info_iter)?;
130 let update_authority_info = next_account_info(account_info_iter)?;
131
132 let mut token_metadata = {
135 let buffer = metadata_info.try_borrow_data()?;
136 let state = TlvStateBorrowed::unpack(&buffer)?;
137 state.get_first_variable_len_value::<TokenMetadata>()?
138 };
139
140 check_update_authority(update_authority_info, &token_metadata.update_authority)?;
141 if !token_metadata.remove_key(&data.key) && !data.idempotent {
142 return Err(TokenMetadataError::KeyNotFound.into());
143 }
144 realloc_and_pack_first_variable_len(metadata_info, &token_metadata)?;
145
146 Ok(())
147}
148
149pub fn process_update_authority(
152 _program_id: &Pubkey,
153 accounts: &[AccountInfo],
154 data: UpdateAuthority,
155) -> ProgramResult {
156 let account_info_iter = &mut accounts.iter();
157 let metadata_info = next_account_info(account_info_iter)?;
158 let update_authority_info = next_account_info(account_info_iter)?;
159
160 let mut token_metadata = {
163 let buffer = metadata_info.try_borrow_data()?;
164 let state = TlvStateBorrowed::unpack(&buffer)?;
165 state.get_first_variable_len_value::<TokenMetadata>()?
166 };
167
168 check_update_authority(update_authority_info, &token_metadata.update_authority)?;
169 token_metadata.update_authority = data.new_authority;
170 realloc_and_pack_first_variable_len(metadata_info, &token_metadata)?;
172
173 Ok(())
174}
175
176pub fn process_emit(program_id: &Pubkey, accounts: &[AccountInfo], data: Emit) -> ProgramResult {
178 let account_info_iter = &mut accounts.iter();
179 let metadata_info = next_account_info(account_info_iter)?;
180
181 if metadata_info.owner != program_id {
182 return Err(ProgramError::IllegalOwner);
183 }
184
185 let buffer = metadata_info.try_borrow_data()?;
186 let state = TlvStateBorrowed::unpack(&buffer)?;
187 let metadata_bytes = state.get_first_bytes::<TokenMetadata>()?;
188
189 if let Some(range) = TokenMetadata::get_slice(metadata_bytes, data.start, data.end) {
190 set_return_data(range);
191 }
192
193 Ok(())
194}
195
196pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
198 let instruction = TokenMetadataInstruction::unpack(input)?;
199
200 match instruction {
201 TokenMetadataInstruction::Initialize(data) => {
202 msg!("Instruction: Initialize");
203 process_initialize(program_id, accounts, data)
204 }
205 TokenMetadataInstruction::UpdateField(data) => {
206 msg!("Instruction: UpdateField");
207 process_update_field(program_id, accounts, data)
208 }
209 TokenMetadataInstruction::RemoveKey(data) => {
210 msg!("Instruction: RemoveKey");
211 process_remove_key(program_id, accounts, data)
212 }
213 TokenMetadataInstruction::UpdateAuthority(data) => {
214 msg!("Instruction: UpdateAuthority");
215 process_update_authority(program_id, accounts, data)
216 }
217 TokenMetadataInstruction::Emit(data) => {
218 msg!("Instruction: Emit");
219 process_emit(program_id, accounts, data)
220 }
221 }
222}