mpl_token_metadata/state/
metadata.rs

1use super::*;
2use crate::{
3    assertions::{
4        collection::assert_collection_update_is_valid, metadata::assert_data_valid,
5        uses::assert_valid_use,
6    },
7    instruction::{CollectionDetailsToggle, CollectionToggle, RuleSetToggle, UpdateArgs},
8    utils::{clean_write_metadata, puff_out_data_fields},
9};
10
11pub const MAX_NAME_LENGTH: usize = 32;
12
13pub const MAX_SYMBOL_LENGTH: usize = 10;
14
15pub const MAX_URI_LENGTH: usize = 200;
16
17pub const MAX_METADATA_LEN: usize = 1 // key 
18+ 32             // update auth pubkey
19+ 32             // mint pubkey
20+ MAX_DATA_SIZE
21+ 1              // primary sale
22+ 1              // mutable
23+ 9              // nonce (pretty sure this only needs to be 2)
24+ 2              // token standard
25+ 34             // collection
26+ 18             // uses
27+ 10             // collection details
28+ 33             // programmable config
29+ 75; // Padding
30
31pub const MAX_DATA_SIZE: usize = 4
32    + MAX_NAME_LENGTH
33    + 4
34    + MAX_SYMBOL_LENGTH
35    + 4
36    + MAX_URI_LENGTH
37    + 2
38    + 1
39    + 4
40    + MAX_CREATOR_LIMIT * MAX_CREATOR_LEN;
41
42#[macro_export]
43macro_rules! metadata_seeds {
44    ($mint:expr) => {{
45        let path = vec!["metadata".as_bytes(), $crate::ID.as_ref(), $mint.as_ref()];
46        let (_, bump) = Pubkey::find_program_address(&path, &$crate::ID);
47        &[
48            "metadata".as_bytes(),
49            $crate::ID.as_ref(),
50            $mint.as_ref(),
51            &[bump],
52        ]
53    }};
54}
55
56#[repr(C)]
57#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
58#[derive(Clone, BorshSerialize, Debug, PartialEq, Eq, ShankAccount)]
59pub struct Metadata {
60    /// Account discriminator.
61    pub key: Key,
62    /// Address of the update authority.
63    #[cfg_attr(feature = "serde-feature", serde(with = "As::<DisplayFromStr>"))]
64    pub update_authority: Pubkey,
65    /// Address of the mint.
66    #[cfg_attr(feature = "serde-feature", serde(with = "As::<DisplayFromStr>"))]
67    pub mint: Pubkey,
68    /// Asset data.
69    pub data: Data,
70    // Immutable, once flipped, all sales of this metadata are considered secondary.
71    pub primary_sale_happened: bool,
72    // Whether or not the data struct is mutable, default is not
73    pub is_mutable: bool,
74    /// nonce for easy calculation of editions, if present
75    pub edition_nonce: Option<u8>,
76    /// Since we cannot easily change Metadata, we add the new DataV2 fields here at the end.
77    pub token_standard: Option<TokenStandard>,
78    /// Collection
79    pub collection: Option<Collection>,
80    /// Uses
81    pub uses: Option<Uses>,
82    /// Collection Details
83    pub collection_details: Option<CollectionDetails>,
84    /// Programmable Config
85    pub programmable_config: Option<ProgrammableConfig>,
86}
87
88impl Metadata {
89    pub fn save(&self, data: &mut [u8]) -> Result<(), BorshError> {
90        let mut bytes = Vec::with_capacity(MAX_METADATA_LEN);
91        BorshSerialize::serialize(&self, &mut bytes)?;
92        data[..bytes.len()].copy_from_slice(&bytes);
93        Ok(())
94    }
95
96    pub(crate) fn update_v1<'a>(
97        &mut self,
98        args: UpdateArgs,
99        update_authority: &AccountInfo<'a>,
100        metadata: &AccountInfo<'a>,
101        token: Option<TokenAccount>,
102        token_standard: TokenStandard,
103    ) -> ProgramResult {
104        // Update the token standard if it is changed.
105        self.token_standard = Some(token_standard);
106
107        // Only the Update Authority can update this section.
108        match &args {
109            UpdateArgs::V1 {
110                uses,
111                collection_details,
112                ..
113            }
114            | UpdateArgs::AsUpdateAuthorityV2 {
115                uses,
116                collection_details,
117                ..
118            } => {
119                if uses.is_some() {
120                    let uses_option = uses.clone().to_option();
121                    // If already None leave it as None.
122                    assert_valid_use(&uses_option, &self.uses)?;
123                    self.uses = uses_option;
124                }
125
126                if let CollectionDetailsToggle::Set(collection_details) = collection_details {
127                    // only unsized collections can have the size set, and only once.
128                    if self.collection_details.is_some() {
129                        return Err(MetadataError::SizedCollection.into());
130                    }
131
132                    self.collection_details = Some(collection_details.clone());
133                }
134            }
135            _ => (),
136        }
137
138        // Update Authority or Data Delegates can update this section.  Note this section is before
139        // the section that updates `is_mutable` so that both `data` and `is_mutable` can be updated
140        // in the same instruction.
141        match &args {
142            UpdateArgs::V1 { data, .. }
143            | UpdateArgs::AsUpdateAuthorityV2 { data, .. }
144            | UpdateArgs::AsDataDelegateV2 { data, .. }
145            | UpdateArgs::AsDataItemDelegateV2 { data, .. } => {
146                if let Some(data) = data {
147                    if !self.is_mutable {
148                        return Err(MetadataError::DataIsImmutable.into());
149                    }
150
151                    assert_data_valid(
152                        data,
153                        update_authority.key,
154                        self,
155                        false,
156                        update_authority.is_signer,
157                    )?;
158                    self.data = data.clone();
159                }
160            }
161            _ => (),
162        }
163
164        // Update Authority or Authority Item Delegate can update this section.
165        match &args {
166            UpdateArgs::V1 {
167                new_update_authority,
168                primary_sale_happened,
169                is_mutable,
170                ..
171            }
172            | UpdateArgs::AsUpdateAuthorityV2 {
173                new_update_authority,
174                primary_sale_happened,
175                is_mutable,
176                ..
177            }
178            | UpdateArgs::AsAuthorityItemDelegateV2 {
179                new_update_authority,
180                primary_sale_happened,
181                is_mutable,
182                ..
183            } => {
184                if let Some(authority) = new_update_authority {
185                    self.update_authority = *authority;
186                }
187
188                if let Some(primary_sale) = primary_sale_happened {
189                    // If received primary_sale is true, flip to true.
190                    if *primary_sale || !self.primary_sale_happened {
191                        self.primary_sale_happened = *primary_sale
192                    } else {
193                        return Err(MetadataError::PrimarySaleCanOnlyBeFlippedToTrue.into());
194                    }
195                }
196
197                if let Some(mutable) = is_mutable {
198                    // If received value is false, flip to false.
199                    if !mutable || self.is_mutable {
200                        self.is_mutable = *mutable
201                    } else {
202                        return Err(MetadataError::IsMutableCanOnlyBeFlippedToFalse.into());
203                    }
204                }
205            }
206            _ => (),
207        }
208
209        // Update Authority or Collection Delegates can update this section.
210        match &args {
211            UpdateArgs::V1 { collection, .. }
212            | UpdateArgs::AsUpdateAuthorityV2 { collection, .. }
213            | UpdateArgs::AsCollectionDelegateV2 { collection, .. }
214            | UpdateArgs::AsCollectionItemDelegateV2 { collection, .. } => match collection {
215                // if the Collection data is 'Set', only allow updating if it is unverified
216                // or if it exactly matches the existing collection info; if the Collection data
217                // is 'Clear', then only set to 'None' if it is unverified.
218                CollectionToggle::Set(_) => {
219                    let collection_option = collection.clone().to_option();
220                    assert_collection_update_is_valid(false, &self.collection, &collection_option)?;
221                    self.collection = collection_option;
222                }
223                CollectionToggle::Clear => {
224                    if let Some(current_collection) = self.collection.as_ref() {
225                        // Can't change a verified collection in this command.
226                        if current_collection.verified {
227                            return Err(MetadataError::CannotUpdateVerifiedCollection.into());
228                        }
229                        // If it's unverified, it's ok to set to None.
230                        self.collection = None;
231                    }
232                }
233                CollectionToggle::None => { /* nothing to do */ }
234            },
235            _ => (),
236        };
237
238        // Update Authority or Programmable Config Delegates can update this section.
239        match &args {
240            UpdateArgs::V1 { rule_set, .. }
241            | UpdateArgs::AsUpdateAuthorityV2 { rule_set, .. }
242            | UpdateArgs::AsProgrammableConfigDelegateV2 { rule_set, .. }
243            | UpdateArgs::AsProgrammableConfigItemDelegateV2 { rule_set, .. } => {
244                // if the rule_set data is either 'Set' or 'Clear', only allow updating if the
245                // token standard is equal to `ProgrammableNonFungible` and no SPL delegate is set.
246                if matches!(rule_set, RuleSetToggle::Clear | RuleSetToggle::Set(_)) {
247                    if token_standard != TokenStandard::ProgrammableNonFungible {
248                        return Err(MetadataError::InvalidTokenStandard.into());
249                    }
250
251                    // Require the token so we can check if it has a token delegate.
252                    let token = token.ok_or(MetadataError::MissingTokenAccount)?;
253
254                    // If the token has a delegate, we cannot update the rule set.
255                    if token.delegate.is_some() {
256                        return Err(MetadataError::CannotUpdateAssetWithDelegate.into());
257                    }
258
259                    self.programmable_config =
260                        rule_set
261                            .clone()
262                            .to_option()
263                            .map(|rule_set| ProgrammableConfig::V1 {
264                                rule_set: Some(rule_set),
265                            });
266                }
267            }
268            _ => (),
269        };
270
271        // Re-serialize metadata.
272        puff_out_data_fields(self);
273        clean_write_metadata(self, metadata)
274    }
275
276    pub fn into_asset_data(self) -> AssetData {
277        let mut asset_data = AssetData::new(
278            self.token_standard.unwrap_or(TokenStandard::NonFungible),
279            self.data.name,
280            self.data.symbol,
281            self.data.uri,
282        );
283        asset_data.seller_fee_basis_points = self.data.seller_fee_basis_points;
284        asset_data.creators = self.data.creators;
285        asset_data.primary_sale_happened = self.primary_sale_happened;
286        asset_data.is_mutable = self.is_mutable;
287        asset_data.collection = self.collection;
288        asset_data.uses = self.uses;
289        asset_data.collection_details = self.collection_details;
290        asset_data.rule_set =
291            if let Some(ProgrammableConfig::V1 { rule_set }) = self.programmable_config {
292                rule_set
293            } else {
294                None
295            };
296
297        asset_data
298    }
299}
300
301impl Default for Metadata {
302    fn default() -> Self {
303        Metadata {
304            key: Key::MetadataV1,
305            update_authority: Pubkey::default(),
306            mint: Pubkey::default(),
307            data: Data::default(),
308            primary_sale_happened: false,
309            is_mutable: false,
310            edition_nonce: None,
311            token_standard: None,
312            collection: None,
313            uses: None,
314            collection_details: None,
315            programmable_config: None,
316        }
317    }
318}
319
320impl TokenMetadataAccount for Metadata {
321    fn key() -> Key {
322        Key::MetadataV1
323    }
324
325    fn size() -> usize {
326        MAX_METADATA_LEN
327    }
328}
329
330// We have a custom implementation of BorshDeserialize for Metadata because of corrupted metadata issues
331// caused by resizing of the Creators array. We use a custom `meta_deser_unchecked` function
332// that has fallback values for corrupted fields.
333impl borsh::de::BorshDeserialize for Metadata {
334    fn deserialize(buf: &mut &[u8]) -> ::core::result::Result<Self, BorshError> {
335        let md = meta_deser_unchecked(buf)?;
336        Ok(md)
337    }
338}
339
340#[repr(C)]
341#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
342#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
343/// Represents the print supply of a non-fungible asset.
344pub enum PrintSupply {
345    /// The asset does not have any prints.
346    Zero,
347    /// The asset has a limited amount of prints.
348    Limited(u64),
349    /// The asset has an unlimited amount of prints.
350    Unlimited,
351}
352
353impl PrintSupply {
354    /// Converts the print supply to an option.
355    pub fn to_option(&self) -> Option<u64> {
356        match self {
357            PrintSupply::Zero => Some(0),
358            PrintSupply::Limited(supply) => Some(*supply),
359            PrintSupply::Unlimited => None,
360        }
361    }
362}
363
364/// Configuration for programmable assets.
365#[repr(C)]
366#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
367#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
368pub enum ProgrammableConfig {
369    V1 {
370        /// Programmable authorization rules.
371        #[cfg_attr(
372            feature = "serde-feature",
373            serde(
374                deserialize_with = "deser_option_pubkey",
375                serialize_with = "ser_option_pubkey"
376            )
377        )]
378        rule_set: Option<Pubkey>,
379    },
380}
381
382#[cfg(test)]
383mod tests {
384    use borsh::{BorshDeserialize, BorshSerialize};
385    use solana_program::account_info::AccountInfo;
386    use solana_sdk::{signature::Keypair, signer::Signer};
387
388    use crate::{
389        error::MetadataError,
390        state::{
391            CollectionAuthorityRecord, Edition, EditionMarker, Key, MasterEditionV2, Metadata,
392            TokenMetadataAccount, UseAuthorityRecord, MAX_METADATA_LEN,
393        },
394        utils::metadata::tests::{expected_pesky_metadata, pesky_data},
395        ID,
396    };
397
398    fn pad_metadata_length(metadata: &mut Vec<u8>) {
399        let padding_length = MAX_METADATA_LEN - metadata.len();
400        metadata.extend(vec![0; padding_length]);
401    }
402
403    #[test]
404    fn successfully_deserialize_corrupted_metadata() {
405        // This should be able to deserialize the corrupted metadata account successfully due to the custom BorshDeserilization
406        // implementation for the Metadata struct.
407        let expected_metadata = expected_pesky_metadata();
408        let mut corrupted_data = pesky_data();
409
410        let metadata = Metadata::deserialize(&mut corrupted_data).unwrap();
411
412        assert_eq!(metadata, expected_metadata);
413    }
414
415    #[test]
416    fn successfully_deserialize_metadata() {
417        let expected_metadata = expected_pesky_metadata();
418
419        let mut buf = Vec::new();
420        expected_metadata.serialize(&mut buf).unwrap();
421        pad_metadata_length(&mut buf);
422
423        let pubkey = Keypair::new().pubkey();
424        let owner = &ID;
425        let mut lamports = 1_000_000_000;
426        let mut data = buf.clone();
427
428        let md_account_info = AccountInfo::new(
429            &pubkey,
430            false,
431            true,
432            &mut lamports,
433            &mut data,
434            owner,
435            false,
436            1_000_000_000,
437        );
438
439        let md = Metadata::from_account_info(&md_account_info).unwrap();
440        assert_eq!(md.key, Key::MetadataV1);
441        assert_eq!(md, expected_metadata);
442    }
443
444    #[test]
445    fn fail_to_deserialize_metadata_with_wrong_owner() {
446        let expected_metadata = expected_pesky_metadata();
447
448        let mut buf = Vec::new();
449        expected_metadata.serialize(&mut buf).unwrap();
450        pad_metadata_length(&mut buf);
451
452        let pubkey = Keypair::new().pubkey();
453        let invalid_owner = Keypair::new().pubkey();
454        let mut lamports = 1_000_000_000;
455        let mut data = buf.clone();
456
457        let md_account_info = AccountInfo::new(
458            &pubkey,
459            false,
460            true,
461            &mut lamports,
462            &mut data,
463            &invalid_owner,
464            false,
465            1_000_000_000,
466        );
467
468        // `from_account_info` should not succeed because this account is not owned
469        // by `token-metadata` program.
470        let error = Metadata::from_account_info(&md_account_info).unwrap_err();
471        assert_eq!(error, MetadataError::IncorrectOwner.into());
472    }
473
474    #[test]
475    fn fail_to_deserialize_metadata_with_wrong_size() {
476        let expected_metadata = expected_pesky_metadata();
477
478        let mut buf = Vec::new();
479        expected_metadata.serialize(&mut buf).unwrap();
480        // No padding is added to the metadata so it's too short.
481
482        let pubkey = Keypair::new().pubkey();
483        let owner = ID;
484        let mut lamports = 1_000_000_000;
485        let mut data = buf.clone();
486
487        let account_info = AccountInfo::new(
488            &pubkey,
489            false,
490            true,
491            &mut lamports,
492            &mut data,
493            &owner,
494            false,
495            1_000_000_000,
496        );
497
498        // `from_account_info` should not succeed because this account is not owned
499        // by `token-metadata` program.
500        let error = Metadata::from_account_info(&account_info).unwrap_err();
501        assert_eq!(error, MetadataError::DataTypeMismatch.into());
502    }
503
504    #[test]
505    fn fail_to_deserialize_master_edition_into_metadata() {
506        let master_edition = MasterEditionV2 {
507            key: Key::MasterEditionV2,
508            supply: 0,
509            max_supply: Some(0),
510        };
511        let mut buf = Vec::new();
512        master_edition.serialize(&mut buf).unwrap();
513
514        let pubkey = Keypair::new().pubkey();
515        let owner = &ID;
516        let mut lamports = 1_000_000_000;
517        let mut data = buf.clone();
518
519        let account_info = AccountInfo::new(
520            &pubkey,
521            false,
522            true,
523            &mut lamports,
524            &mut data,
525            owner,
526            false,
527            1_000_000_000,
528        );
529
530        let err = Metadata::from_account_info(&account_info).unwrap_err();
531        assert_eq!(err, MetadataError::DataTypeMismatch.into());
532    }
533
534    #[test]
535    fn fail_to_deserialize_edition_into_metadata() {
536        let parent = Keypair::new().pubkey();
537        let edition = 1;
538
539        let edition = Edition {
540            key: Key::EditionV1,
541            parent,
542            edition,
543        };
544
545        let mut buf = Vec::new();
546        edition.serialize(&mut buf).unwrap();
547
548        let pubkey = Keypair::new().pubkey();
549        let owner = &ID;
550        let mut lamports = 1_000_000_000;
551        let mut data = buf.clone();
552
553        let account_info = AccountInfo::new(
554            &pubkey,
555            false,
556            true,
557            &mut lamports,
558            &mut data,
559            owner,
560            false,
561            1_000_000_000,
562        );
563
564        let err = Metadata::from_account_info(&account_info).unwrap_err();
565        assert_eq!(err, MetadataError::DataTypeMismatch.into());
566    }
567
568    #[test]
569    fn fail_to_deserialize_use_authority_record_into_metadata() {
570        let use_record = UseAuthorityRecord {
571            key: Key::UseAuthorityRecord,
572            allowed_uses: 14,
573            bump: 255,
574        };
575
576        let mut buf = Vec::new();
577        use_record.serialize(&mut buf).unwrap();
578
579        let pubkey = Keypair::new().pubkey();
580        let owner = &ID;
581        let mut lamports = 1_000_000_000;
582        let mut data = buf.clone();
583
584        let account_info = AccountInfo::new(
585            &pubkey,
586            false,
587            true,
588            &mut lamports,
589            &mut data,
590            owner,
591            false,
592            1_000_000_000,
593        );
594
595        let err = Metadata::from_account_info(&account_info).unwrap_err();
596        assert_eq!(err, MetadataError::DataTypeMismatch.into());
597    }
598
599    #[test]
600    fn fail_to_deserialize_collection_authority_record_into_metadata() {
601        let collection_record = CollectionAuthorityRecord {
602            key: Key::CollectionAuthorityRecord,
603            bump: 255,
604            update_authority: None,
605        };
606
607        let mut buf = Vec::new();
608        collection_record.serialize(&mut buf).unwrap();
609
610        let pubkey = Keypair::new().pubkey();
611        let owner = &ID;
612        let mut lamports = 1_000_000_000;
613        let mut data = buf.clone();
614
615        let account_info = AccountInfo::new(
616            &pubkey,
617            false,
618            true,
619            &mut lamports,
620            &mut data,
621            owner,
622            false,
623            1_000_000_000,
624        );
625
626        let err = Metadata::from_account_info(&account_info).unwrap_err();
627        assert_eq!(err, MetadataError::DataTypeMismatch.into());
628    }
629
630    #[test]
631    fn fail_to_deserialize_edition_marker_into_metadata() {
632        let edition_marker = EditionMarker {
633            key: Key::EditionMarker,
634            ledger: [0; 31],
635        };
636
637        let mut buf = Vec::new();
638        edition_marker.serialize(&mut buf).unwrap();
639
640        let pubkey = Keypair::new().pubkey();
641        let owner = &ID;
642        let mut lamports = 1_000_000_000;
643        let mut data = buf.clone();
644
645        let account_info = AccountInfo::new(
646            &pubkey,
647            false,
648            true,
649            &mut lamports,
650            &mut data,
651            owner,
652            false,
653            1_000_000_000,
654        );
655
656        let err = Metadata::from_account_info(&account_info).unwrap_err();
657        assert_eq!(err, MetadataError::DataTypeMismatch.into());
658    }
659}