referrerpolicy=no-referrer-when-downgrade

pallet_nfts/
impl_nonfungibles.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Implementations for `nonfungibles` traits.
19
20use super::*;
21use frame_support::{
22	ensure,
23	storage::KeyPrefixIterator,
24	traits::{tokens::nonfungibles_v2::*, Get},
25	BoundedSlice,
26};
27use sp_runtime::{DispatchError, DispatchResult};
28
29impl<T: Config<I>, I: 'static> Inspect<<T as SystemConfig>::AccountId> for Pallet<T, I> {
30	type ItemId = T::ItemId;
31	type CollectionId = T::CollectionId;
32
33	fn owner(
34		collection: &Self::CollectionId,
35		item: &Self::ItemId,
36	) -> Option<<T as SystemConfig>::AccountId> {
37		Item::<T, I>::get(collection, item).map(|a| a.owner)
38	}
39
40	fn collection_owner(collection: &Self::CollectionId) -> Option<<T as SystemConfig>::AccountId> {
41		Collection::<T, I>::get(collection).map(|a| a.owner)
42	}
43
44	/// Returns the attribute value of `item` of `collection` corresponding to `key`.
45	///
46	/// When `key` is empty, we return the item metadata value.
47	///
48	/// By default this is `None`; no attributes are defined.
49	fn attribute(
50		collection: &Self::CollectionId,
51		item: &Self::ItemId,
52		key: &[u8],
53	) -> Option<Vec<u8>> {
54		if key.is_empty() {
55			// We make the empty key map to the item metadata value.
56			ItemMetadataOf::<T, I>::get(collection, item).map(|m| m.data.into())
57		} else {
58			let namespace = AttributeNamespace::CollectionOwner;
59			let key = BoundedSlice::<_, _>::try_from(key).ok()?;
60			Attribute::<T, I>::get((collection, Some(item), namespace, key)).map(|a| a.0.into())
61		}
62	}
63
64	/// Returns the custom attribute value of `item` of `collection` corresponding to `key`.
65	///
66	/// By default this is `None`; no attributes are defined.
67	fn custom_attribute(
68		account: &T::AccountId,
69		collection: &Self::CollectionId,
70		item: &Self::ItemId,
71		key: &[u8],
72	) -> Option<Vec<u8>> {
73		let namespace = Account::<T, I>::get((account, collection, item))
74			.map(|_| AttributeNamespace::ItemOwner)
75			.unwrap_or_else(|| AttributeNamespace::Account(account.clone()));
76
77		let key = BoundedSlice::<_, _>::try_from(key).ok()?;
78		Attribute::<T, I>::get((collection, Some(item), namespace, key)).map(|a| a.0.into())
79	}
80
81	/// Returns the system attribute value of `item` of `collection` corresponding to `key` if
82	/// `item` is `Some`. Otherwise, returns the system attribute value of `collection`
83	/// corresponding to `key`.
84	///
85	/// By default this is `None`; no attributes are defined.
86	fn system_attribute(
87		collection: &Self::CollectionId,
88		item: Option<&Self::ItemId>,
89		key: &[u8],
90	) -> Option<Vec<u8>> {
91		let namespace = AttributeNamespace::Pallet;
92		let key = BoundedSlice::<_, _>::try_from(key).ok()?;
93		Attribute::<T, I>::get((collection, item, namespace, key)).map(|a| a.0.into())
94	}
95
96	/// Returns the attribute value of `item` of `collection` corresponding to `key`.
97	///
98	/// When `key` is empty, we return the item metadata value.
99	///
100	/// By default this is `None`; no attributes are defined.
101	fn collection_attribute(collection: &Self::CollectionId, key: &[u8]) -> Option<Vec<u8>> {
102		if key.is_empty() {
103			// We make the empty key map to the item metadata value.
104			CollectionMetadataOf::<T, I>::get(collection).map(|m| m.data.into())
105		} else {
106			let key = BoundedSlice::<_, _>::try_from(key).ok()?;
107			Attribute::<T, I>::get((
108				collection,
109				Option::<T::ItemId>::None,
110				AttributeNamespace::CollectionOwner,
111				key,
112			))
113			.map(|a| a.0.into())
114		}
115	}
116
117	/// Returns `true` if the `item` of `collection` may be transferred.
118	///
119	/// Default implementation is that all items are transferable.
120	fn can_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> bool {
121		use PalletAttributes::TransferDisabled;
122		match Self::has_system_attribute(&collection, &item, TransferDisabled) {
123			Ok(transfer_disabled) if transfer_disabled => return false,
124			_ => (),
125		}
126		match (
127			CollectionConfigOf::<T, I>::get(collection),
128			ItemConfigOf::<T, I>::get(collection, item),
129		) {
130			(Some(cc), Some(ic))
131				if cc.is_setting_enabled(CollectionSetting::TransferableItems) &&
132					ic.is_setting_enabled(ItemSetting::Transferable) =>
133			{
134				true
135			},
136			_ => false,
137		}
138	}
139}
140
141impl<T: Config<I>, I: 'static> InspectRole<<T as SystemConfig>::AccountId> for Pallet<T, I> {
142	fn is_issuer(collection: &Self::CollectionId, who: &<T as SystemConfig>::AccountId) -> bool {
143		Self::has_role(collection, who, CollectionRole::Issuer)
144	}
145	fn is_admin(collection: &Self::CollectionId, who: &<T as SystemConfig>::AccountId) -> bool {
146		Self::has_role(collection, who, CollectionRole::Admin)
147	}
148	fn is_freezer(collection: &Self::CollectionId, who: &<T as SystemConfig>::AccountId) -> bool {
149		Self::has_role(collection, who, CollectionRole::Freezer)
150	}
151}
152
153impl<T: Config<I>, I: 'static> Create<<T as SystemConfig>::AccountId, CollectionConfigFor<T, I>>
154	for Pallet<T, I>
155{
156	/// Create a `collection` of nonfungible items to be owned by `who` and managed by `admin`.
157	fn create_collection(
158		who: &T::AccountId,
159		admin: &T::AccountId,
160		config: &CollectionConfigFor<T, I>,
161	) -> Result<T::CollectionId, DispatchError> {
162		// DepositRequired can be disabled by calling the force_create() only
163		ensure!(
164			!config.has_disabled_setting(CollectionSetting::DepositRequired),
165			Error::<T, I>::WrongSetting
166		);
167
168		let collection = NextCollectionId::<T, I>::get()
169			.or(T::CollectionId::initial_value())
170			.ok_or(Error::<T, I>::UnknownCollection)?;
171
172		Self::do_create_collection(
173			collection,
174			who.clone(),
175			admin.clone(),
176			*config,
177			T::CollectionDeposit::get(),
178			Event::Created { collection, creator: who.clone(), owner: admin.clone() },
179		)?;
180
181		Self::set_next_collection_id(collection);
182
183		Ok(collection)
184	}
185
186	/// Create a collection of nonfungible items with `collection` Id to be owned by `who` and
187	/// managed by `admin`. Should be only used for applications that do not have an
188	/// incremental order for the collection IDs and is a replacement for the auto id creation.
189	///
190	///
191	/// SAFETY: This function can break the pallet if it is used in combination with the auto
192	/// increment functionality, as it can claim a value in the ID sequence.
193	fn create_collection_with_id(
194		collection: T::CollectionId,
195		who: &T::AccountId,
196		admin: &T::AccountId,
197		config: &CollectionConfigFor<T, I>,
198	) -> Result<(), DispatchError> {
199		// DepositRequired can be disabled by calling the force_create() only
200		ensure!(
201			!config.has_disabled_setting(CollectionSetting::DepositRequired),
202			Error::<T, I>::WrongSetting
203		);
204
205		Self::do_create_collection(
206			collection,
207			who.clone(),
208			admin.clone(),
209			*config,
210			T::CollectionDeposit::get(),
211			Event::Created { collection, creator: who.clone(), owner: admin.clone() },
212		)
213	}
214}
215
216impl<T: Config<I>, I: 'static> Destroy<<T as SystemConfig>::AccountId> for Pallet<T, I> {
217	type DestroyWitness = DestroyWitness;
218
219	fn get_destroy_witness(collection: &Self::CollectionId) -> Option<DestroyWitness> {
220		Collection::<T, I>::get(collection).map(|a| a.destroy_witness())
221	}
222
223	fn destroy(
224		collection: Self::CollectionId,
225		witness: Self::DestroyWitness,
226		maybe_check_owner: Option<T::AccountId>,
227	) -> Result<Self::DestroyWitness, DispatchError> {
228		Self::do_destroy_collection(collection, witness, maybe_check_owner)
229	}
230}
231
232impl<T: Config<I>, I: 'static> Mutate<<T as SystemConfig>::AccountId, ItemConfig> for Pallet<T, I> {
233	fn mint_into(
234		collection: &Self::CollectionId,
235		item: &Self::ItemId,
236		who: &T::AccountId,
237		item_config: &ItemConfig,
238		deposit_collection_owner: bool,
239	) -> DispatchResult {
240		Self::do_mint(
241			*collection,
242			*item,
243			match deposit_collection_owner {
244				true => None,
245				false => Some(who.clone()),
246			},
247			who.clone(),
248			*item_config,
249			|_, _| Ok(()),
250		)
251	}
252
253	fn burn(
254		collection: &Self::CollectionId,
255		item: &Self::ItemId,
256		maybe_check_owner: Option<&T::AccountId>,
257	) -> DispatchResult {
258		Self::do_burn(*collection, *item, |d| {
259			if let Some(check_owner) = maybe_check_owner {
260				if &d.owner != check_owner {
261					return Err(Error::<T, I>::NoPermission.into());
262				}
263			}
264			Ok(())
265		})
266	}
267
268	fn set_attribute(
269		collection: &Self::CollectionId,
270		item: &Self::ItemId,
271		key: &[u8],
272		value: &[u8],
273	) -> DispatchResult {
274		Self::do_force_set_attribute(
275			None,
276			*collection,
277			Some(*item),
278			AttributeNamespace::Pallet,
279			Self::construct_attribute_key(key.to_vec())?,
280			Self::construct_attribute_value(value.to_vec())?,
281		)
282	}
283
284	fn set_typed_attribute<K: Encode, V: Encode>(
285		collection: &Self::CollectionId,
286		item: &Self::ItemId,
287		key: &K,
288		value: &V,
289	) -> DispatchResult {
290		key.using_encoded(|k| {
291			value.using_encoded(|v| {
292				<Self as Mutate<T::AccountId, ItemConfig>>::set_attribute(collection, item, k, v)
293			})
294		})
295	}
296
297	fn set_collection_attribute(
298		collection: &Self::CollectionId,
299		key: &[u8],
300		value: &[u8],
301	) -> DispatchResult {
302		Self::do_force_set_attribute(
303			None,
304			*collection,
305			None,
306			AttributeNamespace::Pallet,
307			Self::construct_attribute_key(key.to_vec())?,
308			Self::construct_attribute_value(value.to_vec())?,
309		)
310	}
311
312	fn set_typed_collection_attribute<K: Encode, V: Encode>(
313		collection: &Self::CollectionId,
314		key: &K,
315		value: &V,
316	) -> DispatchResult {
317		key.using_encoded(|k| {
318			value.using_encoded(|v| {
319				<Self as Mutate<T::AccountId, ItemConfig>>::set_collection_attribute(
320					collection, k, v,
321				)
322			})
323		})
324	}
325
326	fn set_item_metadata(
327		who: Option<&T::AccountId>,
328		collection: &Self::CollectionId,
329		item: &Self::ItemId,
330		data: &[u8],
331	) -> DispatchResult {
332		Self::do_set_item_metadata(
333			who.cloned(),
334			*collection,
335			*item,
336			Self::construct_metadata(data.to_vec())?,
337			None,
338		)
339	}
340
341	fn set_collection_metadata(
342		who: Option<&T::AccountId>,
343		collection: &Self::CollectionId,
344		data: &[u8],
345	) -> DispatchResult {
346		Self::do_set_collection_metadata(
347			who.cloned(),
348			*collection,
349			Self::construct_metadata(data.to_vec())?,
350		)
351	}
352
353	fn clear_attribute(
354		collection: &Self::CollectionId,
355		item: &Self::ItemId,
356		key: &[u8],
357	) -> DispatchResult {
358		Self::do_clear_attribute(
359			None,
360			*collection,
361			Some(*item),
362			AttributeNamespace::Pallet,
363			Self::construct_attribute_key(key.to_vec())?,
364		)
365	}
366
367	fn clear_typed_attribute<K: Encode>(
368		collection: &Self::CollectionId,
369		item: &Self::ItemId,
370		key: &K,
371	) -> DispatchResult {
372		key.using_encoded(|k| {
373			<Self as Mutate<T::AccountId, ItemConfig>>::clear_attribute(collection, item, k)
374		})
375	}
376
377	fn clear_collection_attribute(collection: &Self::CollectionId, key: &[u8]) -> DispatchResult {
378		Self::do_clear_attribute(
379			None,
380			*collection,
381			None,
382			AttributeNamespace::Pallet,
383			Self::construct_attribute_key(key.to_vec())?,
384		)
385	}
386
387	fn clear_typed_collection_attribute<K: Encode>(
388		collection: &Self::CollectionId,
389		key: &K,
390	) -> DispatchResult {
391		key.using_encoded(|k| {
392			<Self as Mutate<T::AccountId, ItemConfig>>::clear_collection_attribute(collection, k)
393		})
394	}
395
396	fn clear_item_metadata(
397		who: Option<&T::AccountId>,
398		collection: &Self::CollectionId,
399		item: &Self::ItemId,
400	) -> DispatchResult {
401		Self::do_clear_item_metadata(who.cloned(), *collection, *item)
402	}
403
404	fn clear_collection_metadata(
405		who: Option<&T::AccountId>,
406		collection: &Self::CollectionId,
407	) -> DispatchResult {
408		Self::do_clear_collection_metadata(who.cloned(), *collection)
409	}
410}
411
412impl<T: Config<I>, I: 'static> Transfer<T::AccountId> for Pallet<T, I> {
413	fn transfer(
414		collection: &Self::CollectionId,
415		item: &Self::ItemId,
416		destination: &T::AccountId,
417	) -> DispatchResult {
418		Self::do_transfer(*collection, *item, destination.clone(), |_, _| Ok(()))
419	}
420
421	fn disable_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> DispatchResult {
422		let transfer_disabled =
423			Self::has_system_attribute(&collection, &item, PalletAttributes::TransferDisabled)?;
424		// Can't lock the item twice
425		if transfer_disabled {
426			return Err(Error::<T, I>::ItemLocked.into());
427		}
428
429		<Self as Mutate<T::AccountId, ItemConfig>>::set_attribute(
430			collection,
431			item,
432			&PalletAttributes::<Self::CollectionId>::TransferDisabled.encode(),
433			&[],
434		)
435	}
436
437	fn enable_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> DispatchResult {
438		<Self as Mutate<T::AccountId, ItemConfig>>::clear_attribute(
439			collection,
440			item,
441			&PalletAttributes::<Self::CollectionId>::TransferDisabled.encode(),
442		)
443	}
444}
445
446impl<T: Config<I>, I: 'static> Trading<T::AccountId, ItemPrice<T, I>> for Pallet<T, I> {
447	fn buy_item(
448		collection: &Self::CollectionId,
449		item: &Self::ItemId,
450		buyer: &T::AccountId,
451		bid_price: &ItemPrice<T, I>,
452	) -> DispatchResult {
453		Self::do_buy_item(*collection, *item, buyer.clone(), *bid_price)
454	}
455
456	fn set_price(
457		collection: &Self::CollectionId,
458		item: &Self::ItemId,
459		sender: &T::AccountId,
460		price: Option<ItemPrice<T, I>>,
461		whitelisted_buyer: Option<T::AccountId>,
462	) -> DispatchResult {
463		Self::do_set_price(*collection, *item, sender.clone(), price, whitelisted_buyer)
464	}
465
466	fn item_price(collection: &Self::CollectionId, item: &Self::ItemId) -> Option<ItemPrice<T, I>> {
467		ItemPriceOf::<T, I>::get(collection, item).map(|a| a.0)
468	}
469}
470
471impl<T: Config<I>, I: 'static> InspectEnumerable<T::AccountId> for Pallet<T, I> {
472	type CollectionsIterator = KeyPrefixIterator<<T as Config<I>>::CollectionId>;
473	type ItemsIterator = KeyPrefixIterator<<T as Config<I>>::ItemId>;
474	type OwnedIterator =
475		KeyPrefixIterator<(<T as Config<I>>::CollectionId, <T as Config<I>>::ItemId)>;
476	type OwnedInCollectionIterator = KeyPrefixIterator<<T as Config<I>>::ItemId>;
477
478	/// Returns an iterator of the collections in existence.
479	///
480	/// NOTE: iterating this list invokes a storage read per item.
481	fn collections() -> Self::CollectionsIterator {
482		Collection::<T, I>::iter_keys()
483	}
484
485	/// Returns an iterator of the items of a `collection` in existence.
486	///
487	/// NOTE: iterating this list invokes a storage read per item.
488	fn items(collection: &Self::CollectionId) -> Self::ItemsIterator {
489		Item::<T, I>::iter_key_prefix(collection)
490	}
491
492	/// Returns an iterator of the items of all collections owned by `who`.
493	///
494	/// NOTE: iterating this list invokes a storage read per item.
495	fn owned(who: &T::AccountId) -> Self::OwnedIterator {
496		Account::<T, I>::iter_key_prefix((who,))
497	}
498
499	/// Returns an iterator of the items of `collection` owned by `who`.
500	///
501	/// NOTE: iterating this list invokes a storage read per item.
502	fn owned_in_collection(
503		collection: &Self::CollectionId,
504		who: &T::AccountId,
505	) -> Self::OwnedInCollectionIterator {
506		Account::<T, I>::iter_key_prefix((who, collection))
507	}
508}