referrerpolicy=no-referrer-when-downgrade

frame_support/traits/tokens/
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//! 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;
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;
39
40	/// Type for identifying a collection (an identifier for an independent collection of
41	/// items).
42	type CollectionId;
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 strongly-typed attribute value of `item` of `collection` corresponding to
67	/// `key`.
68	///
69	/// By default this just attempts to use `attribute`.
70	fn typed_attribute<K: Encode, V: Decode>(
71		collection: &Self::CollectionId,
72		item: &Self::ItemId,
73		key: &K,
74	) -> Option<V> {
75		key.using_encoded(|d| Self::attribute(collection, item, d))
76			.and_then(|v| V::decode(&mut &v[..]).ok())
77	}
78
79	/// Returns the attribute value of `collection` corresponding to `key`.
80	///
81	/// By default this is `None`; no attributes are defined.
82	fn collection_attribute(_collection: &Self::CollectionId, _key: &[u8]) -> Option<Vec<u8>> {
83		None
84	}
85
86	/// Returns the strongly-typed attribute value of `collection` corresponding to `key`.
87	///
88	/// By default this just attempts to use `collection_attribute`.
89	fn typed_collection_attribute<K: Encode, V: Decode>(
90		collection: &Self::CollectionId,
91		key: &K,
92	) -> Option<V> {
93		key.using_encoded(|d| Self::collection_attribute(collection, d))
94			.and_then(|v| V::decode(&mut &v[..]).ok())
95	}
96
97	/// Returns `true` if the `item` of `collection` may be transferred.
98	///
99	/// Default implementation is that all items are transferable.
100	fn can_transfer(_collection: &Self::CollectionId, _item: &Self::ItemId) -> bool {
101		true
102	}
103}
104
105/// Interface for enumerating items in existence or owned by a given account over many collections
106/// of NFTs.
107pub trait InspectEnumerable<AccountId>: Inspect<AccountId> {
108	/// The iterator type for [`Self::collections`].
109	type CollectionsIterator: Iterator<Item = Self::CollectionId>;
110	/// The iterator type for [`Self::items`].
111	type ItemsIterator: Iterator<Item = Self::ItemId>;
112	/// The iterator type for [`Self::owned`].
113	type OwnedIterator: Iterator<Item = (Self::CollectionId, Self::ItemId)>;
114	/// The iterator type for [`Self::owned_in_collection`].
115	type OwnedInCollectionIterator: Iterator<Item = Self::ItemId>;
116
117	/// Returns an iterator of the collections in existence.
118	fn collections() -> Self::CollectionsIterator;
119
120	/// Returns an iterator of the items of a `collection` in existence.
121	fn items(collection: &Self::CollectionId) -> Self::ItemsIterator;
122
123	/// Returns an iterator of the items of all collections owned by `who`.
124	fn owned(who: &AccountId) -> Self::OwnedIterator;
125
126	/// Returns an iterator of the items of `collection` owned by `who`.
127	fn owned_in_collection(
128		collection: &Self::CollectionId,
129		who: &AccountId,
130	) -> Self::OwnedInCollectionIterator;
131}
132
133/// Trait for providing the ability to create collections of nonfungible items.
134pub trait Create<AccountId>: Inspect<AccountId> {
135	/// Create a `collection` of nonfungible items to be owned by `who` and managed by `admin`.
136	fn create_collection(
137		collection: &Self::CollectionId,
138		who: &AccountId,
139		admin: &AccountId,
140	) -> DispatchResult;
141}
142
143/// Trait for providing the ability to destroy collections of nonfungible items.
144pub trait Destroy<AccountId>: Inspect<AccountId> {
145	/// The witness data needed to destroy an item.
146	type DestroyWitness;
147
148	/// Provide the appropriate witness data needed to destroy an item.
149	fn get_destroy_witness(collection: &Self::CollectionId) -> Option<Self::DestroyWitness>;
150
151	/// Destroy an existing fungible item.
152	/// * `collection`: The `CollectionId` to be destroyed.
153	/// * `witness`: Any witness data that needs to be provided to complete the operation
154	///   successfully.
155	/// * `maybe_check_owner`: An optional account id that can be used to authorize the destroy
156	///   command. If not provided, we will not do any authorization checks before destroying the
157	///   item.
158	///
159	/// If successful, this function will return the actual witness data from the destroyed item.
160	/// This may be different than the witness data provided, and can be used to refund weight.
161	fn destroy(
162		collection: Self::CollectionId,
163		witness: Self::DestroyWitness,
164		maybe_check_owner: Option<AccountId>,
165	) -> Result<Self::DestroyWitness, DispatchError>;
166}
167
168/// Trait for providing an interface for multiple collections of NFT-like items which may be
169/// minted, burned and/or have attributes set on them.
170pub trait Mutate<AccountId>: Inspect<AccountId> {
171	/// Mint some `item` of `collection` to be owned by `who`.
172	///
173	/// By default, this is not a supported operation.
174	fn mint_into(
175		_collection: &Self::CollectionId,
176		_item: &Self::ItemId,
177		_who: &AccountId,
178	) -> DispatchResult {
179		Err(TokenError::Unsupported.into())
180	}
181
182	/// Burn some `item` of `collection`.
183	///
184	/// By default, this is not a supported operation.
185	fn burn(
186		_collection: &Self::CollectionId,
187		_item: &Self::ItemId,
188		_maybe_check_owner: Option<&AccountId>,
189	) -> DispatchResult {
190		Err(TokenError::Unsupported.into())
191	}
192
193	/// Set attribute `value` of `item` of `collection`'s `key`.
194	///
195	/// By default, this is not a supported operation.
196	fn set_attribute(
197		_collection: &Self::CollectionId,
198		_item: &Self::ItemId,
199		_key: &[u8],
200		_value: &[u8],
201	) -> DispatchResult {
202		Err(TokenError::Unsupported.into())
203	}
204
205	/// Attempt to set the strongly-typed attribute `value` of `item` of `collection`'s `key`.
206	///
207	/// By default this just attempts to use `set_attribute`.
208	fn set_typed_attribute<K: Encode, V: Encode>(
209		collection: &Self::CollectionId,
210		item: &Self::ItemId,
211		key: &K,
212		value: &V,
213	) -> DispatchResult {
214		key.using_encoded(|k| value.using_encoded(|v| Self::set_attribute(collection, item, k, v)))
215	}
216
217	/// Set attribute `value` of `collection`'s `key`.
218	///
219	/// By default, this is not a supported operation.
220	fn set_collection_attribute(
221		_collection: &Self::CollectionId,
222		_key: &[u8],
223		_value: &[u8],
224	) -> DispatchResult {
225		Err(TokenError::Unsupported.into())
226	}
227
228	/// Attempt to set the strongly-typed attribute `value` of `collection`'s `key`.
229	///
230	/// By default this just attempts to use `set_attribute`.
231	fn set_typed_collection_attribute<K: Encode, V: Encode>(
232		collection: &Self::CollectionId,
233		key: &K,
234		value: &V,
235	) -> DispatchResult {
236		key.using_encoded(|k| {
237			value.using_encoded(|v| Self::set_collection_attribute(collection, k, v))
238		})
239	}
240}
241
242/// Trait for providing a non-fungible sets of items which can only be transferred.
243pub trait Transfer<AccountId>: Inspect<AccountId> {
244	/// Transfer `item` of `collection` into `destination` account.
245	fn transfer(
246		collection: &Self::CollectionId,
247		item: &Self::ItemId,
248		destination: &AccountId,
249	) -> DispatchResult;
250}