referrerpolicy=no-referrer-when-downgrade

pallet_uniques/asset_ops/
item.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
18use core::marker::PhantomData;
19
20use crate::{
21	asset_strategies::{Attribute, WithItemConfig},
22	Item as ItemStorage, *,
23};
24use frame_support::{
25	dispatch::DispatchResult,
26	ensure,
27	traits::tokens::asset_ops::{
28		common_strategies::{
29			Bytes, CanUpdate, ChangeOwnerFrom, CheckOrigin, CheckState, ConfigValue, IfOwnedBy,
30			NoParams, Owner, PredefinedId, WithConfig,
31		},
32		AssetDefinition, Create, Inspect, Restore, Stash, Update,
33	},
34	BoundedSlice,
35};
36use frame_system::ensure_signed;
37use sp_runtime::DispatchError;
38
39pub struct Item<PalletInstance>(PhantomData<PalletInstance>);
40
41impl<T: Config<I>, I: 'static> AssetDefinition for Item<Pallet<T, I>> {
42	type Id = (T::CollectionId, T::ItemId);
43}
44
45impl<T: Config<I>, I: 'static> Inspect<Owner<T::AccountId>> for Item<Pallet<T, I>> {
46	fn inspect(
47		(collection, item): &Self::Id,
48		_ownership: Owner<T::AccountId>,
49	) -> Result<T::AccountId, DispatchError> {
50		ItemStorage::<T, I>::get(collection, item)
51			.map(|a| a.owner)
52			.ok_or(Error::<T, I>::UnknownItem.into())
53	}
54}
55
56impl<T: Config<I>, I: 'static> Inspect<Bytes> for Item<Pallet<T, I>> {
57	fn inspect((collection, item): &Self::Id, _bytes: Bytes) -> Result<Vec<u8>, DispatchError> {
58		ItemMetadataOf::<T, I>::get(collection, item)
59			.map(|m| m.data.into())
60			.ok_or(Error::<T, I>::NoMetadata.into())
61	}
62}
63
64impl<'a, T: Config<I>, I: 'static> Inspect<Bytes<Attribute<'a>>> for Item<Pallet<T, I>> {
65	fn inspect(
66		(collection, item): &Self::Id,
67		strategy: Bytes<Attribute>,
68	) -> Result<Vec<u8>, DispatchError> {
69		let Bytes(Attribute(attribute)) = strategy;
70
71		let attribute =
72			BoundedSlice::try_from(attribute).map_err(|_| Error::<T, I>::WrongAttribute)?;
73		crate::Attribute::<T, I>::get((collection, Some(item), attribute))
74			.map(|a| a.0.into())
75			.ok_or(Error::<T, I>::AttributeNotFound.into())
76	}
77}
78
79impl<T: Config<I>, I: 'static> Inspect<CanUpdate<Owner<T::AccountId>>> for Item<Pallet<T, I>> {
80	fn inspect(
81		(collection, item): &Self::Id,
82		_can_update: CanUpdate<Owner<T::AccountId>>,
83	) -> Result<bool, DispatchError> {
84		match (Collection::<T, I>::get(collection), ItemStorage::<T, I>::get(collection, item)) {
85			(Some(cd), Some(id)) => Ok(!cd.is_frozen && !id.is_frozen),
86			_ => Err(Error::<T, I>::UnknownItem.into()),
87		}
88	}
89}
90
91impl<T: Config<I>, I: 'static> Create<WithItemConfig<T, I>> for Item<Pallet<T, I>> {
92	fn create(
93		strategy: WithItemConfig<T, I>,
94	) -> Result<(T::CollectionId, T::ItemId), DispatchError> {
95		let WithConfig { config: ConfigValue::<_>(owner), extra: id_assignment } = strategy;
96		let (collection, item) = id_assignment.params;
97
98		<Pallet<T, I>>::do_mint(collection.clone(), item, owner, |_| Ok(()))?;
99
100		Ok((collection, item))
101	}
102}
103
104impl<T: Config<I>, I: 'static> Create<CheckOrigin<T::RuntimeOrigin, WithItemConfig<T, I>>>
105	for Item<Pallet<T, I>>
106{
107	fn create(
108		strategy: CheckOrigin<T::RuntimeOrigin, WithItemConfig<T, I>>,
109	) -> Result<(T::CollectionId, T::ItemId), DispatchError> {
110		let CheckOrigin(
111			origin,
112			WithConfig { config: ConfigValue::<_>(owner), extra: id_assignment },
113		) = strategy;
114		let (collection, item) = id_assignment.params;
115
116		let signer = ensure_signed(origin)?;
117
118		<Pallet<T, I>>::do_mint(collection.clone(), item, owner, |collection_details| {
119			ensure!(collection_details.issuer == signer, Error::<T, I>::NoPermission);
120			Ok(())
121		})?;
122
123		Ok((collection, item))
124	}
125}
126
127impl<T: Config<I>, I: 'static> Update<Owner<T::AccountId>> for Item<Pallet<T, I>> {
128	fn update(
129		(collection, item): &Self::Id,
130		_strategy: Owner<T::AccountId>,
131		dest: &T::AccountId,
132	) -> DispatchResult {
133		<Pallet<T, I>>::do_transfer(collection.clone(), *item, dest.clone(), |_, _| Ok(()))
134	}
135}
136
137impl<T: Config<I>, I: 'static> Update<CheckOrigin<T::RuntimeOrigin, Owner<T::AccountId>>>
138	for Item<Pallet<T, I>>
139{
140	fn update(
141		(collection, item): &Self::Id,
142		strategy: CheckOrigin<T::RuntimeOrigin, Owner<T::AccountId>>,
143		dest: &T::AccountId,
144	) -> DispatchResult {
145		let CheckOrigin(origin, ..) = strategy;
146
147		let signer = ensure_signed(origin)?;
148
149		<Pallet<T, I>>::do_transfer(
150			collection.clone(),
151			*item,
152			dest.clone(),
153			|collection_details, details| {
154				if details.owner != signer && collection_details.admin != signer {
155					let approved = details.approved.take().map_or(false, |i| i == signer);
156					ensure!(approved, Error::<T, I>::NoPermission);
157				}
158				Ok(())
159			},
160		)
161	}
162}
163
164impl<T: Config<I>, I: 'static> Update<ChangeOwnerFrom<T::AccountId>> for Item<Pallet<T, I>> {
165	fn update(
166		(collection, item): &Self::Id,
167		strategy: ChangeOwnerFrom<T::AccountId>,
168		dest: &T::AccountId,
169	) -> DispatchResult {
170		let CheckState(from, ..) = strategy;
171
172		<Pallet<T, I>>::do_transfer(collection.clone(), *item, dest.clone(), |_, details| {
173			ensure!(details.owner == from, Error::<T, I>::WrongOwner);
174			Ok(())
175		})
176	}
177}
178
179impl<T: Config<I>, I: 'static> Stash<NoParams> for Item<Pallet<T, I>> {
180	fn stash((collection, item): &Self::Id, _strategy: NoParams) -> DispatchResult {
181		<Pallet<T, I>>::do_burn(collection.clone(), *item, |_, _| Ok(()))
182	}
183}
184
185impl<T: Config<I>, I: 'static> Stash<CheckOrigin<T::RuntimeOrigin>> for Item<Pallet<T, I>> {
186	fn stash(
187		(collection, item): &Self::Id,
188		strategy: CheckOrigin<T::RuntimeOrigin>,
189	) -> DispatchResult {
190		let CheckOrigin(origin, ..) = strategy;
191
192		let signer = ensure_signed(origin)?;
193
194		<Pallet<T, I>>::do_burn(collection.clone(), *item, |collection_details, details| {
195			let is_permitted = collection_details.admin == signer || details.owner == signer;
196			ensure!(is_permitted, Error::<T, I>::NoPermission);
197			Ok(())
198		})
199	}
200}
201
202impl<T: Config<I>, I: 'static> Stash<IfOwnedBy<T::AccountId>> for Item<Pallet<T, I>> {
203	fn stash((collection, item): &Self::Id, strategy: IfOwnedBy<T::AccountId>) -> DispatchResult {
204		let CheckState(who, ..) = strategy;
205
206		<Pallet<T, I>>::do_burn(collection.clone(), *item, |_, d| {
207			ensure!(d.owner == who, Error::<T, I>::NoPermission);
208			Ok(())
209		})
210	}
211}
212
213// NOTE: pallet-uniques create and restore operations are equivalent.
214// If an NFT was burned, it can be "re-created" (equivalently, "restored").
215// It will be "re-created" with all the data still bound to it.
216// If an NFT is minted for the first time, it can be regarded as "restored" with an empty data
217// because it is indistinguishable from a burned empty NFT from the chain's perspective.
218impl<T: Config<I>, I: 'static> Restore<WithConfig<ConfigValue<Owner<T::AccountId>>>>
219	for Item<Pallet<T, I>>
220{
221	fn restore(
222		(collection, item): &Self::Id,
223		strategy: WithConfig<ConfigValue<Owner<T::AccountId>>>,
224	) -> DispatchResult {
225		Self::create(WithConfig::new(
226			strategy.config,
227			PredefinedId::from((collection.clone(), *item)),
228		))?;
229
230		Ok(())
231	}
232}