@@ -16,7 +16,7 @@ use crate::{
1616 AuthorityRequest , AuthorityResponse , AuthorityType , Collection , Metadata ,
1717 ProgrammableConfig , TokenMetadataAccount , TokenStandard ,
1818 } ,
19- utils:: { assert_derivation, check_token_standard} ,
19+ utils:: { assert_derivation, check_token_standard, mint_decimals_is_zero } ,
2020} ;
2121
2222#[ derive( Clone , Debug , PartialEq , Eq ) ]
@@ -45,6 +45,7 @@ pub fn update<'a>(
4545
4646 match args {
4747 UpdateArgs :: V1 { .. } => update_v1 ( program_id, context, args) ,
48+ UpdateArgs :: V2 { .. } => update_v1 ( program_id, context, args) ,
4849 }
4950}
5051
@@ -63,7 +64,7 @@ fn update_v1(program_id: &Pubkey, ctx: Context<Update>, args: UpdateArgs) -> Pro
6364
6465 if let Some ( edition) = ctx. accounts . edition_info {
6566 assert_owned_by ( edition, program_id) ?;
66- // checks that we got the correct master account
67+ // checks that we got the correct edition account
6768 assert_derivation (
6869 program_id,
6970 edition,
@@ -114,12 +115,6 @@ fn update_v1(program_id: &Pubkey, ctx: Context<Update>, args: UpdateArgs) -> Pro
114115 return Err ( MetadataError :: MintMismatch . into ( ) ) ;
115116 }
116117
117- let token_standard = if let Some ( token_standard) = metadata. token_standard {
118- token_standard
119- } else {
120- check_token_standard ( ctx. accounts . mint_info , ctx. accounts . edition_info ) ?
121- } ;
122-
123118 let ( token_pubkey, token) = if let Some ( token_info) = ctx. accounts . token_info {
124119 (
125120 Some ( token_info. key ) ,
@@ -152,7 +147,18 @@ fn update_v1(program_id: &Pubkey, ctx: Context<Update>, args: UpdateArgs) -> Pro
152147 token : token_pubkey,
153148 token_account : token. as_ref ( ) ,
154149 metadata_delegate_record_info : ctx. accounts . delegate_record_info ,
155- metadata_delegate_roles : vec ! [ MetadataDelegateRole :: ProgrammableConfig ] ,
150+ metadata_delegate_roles : vec ! [
151+ MetadataDelegateRole :: Authority ,
152+ MetadataDelegateRole :: Data ,
153+ MetadataDelegateRole :: Collection ,
154+ MetadataDelegateRole :: CollectionItem ,
155+ MetadataDelegateRole :: ProgrammableConfig ,
156+ MetadataDelegateRole :: ProgrammableConfigItem ,
157+ ] ,
158+ collection_metadata_delegate_roles : vec ! [
159+ MetadataDelegateRole :: Collection ,
160+ MetadataDelegateRole :: ProgrammableConfig ,
161+ ] ,
156162 precedence : & [
157163 AuthorityType :: Metadata ,
158164 AuthorityType :: MetadataDelegate ,
@@ -161,6 +167,34 @@ fn update_v1(program_id: &Pubkey, ctx: Context<Update>, args: UpdateArgs) -> Pro
161167 ..Default :: default ( )
162168 } ) ?;
163169
170+ // Find existing token standard from metadata or infer it.
171+ let existing_or_inferred_token_standard = if let Some ( token_standard) = metadata. token_standard
172+ {
173+ token_standard
174+ } else {
175+ check_token_standard ( ctx. accounts . mint_info , ctx. accounts . edition_info ) ?
176+ } ;
177+
178+ // Check if caller passed in a desired token standard.
179+ let desired_token_standard = match args {
180+ UpdateArgs :: V1 { .. } => None ,
181+ UpdateArgs :: V2 { token_standard, .. } => token_standard,
182+ } ;
183+
184+ // If there is a desired token standard, use it if it passes the check. If there is not a
185+ // desired token standard, use the existing or inferred token standard.
186+ let token_standard = match desired_token_standard {
187+ Some ( desired_token_standard) => {
188+ check_desired_token_standard (
189+ mint_decimals_is_zero ( ctx. accounts . mint_info ) ?,
190+ existing_or_inferred_token_standard,
191+ desired_token_standard,
192+ ) ?;
193+ desired_token_standard
194+ }
195+ None => existing_or_inferred_token_standard,
196+ } ;
197+
164198 // For pNFTs, we need to validate the authorization rules.
165199 if matches ! ( token_standard, TokenStandard :: ProgrammableNonFungible ) {
166200 // If the metadata account has a current rule set, we validate that
@@ -174,6 +208,8 @@ fn update_v1(program_id: &Pubkey, ctx: Context<Update>, args: UpdateArgs) -> Pro
174208 }
175209 }
176210
211+ // Validate that authority has permission to update the fields that have been specified in the
212+ // update args.
177213 validate_update ( & args, & authority_type, metadata_delegate_role) ?;
178214
179215 // If we reach here without errors we have validated that the authority is allowed to
@@ -199,58 +235,171 @@ fn validate_update(
199235 metadata_delegate_role : Option < MetadataDelegateRole > ,
200236) -> ProgramResult {
201237 // validate the authority type
202-
203238 match authority_type {
204239 AuthorityType :: Metadata => {
205- // metadata authority is the paramount (upadte ) authority
240+ // metadata authority is the paramount (update ) authority
206241 msg ! ( "Auth type: Metadata" ) ;
207242 }
208- AuthorityType :: MetadataDelegate => {
209- // support for delegate update
210- msg ! ( "Auth type: Delegate" ) ;
211- }
212243 AuthorityType :: Holder => {
213244 // support for holder update
214245 msg ! ( "Auth type: Holder" ) ;
215246 return Err ( MetadataError :: FeatureNotSupported . into ( ) ) ;
216247 }
248+ AuthorityType :: MetadataDelegate => {
249+ // support for delegate update
250+ msg ! ( "Auth type: Delegate" ) ;
251+ }
217252 _ => {
218253 return Err ( MetadataError :: InvalidAuthorityType . into ( ) ) ;
219254 }
220255 }
221256
222- let UpdateArgs :: V1 {
257+ // Destructure args.
258+ let (
259+ new_update_authority,
223260 data,
224261 primary_sale_happened,
225262 is_mutable,
226263 collection,
227- uses,
228- new_update_authority,
229264 collection_details,
230- ..
231- } = args;
265+ uses,
266+ rule_set,
267+ token_standard,
268+ ) = match args {
269+ UpdateArgs :: V1 {
270+ new_update_authority,
271+ data,
272+ primary_sale_happened,
273+ is_mutable,
274+ collection,
275+ collection_details,
276+ uses,
277+ rule_set,
278+ ..
279+ } => (
280+ new_update_authority,
281+ data,
282+ primary_sale_happened,
283+ is_mutable,
284+ collection,
285+ collection_details,
286+ uses,
287+ rule_set,
288+ & None ,
289+ ) ,
290+ UpdateArgs :: V2 {
291+ new_update_authority,
292+ data,
293+ primary_sale_happened,
294+ is_mutable,
295+ collection,
296+ collection_details,
297+ uses,
298+ rule_set,
299+ token_standard,
300+ ..
301+ } => (
302+ new_update_authority,
303+ data,
304+ primary_sale_happened,
305+ is_mutable,
306+ collection,
307+ collection_details,
308+ uses,
309+ rule_set,
310+ token_standard,
311+ ) ,
312+ } ;
232313
233314 // validate the delegate role: this consist in checking that
234315 // the delegate is only updating fields that it has access to
235- match metadata_delegate_role {
236- Some ( MetadataDelegateRole :: ProgrammableConfig ) => {
237- // can only update the programmable config
238- if data. is_some ( )
239- || primary_sale_happened. is_some ( )
240- || is_mutable. is_some ( )
241- || collection. is_some ( )
242- || uses. is_some ( )
243- || new_update_authority. is_some ( )
244- || collection_details. is_some ( )
245- {
246- return Err ( MetadataError :: InvalidUpdateArgs . into ( ) ) ;
316+ if let Some ( metadata_delegate_role) = metadata_delegate_role {
317+ match metadata_delegate_role {
318+ MetadataDelegateRole :: Authority => {
319+ // Fields allowed for `Authority`:
320+ // `new_update_authority`
321+ // `primary_sale_happened`
322+ // `is_mutable`
323+ // `token_standard`
324+ if data. is_some ( )
325+ || collection. is_some ( )
326+ || collection_details. is_some ( )
327+ || uses. is_some ( )
328+ || rule_set. is_some ( )
329+ {
330+ return Err ( MetadataError :: InvalidUpdateArgs . into ( ) ) ;
331+ }
247332 }
333+ MetadataDelegateRole :: Data => {
334+ // Fields allowed for `Data`:
335+ // `data`
336+ if new_update_authority. is_some ( )
337+ || primary_sale_happened. is_some ( )
338+ || is_mutable. is_some ( )
339+ || collection. is_some ( )
340+ || collection_details. is_some ( )
341+ || uses. is_some ( )
342+ || rule_set. is_some ( )
343+ || token_standard. is_some ( )
344+ {
345+ return Err ( MetadataError :: InvalidUpdateArgs . into ( ) ) ;
346+ }
347+ }
348+
349+ MetadataDelegateRole :: Collection | MetadataDelegateRole :: CollectionItem => {
350+ // Fields allowed for `Collection` and `CollectionItem`:
351+ // `collection`
352+ if new_update_authority. is_some ( )
353+ || data. is_some ( )
354+ || primary_sale_happened. is_some ( )
355+ || is_mutable. is_some ( )
356+ || collection_details. is_some ( )
357+ || uses. is_some ( )
358+ || rule_set. is_some ( )
359+ || token_standard. is_some ( )
360+ {
361+ return Err ( MetadataError :: InvalidUpdateArgs . into ( ) ) ;
362+ }
363+ }
364+ MetadataDelegateRole :: ProgrammableConfig
365+ | MetadataDelegateRole :: ProgrammableConfigItem => {
366+ // Fields allowed for `ProgrammableConfig` and `ProgrammableConfigItem`:
367+ // `rule_set`
368+ if new_update_authority. is_some ( )
369+ || data. is_some ( )
370+ || primary_sale_happened. is_some ( )
371+ || is_mutable. is_some ( )
372+ || collection. is_some ( )
373+ || collection_details. is_some ( )
374+ || uses. is_some ( )
375+ || token_standard. is_some ( )
376+ {
377+ return Err ( MetadataError :: InvalidUpdateArgs . into ( ) ) ;
378+ }
379+ }
380+ _ => return Err ( MetadataError :: InvalidAuthorityType . into ( ) ) ,
248381 }
249- Some ( _) => {
250- return Err ( MetadataError :: InvalidAuthorityType . into ( ) ) ;
251- }
252- None => { /* no delegate role to check */ }
253382 }
254383
255384 Ok ( ( ) )
256385}
386+
387+ fn check_desired_token_standard (
388+ mint_decimals_is_zero : bool ,
389+ existing_or_inferred_token_standard : TokenStandard ,
390+ desired_token_standard : TokenStandard ,
391+ ) -> ProgramResult {
392+ // This code only allows switching between Fungible and FungibleAsset, and only when
393+ // mint decimals is zero.
394+ if !mint_decimals_is_zero {
395+ return Err ( MetadataError :: InvalidTokenStandard . into ( ) ) ;
396+ }
397+
398+ match existing_or_inferred_token_standard {
399+ TokenStandard :: Fungible | TokenStandard :: FungibleAsset => match desired_token_standard {
400+ TokenStandard :: Fungible | TokenStandard :: FungibleAsset => Ok ( ( ) ) ,
401+ _ => Err ( MetadataError :: InvalidTokenStandard . into ( ) ) ,
402+ } ,
403+ _ => Err ( MetadataError :: InvalidTokenStandard . into ( ) ) ,
404+ }
405+ }
0 commit comments