referrerpolicy=no-referrer-when-downgrade

frame_support/traits/tokens/
nonfungible_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 a single non-fungible item.
19//!
20//! This assumes a single-level namespace identified by `Inspect::ItemId`, and could
21//! reasonably be implemented by pallets that want to expose a single collection of NFT-like
22//! objects.
23//!
24//! For an NFT API that has dual-level namespacing, the traits in `nonfungibles` are better to
25//! use.
26
27use super::nonfungibles_v2 as nonfungibles;
28use crate::{
29	dispatch::{DispatchResult, Parameter},
30	traits::Get,
31};
32use alloc::vec::Vec;
33use codec::{Decode, Encode};
34use sp_runtime::TokenError;
35
36/// Trait for providing an interface to a read-only NFT-like item.
37pub trait Inspect<AccountId> {
38	/// Type for identifying an item.
39	type ItemId: Parameter;
40
41	/// Returns the owner of `item`, or `None` if the item doesn't exist or has no
42	/// owner.
43	fn owner(item: &Self::ItemId) -> Option<AccountId>;
44
45	/// Returns the attribute value of `item` corresponding to `key`.
46	///
47	/// By default this is `None`; no attributes are defined.
48	fn attribute(_item: &Self::ItemId, _key: &[u8]) -> Option<Vec<u8>> {
49		None
50	}
51
52	/// Returns the custom attribute value of `item` corresponding to `key`.
53	///
54	/// By default this is `None`; no attributes are defined.
55	fn custom_attribute(
56		_account: &AccountId,
57		_item: &Self::ItemId,
58		_key: &[u8],
59	) -> Option<Vec<u8>> {
60		None
61	}
62
63	/// Returns the system attribute value of `item` corresponding to `key`.
64	///
65	/// By default this is `None`; no attributes are defined.
66	fn system_attribute(_item: &Self::ItemId, _key: &[u8]) -> Option<Vec<u8>> {
67		None
68	}
69
70	/// Returns the strongly-typed attribute value of `item` corresponding to `key`.
71	///
72	/// By default this just attempts to use `attribute`.
73	fn typed_attribute<K: Encode, V: Decode>(item: &Self::ItemId, key: &K) -> Option<V> {
74		key.using_encoded(|d| Self::attribute(item, d))
75			.and_then(|v| V::decode(&mut &v[..]).ok())
76	}
77
78	/// Returns the strongly-typed custom attribute value of `item` corresponding to `key`.
79	///
80	/// By default this just attempts to use `custom_attribute`.
81	fn typed_custom_attribute<K: Encode, V: Decode>(
82		account: &AccountId,
83		item: &Self::ItemId,
84		key: &K,
85	) -> Option<V> {
86		key.using_encoded(|d| Self::custom_attribute(account, item, d))
87			.and_then(|v| V::decode(&mut &v[..]).ok())
88	}
89
90	/// Returns the strongly-typed system attribute value of `item` corresponding to `key`.
91	///
92	/// By default this just attempts to use `system_attribute`.
93	fn typed_system_attribute<K: Encode, V: Decode>(item: &Self::ItemId, key: &K) -> Option<V> {
94		key.using_encoded(|d| Self::system_attribute(item, d))
95			.and_then(|v| V::decode(&mut &v[..]).ok())
96	}
97
98	/// Returns `true` if the `item` may be transferred.
99	///
100	/// Default implementation is that all items are transferable.
101	fn can_transfer(_item: &Self::ItemId) -> bool {
102		true
103	}
104}
105
106/// Interface for enumerating items in existence or owned by a given account over a collection
107/// of NFTs.
108pub trait InspectEnumerable<AccountId>: Inspect<AccountId> {
109	/// The iterator type for [`Self::items`].
110	type ItemsIterator: Iterator<Item = Self::ItemId>;
111	/// The iterator type for [`Self::owned`].
112	type OwnedIterator: Iterator<Item = Self::ItemId>;
113
114	/// Returns an iterator of the items within a `collection` in existence.
115	fn items() -> Self::ItemsIterator;
116
117	/// Returns an iterator of the items of all collections owned by `who`.
118	fn owned(who: &AccountId) -> Self::OwnedIterator;
119}
120
121/// Trait for providing an interface for NFT-like items which may be minted, burned and/or have
122/// attributes and metadata set on them.
123pub trait Mutate<AccountId, ItemConfig>: Inspect<AccountId> {
124	/// Mint some `item` to be owned by `who`.
125	///
126	/// By default, this is not a supported operation.
127	fn mint_into(
128		_item: &Self::ItemId,
129		_who: &AccountId,
130		_config: &ItemConfig,
131		_deposit_collection_owner: bool,
132	) -> DispatchResult {
133		Err(TokenError::Unsupported.into())
134	}
135
136	/// Burn some `item`.
137	///
138	/// By default, this is not a supported operation.
139	fn burn(_item: &Self::ItemId, _maybe_check_owner: Option<&AccountId>) -> DispatchResult {
140		Err(TokenError::Unsupported.into())
141	}
142
143	/// Set attribute `value` of `item`'s `key`.
144	///
145	/// By default, this is not a supported operation.
146	fn set_attribute(_item: &Self::ItemId, _key: &[u8], _value: &[u8]) -> DispatchResult {
147		Err(TokenError::Unsupported.into())
148	}
149
150	/// Attempt to set the strongly-typed attribute `value` of `item`'s `key`.
151	///
152	/// By default this just attempts to use `set_attribute`.
153	fn set_typed_attribute<K: Encode, V: Encode>(
154		item: &Self::ItemId,
155		key: &K,
156		value: &V,
157	) -> DispatchResult {
158		key.using_encoded(|k| value.using_encoded(|v| Self::set_attribute(item, k, v)))
159	}
160
161	/// Set the metadata `data` of an `item`.
162	///
163	/// By default, this is not a supported operation.
164	fn set_metadata(_who: &AccountId, _item: &Self::ItemId, _data: &[u8]) -> DispatchResult {
165		Err(TokenError::Unsupported.into())
166	}
167
168	/// Clear attribute of `item`'s `key`.
169	///
170	/// By default, this is not a supported operation.
171	fn clear_attribute(_item: &Self::ItemId, _key: &[u8]) -> DispatchResult {
172		Err(TokenError::Unsupported.into())
173	}
174
175	/// Attempt to clear the strongly-typed attribute of `item`'s `key`.
176	///
177	/// By default this just attempts to use `clear_attribute`.
178	fn clear_typed_attribute<K: Encode>(item: &Self::ItemId, key: &K) -> DispatchResult {
179		key.using_encoded(|k| Self::clear_attribute(item, k))
180	}
181
182	/// Clear the metadata of an `item`.
183	///
184	/// By default, this is not a supported operation.
185	fn clear_metadata(_who: &AccountId, _item: &Self::ItemId) -> DispatchResult {
186		Err(TokenError::Unsupported.into())
187	}
188}
189
190/// Trait for transferring and controlling the transfer of non-fungible sets of items.
191pub trait Transfer<AccountId>: Inspect<AccountId> {
192	/// Transfer `item` into `destination` account.
193	fn transfer(item: &Self::ItemId, destination: &AccountId) -> DispatchResult;
194	/// Disable the `item` of `collection` transfer.
195	///
196	/// By default, this is not a supported operation.
197	fn disable_transfer(item: &Self::ItemId) -> DispatchResult;
198	/// Re-enable the `item` of `collection` transfer.
199	///
200	/// By default, this is not a supported operation.
201	fn enable_transfer(item: &Self::ItemId) -> DispatchResult;
202}
203
204/// Convert a `nonfungibles` trait implementation into a `nonfungible` trait implementation by
205/// identifying a single item.
206pub struct ItemOf<
207	F: nonfungibles::Inspect<AccountId>,
208	A: Get<<F as nonfungibles::Inspect<AccountId>>::CollectionId>,
209	AccountId,
210>(core::marker::PhantomData<(F, A, AccountId)>);
211
212impl<
213		F: nonfungibles::Inspect<AccountId>,
214		A: Get<<F as nonfungibles::Inspect<AccountId>>::CollectionId>,
215		AccountId,
216	> Inspect<AccountId> for ItemOf<F, A, AccountId>
217{
218	type ItemId = <F as nonfungibles::Inspect<AccountId>>::ItemId;
219	fn owner(item: &Self::ItemId) -> Option<AccountId> {
220		<F as nonfungibles::Inspect<AccountId>>::owner(&A::get(), item)
221	}
222	fn attribute(item: &Self::ItemId, key: &[u8]) -> Option<Vec<u8>> {
223		<F as nonfungibles::Inspect<AccountId>>::attribute(&A::get(), item, key)
224	}
225	fn custom_attribute(account: &AccountId, item: &Self::ItemId, key: &[u8]) -> Option<Vec<u8>> {
226		<F as nonfungibles::Inspect<AccountId>>::custom_attribute(account, &A::get(), item, key)
227	}
228	fn system_attribute(item: &Self::ItemId, key: &[u8]) -> Option<Vec<u8>> {
229		<F as nonfungibles::Inspect<AccountId>>::system_attribute(&A::get(), Some(item), key)
230	}
231	fn typed_attribute<K: Encode, V: Decode>(item: &Self::ItemId, key: &K) -> Option<V> {
232		<F as nonfungibles::Inspect<AccountId>>::typed_attribute(&A::get(), item, key)
233	}
234	fn typed_custom_attribute<K: Encode, V: Decode>(
235		account: &AccountId,
236		item: &Self::ItemId,
237		key: &K,
238	) -> Option<V> {
239		<F as nonfungibles::Inspect<AccountId>>::typed_custom_attribute(
240			account,
241			&A::get(),
242			item,
243			key,
244		)
245	}
246	fn typed_system_attribute<K: Encode, V: Decode>(item: &Self::ItemId, key: &K) -> Option<V> {
247		<F as nonfungibles::Inspect<AccountId>>::typed_system_attribute(&A::get(), Some(item), key)
248	}
249	fn can_transfer(item: &Self::ItemId) -> bool {
250		<F as nonfungibles::Inspect<AccountId>>::can_transfer(&A::get(), item)
251	}
252}
253
254impl<
255		F: nonfungibles::InspectEnumerable<AccountId>,
256		A: Get<<F as nonfungibles::Inspect<AccountId>>::CollectionId>,
257		AccountId,
258	> InspectEnumerable<AccountId> for ItemOf<F, A, AccountId>
259{
260	type ItemsIterator = <F as nonfungibles::InspectEnumerable<AccountId>>::ItemsIterator;
261	type OwnedIterator =
262		<F as nonfungibles::InspectEnumerable<AccountId>>::OwnedInCollectionIterator;
263
264	fn items() -> Self::ItemsIterator {
265		<F as nonfungibles::InspectEnumerable<AccountId>>::items(&A::get())
266	}
267	fn owned(who: &AccountId) -> Self::OwnedIterator {
268		<F as nonfungibles::InspectEnumerable<AccountId>>::owned_in_collection(&A::get(), who)
269	}
270}
271
272impl<
273		F: nonfungibles::Mutate<AccountId, ItemConfig>,
274		A: Get<<F as nonfungibles::Inspect<AccountId>>::CollectionId>,
275		AccountId,
276		ItemConfig,
277	> Mutate<AccountId, ItemConfig> for ItemOf<F, A, AccountId>
278{
279	fn mint_into(
280		item: &Self::ItemId,
281		who: &AccountId,
282		config: &ItemConfig,
283		deposit_collection_owner: bool,
284	) -> DispatchResult {
285		<F as nonfungibles::Mutate<AccountId, ItemConfig>>::mint_into(
286			&A::get(),
287			item,
288			who,
289			config,
290			deposit_collection_owner,
291		)
292	}
293	fn burn(item: &Self::ItemId, maybe_check_owner: Option<&AccountId>) -> DispatchResult {
294		<F as nonfungibles::Mutate<AccountId, ItemConfig>>::burn(&A::get(), item, maybe_check_owner)
295	}
296	fn set_attribute(item: &Self::ItemId, key: &[u8], value: &[u8]) -> DispatchResult {
297		<F as nonfungibles::Mutate<AccountId, ItemConfig>>::set_attribute(
298			&A::get(),
299			item,
300			key,
301			value,
302		)
303	}
304	fn set_typed_attribute<K: Encode, V: Encode>(
305		item: &Self::ItemId,
306		key: &K,
307		value: &V,
308	) -> DispatchResult {
309		<F as nonfungibles::Mutate<AccountId, ItemConfig>>::set_typed_attribute(
310			&A::get(),
311			item,
312			key,
313			value,
314		)
315	}
316	fn clear_attribute(item: &Self::ItemId, key: &[u8]) -> DispatchResult {
317		<F as nonfungibles::Mutate<AccountId, ItemConfig>>::clear_attribute(&A::get(), item, key)
318	}
319	fn clear_typed_attribute<K: Encode>(item: &Self::ItemId, key: &K) -> DispatchResult {
320		<F as nonfungibles::Mutate<AccountId, ItemConfig>>::clear_typed_attribute(
321			&A::get(),
322			item,
323			key,
324		)
325	}
326}
327
328impl<
329		F: nonfungibles::Transfer<AccountId>,
330		A: Get<<F as nonfungibles::Inspect<AccountId>>::CollectionId>,
331		AccountId,
332	> Transfer<AccountId> for ItemOf<F, A, AccountId>
333{
334	fn transfer(item: &Self::ItemId, destination: &AccountId) -> DispatchResult {
335		<F as nonfungibles::Transfer<AccountId>>::transfer(&A::get(), item, destination)
336	}
337	fn disable_transfer(item: &Self::ItemId) -> DispatchResult {
338		<F as nonfungibles::Transfer<AccountId>>::disable_transfer(&A::get(), item)
339	}
340	fn enable_transfer(item: &Self::ItemId) -> DispatchResult {
341		<F as nonfungibles::Transfer<AccountId>>::enable_transfer(&A::get(), item)
342	}
343}