referrerpolicy=no-referrer-when-downgrade

frame_support/traits/tokens/
nonfungible.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 collection of items.
19//!
20//! This assumes a single level namespace identified by `Inspect::ItemId`, and could
21//! reasonably be implemented by pallets which wants to expose a single collection of NFT-like
22//! objects.
23//!
24//! For an NFT API which has dual-level namespacing, the traits in `nonfungibles` are better to
25//! use.
26
27use super::nonfungibles;
28use crate::{dispatch::DispatchResult, traits::Get};
29use alloc::vec::Vec;
30use codec::{Decode, Encode};
31use sp_runtime::TokenError;
32
33/// Trait for providing an interface to a read-only NFT-like set of items.
34pub trait Inspect<AccountId> {
35	/// Type for identifying an item.
36	type ItemId;
37
38	/// Returns the owner of `item`, or `None` if the item doesn't exist or has no
39	/// owner.
40	fn owner(item: &Self::ItemId) -> Option<AccountId>;
41
42	/// Returns the attribute value of `item` corresponding to `key`.
43	///
44	/// By default this is `None`; no attributes are defined.
45	fn attribute(_item: &Self::ItemId, _key: &[u8]) -> Option<Vec<u8>> {
46		None
47	}
48
49	/// Returns the strongly-typed attribute value of `item` corresponding to `key`.
50	///
51	/// By default this just attempts to use `attribute`.
52	fn typed_attribute<K: Encode, V: Decode>(item: &Self::ItemId, key: &K) -> Option<V> {
53		key.using_encoded(|d| Self::attribute(item, d))
54			.and_then(|v| V::decode(&mut &v[..]).ok())
55	}
56
57	/// Returns `true` if the `item` may be transferred.
58	///
59	/// Default implementation is that all items are transferable.
60	fn can_transfer(_item: &Self::ItemId) -> bool {
61		true
62	}
63}
64
65/// Interface for enumerating items in existence or owned by a given account over a collection
66/// of NFTs.
67pub trait InspectEnumerable<AccountId>: Inspect<AccountId> {
68	/// The iterator type for [`Self::items`].
69	type ItemsIterator: Iterator<Item = Self::ItemId>;
70	/// The iterator type for [`Self::owned`].
71	type OwnedIterator: Iterator<Item = Self::ItemId>;
72
73	/// Returns an iterator of the items within a `collection` in existence.
74	fn items() -> Self::ItemsIterator;
75
76	/// Returns an iterator of the items of all collections owned by `who`.
77	fn owned(who: &AccountId) -> Self::OwnedIterator;
78}
79
80/// Trait for providing an interface for NFT-like items which may be minted, burned and/or have
81/// attributes set on them.
82pub trait Mutate<AccountId>: Inspect<AccountId> {
83	/// Mint some `item` to be owned by `who`.
84	///
85	/// By default, this is not a supported operation.
86	fn mint_into(_item: &Self::ItemId, _who: &AccountId) -> DispatchResult {
87		Err(TokenError::Unsupported.into())
88	}
89
90	/// Burn some `item`.
91	///
92	/// By default, this is not a supported operation.
93	fn burn(_item: &Self::ItemId, _maybe_check_owner: Option<&AccountId>) -> DispatchResult {
94		Err(TokenError::Unsupported.into())
95	}
96
97	/// Set attribute `value` of `item`'s `key`.
98	///
99	/// By default, this is not a supported operation.
100	fn set_attribute(_item: &Self::ItemId, _key: &[u8], _value: &[u8]) -> DispatchResult {
101		Err(TokenError::Unsupported.into())
102	}
103
104	/// Attempt to set the strongly-typed attribute `value` of `item`'s `key`.
105	///
106	/// By default this just attempts to use `set_attribute`.
107	fn set_typed_attribute<K: Encode, V: Encode>(
108		item: &Self::ItemId,
109		key: &K,
110		value: &V,
111	) -> DispatchResult {
112		key.using_encoded(|k| value.using_encoded(|v| Self::set_attribute(item, k, v)))
113	}
114}
115
116/// Trait for providing a non-fungible set of items which can only be transferred.
117pub trait Transfer<AccountId>: Inspect<AccountId> {
118	/// Transfer `item` into `destination` account.
119	fn transfer(item: &Self::ItemId, destination: &AccountId) -> DispatchResult;
120}
121
122/// Convert a `fungibles` trait implementation into a `fungible` trait implementation by identifying
123/// a single item.
124pub struct ItemOf<
125	F: nonfungibles::Inspect<AccountId>,
126	A: Get<<F as nonfungibles::Inspect<AccountId>>::CollectionId>,
127	AccountId,
128>(core::marker::PhantomData<(F, A, AccountId)>);
129
130impl<
131		F: nonfungibles::Inspect<AccountId>,
132		A: Get<<F as nonfungibles::Inspect<AccountId>>::CollectionId>,
133		AccountId,
134	> Inspect<AccountId> for ItemOf<F, A, AccountId>
135{
136	type ItemId = <F as nonfungibles::Inspect<AccountId>>::ItemId;
137	fn owner(item: &Self::ItemId) -> Option<AccountId> {
138		<F as nonfungibles::Inspect<AccountId>>::owner(&A::get(), item)
139	}
140	fn attribute(item: &Self::ItemId, key: &[u8]) -> Option<Vec<u8>> {
141		<F as nonfungibles::Inspect<AccountId>>::attribute(&A::get(), item, key)
142	}
143	fn typed_attribute<K: Encode, V: Decode>(item: &Self::ItemId, key: &K) -> Option<V> {
144		<F as nonfungibles::Inspect<AccountId>>::typed_attribute(&A::get(), item, key)
145	}
146	fn can_transfer(item: &Self::ItemId) -> bool {
147		<F as nonfungibles::Inspect<AccountId>>::can_transfer(&A::get(), item)
148	}
149}
150
151impl<
152		F: nonfungibles::InspectEnumerable<AccountId>,
153		A: Get<<F as nonfungibles::Inspect<AccountId>>::CollectionId>,
154		AccountId,
155	> InspectEnumerable<AccountId> for ItemOf<F, A, AccountId>
156{
157	type ItemsIterator = <F as nonfungibles::InspectEnumerable<AccountId>>::ItemsIterator;
158	type OwnedIterator =
159		<F as nonfungibles::InspectEnumerable<AccountId>>::OwnedInCollectionIterator;
160
161	fn items() -> Self::ItemsIterator {
162		<F as nonfungibles::InspectEnumerable<AccountId>>::items(&A::get())
163	}
164
165	fn owned(who: &AccountId) -> Self::OwnedIterator {
166		<F as nonfungibles::InspectEnumerable<AccountId>>::owned_in_collection(&A::get(), who)
167	}
168}
169
170impl<
171		F: nonfungibles::Mutate<AccountId>,
172		A: Get<<F as nonfungibles::Inspect<AccountId>>::CollectionId>,
173		AccountId,
174	> Mutate<AccountId> for ItemOf<F, A, AccountId>
175{
176	fn mint_into(item: &Self::ItemId, who: &AccountId) -> DispatchResult {
177		<F as nonfungibles::Mutate<AccountId>>::mint_into(&A::get(), item, who)
178	}
179	fn burn(item: &Self::ItemId, maybe_check_owner: Option<&AccountId>) -> DispatchResult {
180		<F as nonfungibles::Mutate<AccountId>>::burn(&A::get(), item, maybe_check_owner)
181	}
182	fn set_attribute(item: &Self::ItemId, key: &[u8], value: &[u8]) -> DispatchResult {
183		<F as nonfungibles::Mutate<AccountId>>::set_attribute(&A::get(), item, key, value)
184	}
185	fn set_typed_attribute<K: Encode, V: Encode>(
186		item: &Self::ItemId,
187		key: &K,
188		value: &V,
189	) -> DispatchResult {
190		<F as nonfungibles::Mutate<AccountId>>::set_typed_attribute(&A::get(), item, key, value)
191	}
192}
193
194impl<
195		F: nonfungibles::Transfer<AccountId>,
196		A: Get<<F as nonfungibles::Inspect<AccountId>>::CollectionId>,
197		AccountId,
198	> Transfer<AccountId> for ItemOf<F, A, AccountId>
199{
200	fn transfer(item: &Self::ItemId, destination: &AccountId) -> DispatchResult {
201		<F as nonfungibles::Transfer<AccountId>>::transfer(&A::get(), item, destination)
202	}
203}