Skip to main content

mpl_token_metadata/
traits.rs

1//! Additional trait implementations for generated types.
2
3use std::io::{Error, ErrorKind};
4
5use borsh::BorshDeserialize;
6use solana_program::pubkey::Pubkey;
7
8use crate::{
9    accounts::{
10        CollectionAuthorityRecord, MasterEdition, Metadata, MetadataDelegateRecord, TokenRecord,
11    },
12    errors::MplTokenMetadataError,
13    generated::{
14        types::{CollectionToggle, RuleSetToggle, UsesToggle},
15        {instructions::UpdateV1InstructionArgs, types::CollectionDetailsToggle},
16    },
17    types::{
18        Collection, CollectionDetails, Data, Key, ProgrammableConfig, TokenDelegateRole,
19        TokenStandard, TokenState, UpdateArgs, Uses,
20    },
21};
22
23/// safe deserialize default impl
24
25macro_rules! safe_deserialize {
26    ( ($n:tt, $k:tt), $(($name:tt, $key:tt)),+ ) => {
27        safe_deserialize!(($n, $k));
28        safe_deserialize!($( ($name, $key) ),+);
29    };
30    ( ($name:tt, $key:tt) ) => {
31        impl $name {
32            pub fn safe_deserialize(data: &[u8]) -> Result<Self, borsh::maybestd::io::Error> {
33                if data.is_empty() || data[0] != Key::$key as u8 {
34                    return Err(borsh::maybestd::io::Error::new(
35                        ErrorKind::Other,
36                        "DataTypeMismatch",
37                    ));
38                }
39                // mutable "pointer" to the account data
40                let mut data = data;
41                let result = Self::deserialize(&mut data)?;
42
43                Ok(result)
44            }
45        }
46    };
47}
48
49safe_deserialize!(
50    (CollectionAuthorityRecord, CollectionAuthorityRecord),
51    (MasterEdition, MasterEditionV2),
52    (MetadataDelegateRecord, MetadataDelegate)
53);
54
55// UpdateV1InstructionArgs
56
57impl Default for UpdateV1InstructionArgs {
58    fn default() -> Self {
59        Self {
60            new_update_authority: None,
61            data: None,
62            primary_sale_happened: None,
63            is_mutable: None,
64            collection: CollectionToggle::None,
65            collection_details: CollectionDetailsToggle::None,
66            uses: UsesToggle::None,
67            rule_set: RuleSetToggle::None,
68            authorization_data: None,
69        }
70    }
71}
72
73// Token Standard
74
75impl Copy for TokenStandard {}
76
77// Metadata
78
79impl Metadata {
80    pub fn safe_deserialize(data: &[u8]) -> Result<Self, borsh::maybestd::io::Error> {
81        if data.is_empty() || data[0] != Key::MetadataV1 as u8 {
82            return Err(borsh::maybestd::io::Error::new(
83                ErrorKind::Other,
84                "DataTypeMismatch",
85            ));
86        }
87        // mutable "pointer" to the account data
88        let mut data = data;
89        let result = Self::deserialize_unchecked(&mut data)?;
90
91        Ok(result)
92    }
93
94    fn deserialize_unchecked(buf: &mut &[u8]) -> Result<Self, borsh::maybestd::io::Error> {
95        // Metadata corruption shouldn't appear until after edition_nonce.
96        let key: Key = BorshDeserialize::deserialize(buf)?;
97        let update_authority: Pubkey = BorshDeserialize::deserialize(buf)?;
98        let mint: Pubkey = BorshDeserialize::deserialize(buf)?;
99        let data: Data = BorshDeserialize::deserialize(buf)?;
100        let primary_sale_happened: bool = BorshDeserialize::deserialize(buf)?;
101        let is_mutable: bool = BorshDeserialize::deserialize(buf)?;
102        let edition_nonce: Option<u8> = BorshDeserialize::deserialize(buf)?;
103
104        // V1.2
105        let token_standard_res: Result<Option<TokenStandard>, borsh::maybestd::io::Error> =
106            BorshDeserialize::deserialize(buf);
107        let collection_res: Result<Option<Collection>, borsh::maybestd::io::Error> =
108            BorshDeserialize::deserialize(buf);
109        let uses_res: Result<Option<Uses>, borsh::maybestd::io::Error> =
110            BorshDeserialize::deserialize(buf);
111
112        // V1.3
113        let collection_details_res: Result<Option<CollectionDetails>, borsh::maybestd::io::Error> =
114            BorshDeserialize::deserialize(buf);
115
116        // pNFT - Programmable Config
117        let programmable_config_res: Result<
118            Option<ProgrammableConfig>,
119            borsh::maybestd::io::Error,
120        > = BorshDeserialize::deserialize(buf);
121
122        // We can have accidentally valid, but corrupted data, particularly on the Collection struct,
123        // so to increase probability of catching errors. If any of these deserializations fail, set
124        // all values to None.
125        let (token_standard, collection, uses) =
126            match (token_standard_res, collection_res, uses_res) {
127                (Ok(token_standard_res), Ok(collection_res), Ok(uses_res)) => {
128                    (token_standard_res, collection_res, uses_res)
129                }
130                _ => (None, None, None),
131            };
132
133        // V1.3
134        let collection_details = match collection_details_res {
135            Ok(details) => details,
136            Err(_) => None,
137        };
138
139        // Programmable Config
140        let programmable_config = programmable_config_res.unwrap_or(None);
141
142        let metadata = Metadata {
143            key,
144            update_authority,
145            mint,
146            name: data.name,
147            seller_fee_basis_points: data.seller_fee_basis_points,
148            symbol: data.symbol,
149            uri: data.uri,
150            creators: data.creators,
151            primary_sale_happened,
152            is_mutable,
153            edition_nonce,
154            token_standard,
155            collection,
156            uses,
157            collection_details,
158            programmable_config,
159        };
160
161        Ok(metadata)
162    }
163}
164
165// Token Record
166
167const LOCKED_TRANSFER_SIZE: usize = 33;
168
169impl TokenRecord {
170    pub fn safe_deserialize(data: &[u8]) -> Result<TokenRecord, Error> {
171        // we perform a manual deserialization since we are potentially dealing
172        // with accounts of different sizes
173        let length = TokenRecord::LEN as i64 - data.len() as i64;
174
175        // we use the account length in the 'is_correct_account_type' since we are
176        // manually checking that the account length is valid
177        if !(length == 0 || length == LOCKED_TRANSFER_SIZE as i64)
178            || data[0] != Key::TokenRecord as u8
179        {
180            return Err(Error::new(
181                ErrorKind::InvalidData,
182                MplTokenMetadataError::DataTypeMismatch,
183            ));
184        }
185        // mutable "pointer" to the account data
186        let mut data = data;
187
188        let key: Key = BorshDeserialize::deserialize(&mut data)?;
189        let bump: u8 = BorshDeserialize::deserialize(&mut data)?;
190        let state: TokenState = BorshDeserialize::deserialize(&mut data)?;
191        let rule_set_revision: Option<u64> = BorshDeserialize::deserialize(&mut data)?;
192        let delegate: Option<Pubkey> = BorshDeserialize::deserialize(&mut data)?;
193        let delegate_role: Option<TokenDelegateRole> = BorshDeserialize::deserialize(&mut data)?;
194
195        let locked_transfer: Option<Pubkey> = if length == 0 {
196            BorshDeserialize::deserialize(&mut data)?
197        } else {
198            None
199        };
200
201        Ok(TokenRecord {
202            key,
203            bump,
204            state,
205            rule_set_revision,
206            delegate,
207            delegate_role,
208            locked_transfer,
209        })
210    }
211}
212
213// UpdateArgs
214
215impl Default for UpdateArgs {
216    fn default() -> Self {
217        Self::V1 {
218            new_update_authority: None,
219            data: None,
220            primary_sale_happened: None,
221            is_mutable: None,
222            collection: CollectionToggle::None,
223            collection_details: CollectionDetailsToggle::None,
224            uses: UsesToggle::None,
225            rule_set: RuleSetToggle::None,
226            authorization_data: None,
227        }
228    }
229}
230
231impl UpdateArgs {
232    pub fn default_as_update_authority() -> Self {
233        Self::AsUpdateAuthorityV2 {
234            new_update_authority: None,
235            data: None,
236            primary_sale_happened: None,
237            is_mutable: None,
238            collection: CollectionToggle::None,
239            collection_details: CollectionDetailsToggle::None,
240            uses: UsesToggle::None,
241            rule_set: RuleSetToggle::None,
242            token_standard: None,
243            authorization_data: None,
244        }
245    }
246
247    pub fn default_as_authority_item_delegate() -> Self {
248        Self::AsAuthorityItemDelegateV2 {
249            new_update_authority: None,
250            primary_sale_happened: None,
251            is_mutable: None,
252            token_standard: None,
253            authorization_data: None,
254        }
255    }
256
257    pub fn default_as_collection_delegate() -> Self {
258        Self::AsCollectionDelegateV2 {
259            collection: CollectionToggle::None,
260            authorization_data: None,
261        }
262    }
263
264    pub fn default_as_data_delegate() -> Self {
265        Self::AsDataDelegateV2 {
266            data: None,
267            authorization_data: None,
268        }
269    }
270
271    pub fn default_as_programmable_config_delegate() -> Self {
272        Self::AsProgrammableConfigDelegateV2 {
273            rule_set: RuleSetToggle::None,
274            authorization_data: None,
275        }
276    }
277
278    pub fn default_as_data_item_delegate() -> Self {
279        Self::AsDataItemDelegateV2 {
280            data: None,
281            authorization_data: None,
282        }
283    }
284
285    pub fn default_as_collection_item_delegate() -> Self {
286        Self::AsCollectionItemDelegateV2 {
287            collection: CollectionToggle::None,
288            authorization_data: None,
289        }
290    }
291
292    pub fn default_as_programmable_config_item_delegate() -> Self {
293        Self::AsProgrammableConfigItemDelegateV2 {
294            rule_set: RuleSetToggle::None,
295            authorization_data: None,
296        }
297    }
298}
299
300// Key
301
302impl Copy for Key {}