Skip to main content

mpl_token_metadata/state/
mod.rs

1pub(crate) mod asset_data;
2pub(crate) mod collection;
3pub(crate) mod creator;
4pub(crate) mod data;
5pub(crate) mod delegate;
6pub(crate) mod edition;
7pub(crate) mod edition_marker;
8pub(crate) mod escrow;
9pub(crate) mod master_edition;
10pub(crate) mod metadata;
11pub(crate) mod migrate;
12pub(crate) mod programmable;
13pub(crate) mod reservation;
14pub(crate) mod token_auth_payload;
15pub(crate) mod uses;
16
17use std::io::ErrorKind;
18
19pub use asset_data::*;
20use borsh::{maybestd::io::Error as BorshError, BorshDeserialize, BorshSerialize};
21pub use collection::*;
22pub use creator::*;
23pub use data::*;
24pub use delegate::*;
25pub use edition::*;
26pub use edition_marker::*;
27pub use escrow::*;
28pub use master_edition::*;
29pub use metadata::*;
30pub use migrate::*;
31use mpl_utils::resize_or_reallocate_account_raw;
32use num_derive::FromPrimitive;
33use num_traits::FromPrimitive;
34pub use programmable::*;
35pub use reservation::*;
36use shank::ShankAccount;
37use solana_program::{
38    account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError,
39    pubkey::Pubkey,
40};
41use spl_token::state::Account as TokenAccount;
42pub use uses::*;
43#[cfg(feature = "serde-feature")]
44use {
45    serde::{Deserialize, Deserializer, Serialize},
46    serde_with::{As, DisplayFromStr},
47    std::str::FromStr,
48};
49
50// Re-export constants to maintain compatibility.
51pub use crate::pda::{BURN, COLLECTION_AUTHORITY, EDITION, PREFIX, USER};
52use crate::{
53    assertions::assert_owned_by,
54    error::MetadataError,
55    utils::{meta_deser_unchecked, try_from_slice_checked},
56    ID,
57};
58
59/// Index of the discriminator on the account data.
60pub const DISCRIMINATOR_INDEX: usize = 0;
61
62#[repr(C)]
63#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
64#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone, Copy, FromPrimitive)]
65pub enum TokenStandard {
66    NonFungible,             // This is a master edition
67    FungibleAsset,           // A token with metadata that can also have attributes
68    Fungible,                // A token with simple metadata
69    NonFungibleEdition,      // This is a limited edition
70    ProgrammableNonFungible, // NonFungible with programmable configuration
71}
72
73pub trait TokenMetadataAccount: BorshDeserialize {
74    fn key() -> Key;
75
76    fn size() -> usize;
77
78    fn is_correct_account_type(data: &[u8], data_type: Key, data_size: usize) -> bool {
79        if data.is_empty() {
80            return false;
81        }
82
83        let key: Option<Key> = Key::from_u8(data[0]);
84        match key {
85            Some(key) => {
86                (key == data_type || key == Key::Uninitialized) && (data.len() == data_size)
87            }
88            None => false,
89        }
90    }
91
92    fn pad_length(buf: &mut Vec<u8>) -> Result<(), MetadataError> {
93        let padding_length = Self::size()
94            .checked_sub(buf.len())
95            .ok_or(MetadataError::NumericalOverflowError)?;
96        buf.extend(vec![0; padding_length]);
97        Ok(())
98    }
99
100    fn safe_deserialize(mut data: &[u8]) -> Result<Self, BorshError> {
101        if !Self::is_correct_account_type(data, Self::key(), Self::size()) {
102            return Err(BorshError::new(ErrorKind::Other, "DataTypeMismatch"));
103        }
104
105        let result = Self::deserialize(&mut data)?;
106
107        Ok(result)
108    }
109
110    fn from_account_info(a: &AccountInfo) -> Result<Self, ProgramError>
111where {
112        let data = &a.data.borrow_mut();
113
114        let ua = Self::safe_deserialize(data).map_err(|_| MetadataError::DataTypeMismatch)?;
115
116        // Check that this is a `token-metadata` owned account.
117        assert_owned_by(a, &ID)?;
118
119        Ok(ua)
120    }
121}
122
123#[repr(C)]
124#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
125#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone, Copy, FromPrimitive)]
126pub enum Key {
127    Uninitialized,
128    EditionV1,
129    MasterEditionV1,
130    ReservationListV1,
131    MetadataV1,
132    ReservationListV2,
133    MasterEditionV2,
134    EditionMarker,
135    UseAuthorityRecord,
136    CollectionAuthorityRecord,
137    TokenOwnedEscrow,
138    TokenRecord,
139    MetadataDelegate,
140}
141
142#[cfg(feature = "serde-feature")]
143fn deser_option_pubkey<'de, D>(deserializer: D) -> Result<Option<Pubkey>, D::Error>
144where
145    D: Deserializer<'de>,
146{
147    <Option<String> as serde::de::Deserialize>::deserialize(deserializer)?
148        .map(|s| Pubkey::from_str(&s))
149        .transpose()
150        .map_err(serde::de::Error::custom)
151}
152
153#[cfg(feature = "serde-feature")]
154fn ser_option_pubkey<S>(pubkey: &Option<Pubkey>, serializer: S) -> Result<S::Ok, S::Error>
155where
156    S: serde::Serializer,
157{
158    let pubkey_string = pubkey.as_ref().map(|p| p.to_string());
159    serde::ser::Serialize::serialize(&pubkey_string, serializer)
160}
161
162/// Trait for resizable accounts.
163///
164/// Implementing this trait for a type will automatically allow the use of the `save` method,
165/// which can modify the size of an account.
166///
167/// A type implementing this trait must specify the `from_bytes` method, since an account can
168/// have variable size.
169pub trait Resizable: TokenMetadataAccount + BorshSerialize {
170    /// Saves the information to the specified account, resizing the account if needed.
171    ///
172    /// The account size can either increase or decrease depending on whether the account size
173    /// matches the struct size or not.
174    fn save<'a>(
175        &self,
176        account_info: &'a AccountInfo<'a>,
177        payer_info: &'a AccountInfo<'a>,
178        system_program_info: &'a AccountInfo<'a>,
179    ) -> Result<(), ProgramError> {
180        // the required account size
181        let required_size = Self::size();
182
183        if account_info.data_len() != required_size {
184            resize_or_reallocate_account_raw(
185                account_info,
186                payer_info,
187                system_program_info,
188                required_size,
189            )?;
190        }
191
192        let mut account_data = account_info.data.borrow_mut();
193        // passes a slice to borsh so the internal account data array does not get
194        // temporarily resized
195        let mut storage = &mut account_data[..required_size];
196        BorshSerialize::serialize(self, &mut storage)?;
197
198        Ok(())
199    }
200
201    /// Deserializes the struct data from the specified byte array.
202    ///
203    /// In most cases this will perform a custom deserialization since the size of the
204    /// stored byte array (account) can change.
205    fn from_bytes(data: &[u8]) -> Result<Self, ProgramError>;
206}