referrerpolicy=no-referrer-when-downgrade

frame_support/traits/tokens/
nonfungibles_v2.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//! Traits for dealing with multiple collections of non-fungible items.
19//!
20//! This assumes a dual-level namespace identified by `Inspect::ItemId`, and could
21//! reasonably be implemented by pallets which want to expose multiple independent collections of
22//! NFT-like objects.
23//!
24//! For an NFT API which has single-level namespacing, the traits in `nonfungible` are better to
25//! use.
26//!
27//! Implementations of these traits may be converted to implementations of corresponding
28//! `nonfungible` traits by using the `nonfungible::ItemOf` type adapter.
29
30use crate::dispatch::{DispatchResult, Parameter};
31use alloc::vec::Vec;
32use codec::{Decode, Encode};
33use sp_runtime::{DispatchError, TokenError};
34
35/// Trait for providing an interface to many read-only NFT-like sets of items.
36pub trait Inspect<AccountId> {
37	/// Type for identifying an item.
38	type ItemId: Parameter;
39
40	/// Type for identifying a collection (an identifier for an independent collection of
41	/// items).
42	type CollectionId: Parameter;
43
44	/// Returns the owner of `item` of `collection`, or `None` if the item doesn't exist
45	/// (or somehow has no owner).
46	fn owner(collection: &Self::CollectionId, item: &Self::ItemId) -> Option<AccountId>;
47
48	/// Returns the owner of the `collection`, if there is one. For many NFTs this may not
49	/// make any sense, so users of this API should not be surprised to find a collection
50	/// results in `None` here.
51	fn collection_owner(_collection: &Self::CollectionId) -> Option<AccountId> {
52		None
53	}
54
55	/// Returns the attribute value of `item` of `collection` corresponding to `key`.
56	///
57	/// By default this is `None`; no attributes are defined.
58	fn attribute(
59		_collection: &Self::CollectionId,
60		_item: &Self::ItemId,
61		_key: &[u8],
62	) -> Option<Vec<u8>> {
63		None
64	}
65
66	/// Returns the custom attribute value of `item` of `collection` corresponding to `key`.
67	///
68	/// By default this is `None`; no attributes are defined.
69	fn custom_attribute(
70		_account: &AccountId,
71		_collection: &Self::CollectionId,
72		_item: &Self::ItemId,
73		_key: &[u8],
74	) -> Option<Vec<u8>> {
75		None
76	}
77
78	/// Returns the system attribute value of `item` of `collection` corresponding to `key` if
79	/// `item` is `Some`. Otherwise, returns the system attribute value of `collection`
80	/// corresponding to `key`.
81	///
82	/// By default this is `None`; no attributes are defined.
83	fn system_attribute(
84		_collection: &Self::CollectionId,
85		_item: Option<&Self::ItemId>,
86		_key: &[u8],
87	) -> Option<Vec<u8>> {
88		None
89	}
90
91	/// Returns the strongly-typed attribute value of `item` of `collection` corresponding to
92	/// `key`.
93	///
94	/// By default this just attempts to use `attribute`.
95	fn typed_attribute<K: Encode, V: Decode>(
96		collection: &Self::CollectionId,
97		item: &Self::ItemId,
98		key: &K,
99	) -> Option<V> {
100		key.using_encoded(|d| Self::attribute(collection, item, d))
101			.and_then(|v| V::decode(&mut &v[..]).ok())
102	}
103
104	/// Returns the strongly-typed custom attribute value of `item` of `collection` corresponding to
105	/// `key`.
106	///
107	/// By default this just attempts to use `custom_attribute`.
108	fn typed_custom_attribute<K: Encode, V: Decode>(
109		account: &AccountId,
110		collection: &Self::CollectionId,
111		item: &Self::ItemId,
112		key: &K,
113	) -> Option<V> {
114		key.using_encoded(|d| Self::custom_attribute(account, collection, item, d))
115			.and_then(|v| V::decode(&mut &v[..]).ok())
116	}
117
118	/// Returns the strongly-typed system attribute value of `item` corresponding to `key` if
119	/// `item` is `Some`. Otherwise, returns the strongly-typed system attribute value of
120	/// `collection` corresponding to `key`.
121	///
122	/// By default this just attempts to use `system_attribute`.
123	fn typed_system_attribute<K: Encode, V: Decode>(
124		collection: &Self::CollectionId,
125		item: Option<&Self::ItemId>,
126		key: &K,
127	) -> Option<V> {
128		key.using_encoded(|d| Self::system_attribute(collection, item, d))
129			.and_then(|v| V::decode(&mut &v[..]).ok())
130	}
131
132	/// Returns the attribute value of `collection` corresponding to `key`.
133	///
134	/// By default this is `None`; no attributes are defined.
135	fn collection_attribute(_collection: &Self::CollectionId, _key: &[u8]) -> Option<Vec<u8>> {
136		None
137	}
138
139	/// Returns the strongly-typed attribute value of `collection` corresponding to `key`.
140	///
141	/// By default this just attempts to use `collection_attribute`.
142	fn typed_collection_attribute<K: Encode, V: Decode>(
143		collection: &Self::CollectionId,
144		key: &K,
145	) -> Option<V> {
146		key.using_encoded(|d| Self::collection_attribute(collection, d))
147			.and_then(|v| V::decode(&mut &v[..]).ok())
148	}
149
150	/// Returns `true` if the `item` of `collection` may be transferred.
151	///
152	/// Default implementation is that all items are transferable.
153	fn can_transfer(_collection: &Self::CollectionId, _item: &Self::ItemId) -> bool {
154		true
155	}
156}
157
158/// Interface for enumerating items in existence or owned by a given account over many collections
159/// of NFTs.
160pub trait InspectEnumerable<AccountId>: Inspect<AccountId> {
161	/// The iterator type for [`Self::collections`].
162	type CollectionsIterator: Iterator<Item = Self::CollectionId>;
163	/// The iterator type for [`Self::items`].
164	type ItemsIterator: Iterator<Item = Self::ItemId>;
165	/// The iterator type for [`Self::owned`].
166	type OwnedIterator: Iterator<Item = (Self::CollectionId, Self::ItemId)>;
167	/// The iterator type for [`Self::owned_in_collection`].
168	type OwnedInCollectionIterator: Iterator<Item = Self::ItemId>;
169
170	/// Returns an iterator of the collections in existence.
171	fn collections() -> Self::CollectionsIterator;
172
173	/// Returns an iterator of the items of a `collection` in existence.
174	fn items(collection: &Self::CollectionId) -> Self::ItemsIterator;
175
176	/// Returns an iterator of the items of all collections owned by `who`.
177	fn owned(who: &AccountId) -> Self::OwnedIterator;
178
179	/// Returns an iterator of the items of `collection` owned by `who`.
180	fn owned_in_collection(
181		collection: &Self::CollectionId,
182		who: &AccountId,
183	) -> Self::OwnedInCollectionIterator;
184}
185
186/// Trait for providing an interface to check the account's role within the collection.
187pub trait InspectRole<AccountId>: Inspect<AccountId> {
188	/// Returns `true` if `who` is the issuer of the `collection`.
189	fn is_issuer(collection: &Self::CollectionId, who: &AccountId) -> bool;
190	/// Returns `true` if `who` is the admin of the `collection`.
191	fn is_admin(collection: &Self::CollectionId, who: &AccountId) -> bool;
192	/// Returns `true` if `who` is the freezer of the `collection`.
193	fn is_freezer(collection: &Self::CollectionId, who: &AccountId) -> bool;
194}
195
196/// Trait for providing the ability to create collections of nonfungible items.
197pub trait Create<AccountId, CollectionConfig>: Inspect<AccountId> {
198	/// Create a `collection` of nonfungible items to be owned by `who` and managed by `admin`.
199	fn create_collection(
200		who: &AccountId,
201		admin: &AccountId,
202		config: &CollectionConfig,
203	) -> Result<Self::CollectionId, DispatchError>;
204
205	fn create_collection_with_id(
206		collection: Self::CollectionId,
207		who: &AccountId,
208		admin: &AccountId,
209		config: &CollectionConfig,
210	) -> Result<(), DispatchError>;
211}
212
213/// Trait for providing the ability to destroy collections of nonfungible items.
214pub trait Destroy<AccountId>: Inspect<AccountId> {
215	/// The witness data needed to destroy an item.
216	type DestroyWitness: Parameter;
217
218	/// Provide the appropriate witness data needed to destroy an item.
219	fn get_destroy_witness(collection: &Self::CollectionId) -> Option<Self::DestroyWitness>;
220
221	/// Destroy an existing fungible item.
222	/// * `collection`: The `CollectionId` to be destroyed.
223	/// * `witness`: Any witness data that needs to be provided to complete the operation
224	///   successfully.
225	/// * `maybe_check_owner`: An optional `AccountId` that can be used to authorize the destroy
226	///   command. If not provided, we will not do any authorization checks before destroying the
227	///   item.
228	///
229	/// If successful, this function will return the actual witness data from the destroyed item.
230	/// This may be different than the witness data provided, and can be used to refund weight.
231	fn destroy(
232		collection: Self::CollectionId,
233		witness: Self::DestroyWitness,
234		maybe_check_owner: Option<AccountId>,
235	) -> Result<Self::DestroyWitness, DispatchError>;
236}
237
238/// Trait for providing an interface for multiple collections of NFT-like items which may be
239/// minted, burned and/or have attributes and metadata set on them.
240pub trait Mutate<AccountId, ItemConfig>: Inspect<AccountId> {
241	/// Mint some `item` of `collection` to be owned by `who`.
242	///
243	/// By default, this is not a supported operation.
244	fn mint_into(
245		_collection: &Self::CollectionId,
246		_item: &Self::ItemId,
247		_who: &AccountId,
248		_config: &ItemConfig,
249		_deposit_collection_owner: bool,
250	) -> DispatchResult {
251		Err(TokenError::Unsupported.into())
252	}
253
254	/// Burn some `item` of `collection`.
255	///
256	/// By default, this is not a supported operation.
257	fn burn(
258		_collection: &Self::CollectionId,
259		_item: &Self::ItemId,
260		_maybe_check_owner: Option<&AccountId>,
261	) -> DispatchResult {
262		Err(TokenError::Unsupported.into())
263	}
264
265	/// Set attribute `value` of `item` of `collection`'s `key`.
266	///
267	/// By default, this is not a supported operation.
268	fn set_attribute(
269		_collection: &Self::CollectionId,
270		_item: &Self::ItemId,
271		_key: &[u8],
272		_value: &[u8],
273	) -> DispatchResult {
274		Err(TokenError::Unsupported.into())
275	}
276
277	/// Attempt to set the strongly-typed attribute `value` of `item` of `collection`'s `key`.
278	///
279	/// By default this just attempts to use `set_attribute`.
280	fn set_typed_attribute<K: Encode, V: Encode>(
281		collection: &Self::CollectionId,
282		item: &Self::ItemId,
283		key: &K,
284		value: &V,
285	) -> DispatchResult {
286		key.using_encoded(|k| value.using_encoded(|v| Self::set_attribute(collection, item, k, v)))
287	}
288
289	/// Set attribute `value` of `collection`'s `key`.
290	///
291	/// By default, this is not a supported operation.
292	fn set_collection_attribute(
293		_collection: &Self::CollectionId,
294		_key: &[u8],
295		_value: &[u8],
296	) -> DispatchResult {
297		Err(TokenError::Unsupported.into())
298	}
299
300	/// Attempt to set the strongly-typed attribute `value` of `collection`'s `key`.
301	///
302	/// By default this just attempts to use `set_attribute`.
303	fn set_typed_collection_attribute<K: Encode, V: Encode>(
304		collection: &Self::CollectionId,
305		key: &K,
306		value: &V,
307	) -> DispatchResult {
308		key.using_encoded(|k| {
309			value.using_encoded(|v| Self::set_collection_attribute(collection, k, v))
310		})
311	}
312
313	/// Set the metadata `data` of an `item` of `collection`.
314	///
315	/// By default, this is not a supported operation.
316	fn set_item_metadata(
317		_who: Option<&AccountId>,
318		_collection: &Self::CollectionId,
319		_item: &Self::ItemId,
320		_data: &[u8],
321	) -> DispatchResult {
322		Err(TokenError::Unsupported.into())
323	}
324
325	/// Set the metadata `data` of a `collection`.
326	///
327	/// By default, this is not a supported operation.
328	fn set_collection_metadata(
329		_who: Option<&AccountId>,
330		_collection: &Self::CollectionId,
331		_data: &[u8],
332	) -> DispatchResult {
333		Err(TokenError::Unsupported.into())
334	}
335
336	/// Clear attribute of `item` of `collection`'s `key`.
337	///
338	/// By default, this is not a supported operation.
339	fn clear_attribute(
340		_collection: &Self::CollectionId,
341		_item: &Self::ItemId,
342		_key: &[u8],
343	) -> DispatchResult {
344		Err(TokenError::Unsupported.into())
345	}
346
347	/// Attempt to clear the strongly-typed attribute of `item` of `collection`'s `key`.
348	///
349	/// By default this just attempts to use `clear_attribute`.
350	fn clear_typed_attribute<K: Encode>(
351		collection: &Self::CollectionId,
352		item: &Self::ItemId,
353		key: &K,
354	) -> DispatchResult {
355		key.using_encoded(|k| Self::clear_attribute(collection, item, k))
356	}
357
358	/// Clear attribute of `collection`'s `key`.
359	///
360	/// By default, this is not a supported operation.
361	fn clear_collection_attribute(_collection: &Self::CollectionId, _key: &[u8]) -> DispatchResult {
362		Err(TokenError::Unsupported.into())
363	}
364
365	/// Attempt to clear the strongly-typed attribute of `collection`'s `key`.
366	///
367	/// By default this just attempts to use `clear_attribute`.
368	fn clear_typed_collection_attribute<K: Encode>(
369		collection: &Self::CollectionId,
370		key: &K,
371	) -> DispatchResult {
372		key.using_encoded(|k| Self::clear_collection_attribute(collection, k))
373	}
374
375	/// Clear the metadata of an `item` of `collection`.
376	///
377	/// By default, this is not a supported operation.
378	fn clear_item_metadata(
379		_who: Option<&AccountId>,
380		_collection: &Self::CollectionId,
381		_item: &Self::ItemId,
382	) -> DispatchResult {
383		Err(TokenError::Unsupported.into())
384	}
385
386	/// Clear the metadata of a `collection`.
387	///
388	/// By default, this is not a supported operation.
389	fn clear_collection_metadata(
390		_who: Option<&AccountId>,
391		_collection: &Self::CollectionId,
392	) -> DispatchResult {
393		Err(TokenError::Unsupported.into())
394	}
395}
396
397/// Trait for transferring non-fungible sets of items.
398pub trait Transfer<AccountId>: Inspect<AccountId> {
399	/// Transfer `item` of `collection` into `destination` account.
400	fn transfer(
401		collection: &Self::CollectionId,
402		item: &Self::ItemId,
403		destination: &AccountId,
404	) -> DispatchResult;
405
406	/// Disable the `item` of `collection` transfer.
407	///
408	/// By default, this is not a supported operation.
409	fn disable_transfer(_collection: &Self::CollectionId, _item: &Self::ItemId) -> DispatchResult {
410		Err(TokenError::Unsupported.into())
411	}
412
413	/// Re-enable the `item` of `collection` transfer.
414	///
415	/// By default, this is not a supported operation.
416	fn enable_transfer(_collection: &Self::CollectionId, _item: &Self::ItemId) -> DispatchResult {
417		Err(TokenError::Unsupported.into())
418	}
419}
420
421/// Trait for trading non-fungible items.
422pub trait Trading<AccountId, ItemPrice>: Inspect<AccountId> {
423	/// Allows `buyer` to buy an `item` of `collection` if it's up for sale with a `bid_price` to
424	/// pay.
425	fn buy_item(
426		collection: &Self::CollectionId,
427		item: &Self::ItemId,
428		buyer: &AccountId,
429		bid_price: &ItemPrice,
430	) -> DispatchResult;
431
432	/// Sets the item price for `item` to make it available for sale.
433	fn set_price(
434		collection: &Self::CollectionId,
435		item: &Self::ItemId,
436		sender: &AccountId,
437		price: Option<ItemPrice>,
438		whitelisted_buyer: Option<AccountId>,
439	) -> DispatchResult;
440
441	/// Returns the item price of `item` or `None` if the item is not for sale.
442	fn item_price(collection: &Self::CollectionId, item: &Self::ItemId) -> Option<ItemPrice>;
443}