referrerpolicy=no-referrer-when-downgrade

pallet_nfts/features/
approvals.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//! This module contains helper functions for the approval logic implemented in the NFTs pallet.
19//! The bitflag [`PalletFeature::Approvals`] needs to be set in [`Config::Features`] for NFTs
20//! to have the functionality defined in this module.
21
22use crate::*;
23use frame_support::pallet_prelude::*;
24
25impl<T: Config<I>, I: 'static> Pallet<T, I> {
26	/// Approves the transfer of an item to a delegate.
27	///
28	/// This function is used to approve the transfer of the specified `item` in the `collection` to
29	/// a `delegate`. If `maybe_check_origin` is specified, the function ensures that the
30	/// `check_origin` account is the owner of the item, granting them permission to approve the
31	/// transfer. The `delegate` is the account that will be allowed to take control of the item.
32	/// Optionally, a `deadline` can be specified to set a time limit for the approval. The
33	/// `deadline` is expressed in block numbers and is added to the current block number to
34	/// determine the absolute deadline for the approval. After approving the transfer, the function
35	/// emits the `TransferApproved` event.
36	///
37	/// - `maybe_check_origin`: The optional account that is required to be the owner of the item,
38	///   granting permission to approve the transfer. If `None`, no permission check is performed.
39	/// - `collection`: The identifier of the collection containing the item to be transferred.
40	/// - `item`: The identifier of the item to be transferred.
41	/// - `delegate`: The account that will be allowed to take control of the item.
42	/// - `maybe_deadline`: The optional deadline (in block numbers) specifying the time limit for
43	///   the approval.
44	pub(crate) fn do_approve_transfer(
45		maybe_check_origin: Option<T::AccountId>,
46		collection: T::CollectionId,
47		item: T::ItemId,
48		delegate: T::AccountId,
49		maybe_deadline: Option<BlockNumberFor<T, I>>,
50	) -> DispatchResult {
51		ensure!(
52			Self::is_pallet_feature_enabled(PalletFeature::Approvals),
53			Error::<T, I>::MethodDisabled
54		);
55		let mut details =
56			Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownItem)?;
57
58		let collection_config = Self::get_collection_config(&collection)?;
59		ensure!(
60			collection_config.is_setting_enabled(CollectionSetting::TransferableItems),
61			Error::<T, I>::ItemsNonTransferable
62		);
63
64		if let Some(check_origin) = maybe_check_origin {
65			ensure!(check_origin == details.owner, Error::<T, I>::NoPermission);
66		}
67
68		let now = T::BlockNumberProvider::current_block_number();
69		let deadline = maybe_deadline.map(|d| d.saturating_add(now));
70
71		details
72			.approvals
73			.try_insert(delegate.clone(), deadline)
74			.map_err(|_| Error::<T, I>::ReachedApprovalLimit)?;
75		Item::<T, I>::insert(&collection, &item, &details);
76
77		Self::deposit_event(Event::TransferApproved {
78			collection,
79			item,
80			owner: details.owner,
81			delegate,
82			deadline,
83		});
84
85		Ok(())
86	}
87
88	/// Cancels the approval for the transfer of an item to a delegate.
89	///
90	/// This function is used to cancel the approval for the transfer of the specified `item` in the
91	/// `collection` to a `delegate`. If `maybe_check_origin` is specified, the function ensures
92	/// that the `check_origin` account is the owner of the item or that the approval is past its
93	/// deadline, granting permission to cancel the approval. After canceling the approval, the
94	/// function emits the `ApprovalCancelled` event.
95	///
96	/// - `maybe_check_origin`: The optional account that is required to be the owner of the item or
97	///   that the approval is past its deadline, granting permission to cancel the approval. If
98	///   `None`, no permission check is performed.
99	/// - `collection`: The identifier of the collection containing the item.
100	/// - `item`: The identifier of the item.
101	/// - `delegate`: The account that was previously allowed to take control of the item.
102	pub(crate) fn do_cancel_approval(
103		maybe_check_origin: Option<T::AccountId>,
104		collection: T::CollectionId,
105		item: T::ItemId,
106		delegate: T::AccountId,
107	) -> DispatchResult {
108		let mut details =
109			Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownItem)?;
110
111		let maybe_deadline = details.approvals.get(&delegate).ok_or(Error::<T, I>::NotDelegate)?;
112
113		let is_past_deadline = if let Some(deadline) = maybe_deadline {
114			let now = T::BlockNumberProvider::current_block_number();
115			now > *deadline
116		} else {
117			false
118		};
119
120		if !is_past_deadline {
121			if let Some(check_origin) = maybe_check_origin {
122				ensure!(check_origin == details.owner, Error::<T, I>::NoPermission);
123			}
124		}
125
126		details.approvals.remove(&delegate);
127		Item::<T, I>::insert(&collection, &item, &details);
128
129		Self::deposit_event(Event::ApprovalCancelled {
130			collection,
131			item,
132			owner: details.owner,
133			delegate,
134		});
135
136		Ok(())
137	}
138
139	/// Clears all transfer approvals for an item.
140	///
141	/// This function is used to clear all transfer approvals for the specified `item` in the
142	/// `collection`. If `maybe_check_origin` is specified, the function ensures that the
143	/// `check_origin` account is the owner of the item, granting permission to clear all transfer
144	/// approvals. After clearing all approvals, the function emits the `AllApprovalsCancelled`
145	/// event.
146	///
147	/// - `maybe_check_origin`: The optional account that is required to be the owner of the item,
148	///   granting permission to clear all transfer approvals. If `None`, no permission check is
149	///   performed.
150	/// - `collection`: The collection ID containing the item.
151	/// - `item`: The item ID for which transfer approvals will be cleared.
152	pub(crate) fn do_clear_all_transfer_approvals(
153		maybe_check_origin: Option<T::AccountId>,
154		collection: T::CollectionId,
155		item: T::ItemId,
156	) -> DispatchResult {
157		let mut details =
158			Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownCollection)?;
159
160		if let Some(check_origin) = maybe_check_origin {
161			ensure!(check_origin == details.owner, Error::<T, I>::NoPermission);
162		}
163
164		details.approvals.clear();
165		Item::<T, I>::insert(&collection, &item, &details);
166
167		Self::deposit_event(Event::AllApprovalsCancelled {
168			collection,
169			item,
170			owner: details.owner,
171		});
172
173		Ok(())
174	}
175}