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}