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				true,
134			_ => false,
135		}
136	}
137}
138
139impl<T: Config<I>, I: 'static> InspectRole<<T as SystemConfig>::AccountId> for Pallet<T, I> {
140	fn is_issuer(collection: &Self::CollectionId, who: &<T as SystemConfig>::AccountId) -> bool {
141		Self::has_role(collection, who, CollectionRole::Issuer)
142	}
143	fn is_admin(collection: &Self::CollectionId, who: &<T as SystemConfig>::AccountId) -> bool {
144		Self::has_role(collection, who, CollectionRole::Admin)
145	}
146	fn is_freezer(collection: &Self::CollectionId, who: &<T as SystemConfig>::AccountId) -> bool {
147		Self::has_role(collection, who, CollectionRole::Freezer)
148	}
149}
150
151impl<T: Config<I>, I: 'static> Create<<T as SystemConfig>::AccountId, CollectionConfigFor<T, I>>
152	for Pallet<T, I>
153{
154	/// Create a `collection` of nonfungible items to be owned by `who` and managed by `admin`.
155	fn create_collection(
156		who: &T::AccountId,
157		admin: &T::AccountId,
158		config: &CollectionConfigFor<T, I>,
159	) -> Result<T::CollectionId, DispatchError> {
160		// DepositRequired can be disabled by calling the force_create() only
161		ensure!(
162			!config.has_disabled_setting(CollectionSetting::DepositRequired),
163			Error::<T, I>::WrongSetting
164		);
165
166		let collection = NextCollectionId::<T, I>::get()
167			.or(T::CollectionId::initial_value())
168			.ok_or(Error::<T, I>::UnknownCollection)?;
169
170		Self::do_create_collection(
171			collection,
172			who.clone(),
173			admin.clone(),
174			*config,
175			T::CollectionDeposit::get(),
176			Event::Created { collection, creator: who.clone(), owner: admin.clone() },
177		)?;
178
179		Self::set_next_collection_id(collection);
180
181		Ok(collection)
182	}
183
184	/// Create a collection of nonfungible items with `collection` Id to be owned by `who` and
185	/// managed by `admin`. Should be only used for applications that do not have an
186	/// incremental order for the collection IDs and is a replacement for the auto id creation.
187	///
188	///
189	/// SAFETY: This function can break the pallet if it is used in combination with the auto
190	/// increment functionality, as it can claim a value in the ID sequence.
191	fn create_collection_with_id(
192		collection: T::CollectionId,
193		who: &T::AccountId,
194		admin: &T::AccountId,
195		config: &CollectionConfigFor<T, I>,
196	) -> Result<(), DispatchError> {
197		// DepositRequired can be disabled by calling the force_create() only
198		ensure!(
199			!config.has_disabled_setting(CollectionSetting::DepositRequired),
200			Error::<T, I>::WrongSetting
201		);
202
203		Self::do_create_collection(
204			collection,
205			who.clone(),
206			admin.clone(),
207			*config,
208			T::CollectionDeposit::get(),
209			Event::Created { collection, creator: who.clone(), owner: admin.clone() },
210		)
211	}
212}
213
214impl<T: Config<I>, I: 'static> Destroy<<T as SystemConfig>::AccountId> for Pallet<T, I> {
215	type DestroyWitness = DestroyWitness;
216
217	fn get_destroy_witness(collection: &Self::CollectionId) -> Option<DestroyWitness> {
218		Collection::<T, I>::get(collection).map(|a| a.destroy_witness())
219	}
220
221	fn destroy(
222		collection: Self::CollectionId,
223		witness: Self::DestroyWitness,
224		maybe_check_owner: Option<T::AccountId>,
225	) -> Result<Self::DestroyWitness, DispatchError> {
226		Self::do_destroy_collection(collection, witness, maybe_check_owner)
227	}
228}
229
230impl<T: Config<I>, I: 'static> Mutate<<T as SystemConfig>::AccountId, ItemConfig> for Pallet<T, I> {
231	fn mint_into(
232		collection: &Self::CollectionId,
233		item: &Self::ItemId,
234		who: &T::AccountId,
235		item_config: &ItemConfig,
236		deposit_collection_owner: bool,
237	) -> DispatchResult {
238		Self::do_mint(
239			*collection,
240			*item,
241			match deposit_collection_owner {
242				true => None,
243				false => Some(who.clone()),
244			},
245			who.clone(),
246			*item_config,
247			|_, _| Ok(()),
248		)
249	}
250
251	fn burn(
252		collection: &Self::CollectionId,
253		item: &Self::ItemId,
254		maybe_check_owner: Option<&T::AccountId>,
255	) -> DispatchResult {
256		Self::do_burn(*collection, *item, |d| {
257			if let Some(check_owner) = maybe_check_owner {
258				if &d.owner != check_owner {
259					return Err(Error::<T, I>::NoPermission.into())
260				}
261			}
262			Ok(())
263		})
264	}
265
266	fn set_attribute(
267		collection: &Self::CollectionId,
268		item: &Self::ItemId,
269		key: &[u8],
270		value: &[u8],
271	) -> DispatchResult {
272		Self::do_force_set_attribute(
273			None,
274			*collection,
275			Some(*item),
276			AttributeNamespace::Pallet,
277			Self::construct_attribute_key(key.to_vec())?,
278			Self::construct_attribute_value(value.to_vec())?,
279		)
280	}
281
282	fn set_typed_attribute<K: Encode, V: Encode>(
283		collection: &Self::CollectionId,
284		item: &Self::ItemId,
285		key: &K,
286		value: &V,
287	) -> DispatchResult {
288		key.using_encoded(|k| {
289			value.using_encoded(|v| {
290				<Self as Mutate<T::AccountId, ItemConfig>>::set_attribute(collection, item, k, v)
291			})
292		})
293	}
294
295	fn set_collection_attribute(
296		collection: &Self::CollectionId,
297		key: &[u8],
298		value: &[u8],
299	) -> DispatchResult {
300		Self::do_force_set_attribute(
301			None,
302			*collection,
303			None,
304			AttributeNamespace::Pallet,
305			Self::construct_attribute_key(key.to_vec())?,
306			Self::construct_attribute_value(value.to_vec())?,
307		)
308	}
309
310	fn set_typed_collection_attribute<K: Encode, V: Encode>(
311		collection: &Self::CollectionId,
312		key: &K,
313		value: &V,
314	) -> DispatchResult {
315		key.using_encoded(|k| {
316			value.using_encoded(|v| {
317				<Self as Mutate<T::AccountId, ItemConfig>>::set_collection_attribute(
318					collection, k, v,
319				)
320			})
321		})
322	}
323
324	fn set_item_metadata(
325		who: Option<&T::AccountId>,
326		collection: &Self::CollectionId,
327		item: &Self::ItemId,
328		data: &[u8],
329	) -> DispatchResult {
330		Self::do_set_item_metadata(
331			who.cloned(),
332			*collection,
333			*item,
334			Self::construct_metadata(data.to_vec())?,
335			None,
336		)
337	}
338
339	fn set_collection_metadata(
340		who: Option<&T::AccountId>,
341		collection: &Self::CollectionId,
342		data: &[u8],
343	) -> DispatchResult {
344		Self::do_set_collection_metadata(
345			who.cloned(),
346			*collection,
347			Self::construct_metadata(data.to_vec())?,
348		)
349	}
350
351	fn clear_attribute(
352		collection: &Self::CollectionId,
353		item: &Self::ItemId,
354		key: &[u8],
355	) -> DispatchResult {
356		Self::do_clear_attribute(
357			None,
358			*collection,
359			Some(*item),
360			AttributeNamespace::Pallet,
361			Self::construct_attribute_key(key.to_vec())?,
362		)
363	}
364
365	fn clear_typed_attribute<K: Encode>(
366		collection: &Self::CollectionId,
367		item: &Self::ItemId,
368		key: &K,
369	) -> DispatchResult {
370		key.using_encoded(|k| {
371			<Self as Mutate<T::AccountId, ItemConfig>>::clear_attribute(collection, item, k)
372		})
373	}
374
375	fn clear_collection_attribute(collection: &Self::CollectionId, key: &[u8]) -> DispatchResult {
376		Self::do_clear_attribute(
377			None,
378			*collection,
379			None,
380			AttributeNamespace::Pallet,
381			Self::construct_attribute_key(key.to_vec())?,
382		)
383	}
384
385	fn clear_typed_collection_attribute<K: Encode>(
386		collection: &Self::CollectionId,
387		key: &K,
388	) -> DispatchResult {
389		key.using_encoded(|k| {
390			<Self as Mutate<T::AccountId, ItemConfig>>::clear_collection_attribute(collection, k)
391		})
392	}
393
394	fn clear_item_metadata(
395		who: Option<&T::AccountId>,
396		collection: &Self::CollectionId,
397		item: &Self::ItemId,
398	) -> DispatchResult {
399		Self::do_clear_item_metadata(who.cloned(), *collection, *item)
400	}
401
402	fn clear_collection_metadata(
403		who: Option<&T::AccountId>,
404		collection: &Self::CollectionId,
405	) -> DispatchResult {
406		Self::do_clear_collection_metadata(who.cloned(), *collection)
407	}
408}
409
410impl<T: Config<I>, I: 'static> Transfer<T::AccountId> for Pallet<T, I> {
411	fn transfer(
412		collection: &Self::CollectionId,
413		item: &Self::ItemId,
414		destination: &T::AccountId,
415	) -> DispatchResult {
416		Self::do_transfer(*collection, *item, destination.clone(), |_, _| Ok(()))
417	}
418
419	fn disable_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> DispatchResult {
420		let transfer_disabled =
421			Self::has_system_attribute(&collection, &item, PalletAttributes::TransferDisabled)?;
422		// Can't lock the item twice
423		if transfer_disabled {
424			return Err(Error::<T, I>::ItemLocked.into())
425		}
426
427		<Self as Mutate<T::AccountId, ItemConfig>>::set_attribute(
428			collection,
429			item,
430			&PalletAttributes::<Self::CollectionId>::TransferDisabled.encode(),
431			&[],
432		)
433	}
434
435	fn enable_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> DispatchResult {
436		<Self as Mutate<T::AccountId, ItemConfig>>::clear_attribute(
437			collection,
438			item,
439			&PalletAttributes::<Self::CollectionId>::TransferDisabled.encode(),
440		)
441	}
442}
443
444impl<T: Config<I>, I: 'static> Trading<T::AccountId, ItemPrice<T, I>> for Pallet<T, I> {
445	fn buy_item(
446		collection: &Self::CollectionId,
447		item: &Self::ItemId,
448		buyer: &T::AccountId,
449		bid_price: &ItemPrice<T, I>,
450	) -> DispatchResult {
451		Self::do_buy_item(*collection, *item, buyer.clone(), *bid_price)
452	}
453
454	fn set_price(
455		collection: &Self::CollectionId,
456		item: &Self::ItemId,
457		sender: &T::AccountId,
458		price: Option<ItemPrice<T, I>>,
459		whitelisted_buyer: Option<T::AccountId>,
460	) -> DispatchResult {
461		Self::do_set_price(*collection, *item, sender.clone(), price, whitelisted_buyer)
462	}
463
464	fn item_price(collection: &Self::CollectionId, item: &Self::ItemId) -> Option<ItemPrice<T, I>> {
465		ItemPriceOf::<T, I>::get(collection, item).map(|a| a.0)
466	}
467}
468
469impl<T: Config<I>, I: 'static> InspectEnumerable<T::AccountId> for Pallet<T, I> {
470	type CollectionsIterator = KeyPrefixIterator<<T as Config<I>>::CollectionId>;
471	type ItemsIterator = KeyPrefixIterator<<T as Config<I>>::ItemId>;
472	type OwnedIterator =
473		KeyPrefixIterator<(<T as Config<I>>::CollectionId, <T as Config<I>>::ItemId)>;
474	type OwnedInCollectionIterator = KeyPrefixIterator<<T as Config<I>>::ItemId>;
475
476	/// Returns an iterator of the collections in existence.
477	///
478	/// NOTE: iterating this list invokes a storage read per item.
479	fn collections() -> Self::CollectionsIterator {
480		Collection::<T, I>::iter_keys()
481	}
482
483	/// Returns an iterator of the items of a `collection` in existence.
484	///
485	/// NOTE: iterating this list invokes a storage read per item.
486	fn items(collection: &Self::CollectionId) -> Self::ItemsIterator {
487		Item::<T, I>::iter_key_prefix(collection)
488	}
489
490	/// Returns an iterator of the items of all collections owned by `who`.
491	///
492	/// NOTE: iterating this list invokes a storage read per item.
493	fn owned(who: &T::AccountId) -> Self::OwnedIterator {
494		Account::<T, I>::iter_key_prefix((who,))
495	}
496
497	/// Returns an iterator of the items of `collection` owned by `who`.
498	///
499	/// NOTE: iterating this list invokes a storage read per item.
500	fn owned_in_collection(
501		collection: &Self::CollectionId,
502		who: &T::AccountId,
503	) -> Self::OwnedInCollectionIterator {
504		Account::<T, I>::iter_key_prefix((who, collection))
505	}
506}