mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-25 21:11:07 +00:00
84d6342cd2
Use `contains_key` to not require decoding the actual value.
235 lines
8.6 KiB
Rust
235 lines
8.6 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! This module contains helper methods to perform the transfer functionalities
|
|
//! of the NFTs pallet.
|
|
|
|
use crate::*;
|
|
use frame_support::pallet_prelude::*;
|
|
|
|
impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
|
/// Transfer an NFT to the specified destination account.
|
|
///
|
|
/// - `collection`: The ID of the collection to which the NFT belongs.
|
|
/// - `item`: The ID of the NFT to transfer.
|
|
/// - `dest`: The destination account to which the NFT will be transferred.
|
|
/// - `with_details`: A closure that provides access to the collection and item details,
|
|
/// allowing customization of the transfer process.
|
|
///
|
|
/// This function performs the actual transfer of an NFT to the destination account.
|
|
/// It checks various conditions like item lock status and transferability settings
|
|
/// for the collection and item before transferring the NFT.
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// This function returns a dispatch error in the following cases:
|
|
/// - If the collection ID is invalid ([`UnknownCollection`](crate::Error::UnknownCollection)).
|
|
/// - If the item ID is invalid ([`UnknownItem`](crate::Error::UnknownItem)).
|
|
/// - If the item is locked or transferring it is disabled
|
|
/// ([`ItemLocked`](crate::Error::ItemLocked)).
|
|
/// - If the collection or item is non-transferable
|
|
/// ([`ItemsNonTransferable`](crate::Error::ItemsNonTransferable)).
|
|
pub fn do_transfer(
|
|
collection: T::CollectionId,
|
|
item: T::ItemId,
|
|
dest: T::AccountId,
|
|
with_details: impl FnOnce(
|
|
&CollectionDetailsFor<T, I>,
|
|
&mut ItemDetailsFor<T, I>,
|
|
) -> DispatchResult,
|
|
) -> DispatchResult {
|
|
// Retrieve collection details.
|
|
let collection_details =
|
|
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
|
|
|
|
// Ensure the item is not locked.
|
|
ensure!(!T::Locker::is_locked(collection, item), Error::<T, I>::ItemLocked);
|
|
|
|
// Ensure the item is not transfer disabled on the system level attribute.
|
|
ensure!(
|
|
!Self::has_system_attribute(&collection, &item, PalletAttributes::TransferDisabled)?,
|
|
Error::<T, I>::ItemLocked
|
|
);
|
|
|
|
// Retrieve collection config and check if items are transferable.
|
|
let collection_config = Self::get_collection_config(&collection)?;
|
|
ensure!(
|
|
collection_config.is_setting_enabled(CollectionSetting::TransferableItems),
|
|
Error::<T, I>::ItemsNonTransferable
|
|
);
|
|
|
|
// Retrieve item config and check if the item is transferable.
|
|
let item_config = Self::get_item_config(&collection, &item)?;
|
|
ensure!(
|
|
item_config.is_setting_enabled(ItemSetting::Transferable),
|
|
Error::<T, I>::ItemLocked
|
|
);
|
|
|
|
// Retrieve the item details.
|
|
let mut details =
|
|
Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownItem)?;
|
|
|
|
// Perform the transfer with custom details using the provided closure.
|
|
with_details(&collection_details, &mut details)?;
|
|
|
|
// Update account ownership information.
|
|
Account::<T, I>::remove((&details.owner, &collection, &item));
|
|
Account::<T, I>::insert((&dest, &collection, &item), ());
|
|
let origin = details.owner;
|
|
details.owner = dest;
|
|
|
|
// The approved accounts have to be reset to `None`, because otherwise pre-approve attack
|
|
// would be possible, where the owner can approve their second account before making the
|
|
// transaction and then claiming the item back.
|
|
details.approvals.clear();
|
|
|
|
// Update item details.
|
|
Item::<T, I>::insert(&collection, &item, &details);
|
|
ItemPriceOf::<T, I>::remove(&collection, &item);
|
|
PendingSwapOf::<T, I>::remove(&collection, &item);
|
|
|
|
// Emit `Transferred` event.
|
|
Self::deposit_event(Event::Transferred {
|
|
collection,
|
|
item,
|
|
from: origin,
|
|
to: details.owner,
|
|
});
|
|
Ok(())
|
|
}
|
|
|
|
/// Transfer ownership of a collection to another account.
|
|
///
|
|
/// - `origin`: The account requesting the transfer.
|
|
/// - `collection`: The ID of the collection to transfer ownership.
|
|
/// - `owner`: The new account that will become the owner of the collection.
|
|
///
|
|
/// This function transfers the ownership of a collection to the specified account.
|
|
/// It performs checks to ensure that the `origin` is the current owner and that the
|
|
/// new owner is an acceptable account based on the collection's acceptance settings.
|
|
pub(crate) fn do_transfer_ownership(
|
|
origin: T::AccountId,
|
|
collection: T::CollectionId,
|
|
new_owner: T::AccountId,
|
|
) -> DispatchResult {
|
|
// Check if the new owner is acceptable based on the collection's acceptance settings.
|
|
let acceptable_collection = OwnershipAcceptance::<T, I>::get(&new_owner);
|
|
ensure!(acceptable_collection.as_ref() == Some(&collection), Error::<T, I>::Unaccepted);
|
|
|
|
// Try to retrieve and mutate the collection details.
|
|
Collection::<T, I>::try_mutate(collection, |maybe_details| {
|
|
let details = maybe_details.as_mut().ok_or(Error::<T, I>::UnknownCollection)?;
|
|
// Check if the `origin` is the current owner of the collection.
|
|
ensure!(origin == details.owner, Error::<T, I>::NoPermission);
|
|
if details.owner == new_owner {
|
|
return Ok(())
|
|
}
|
|
|
|
// Move the deposit to the new owner.
|
|
T::Currency::repatriate_reserved(
|
|
&details.owner,
|
|
&new_owner,
|
|
details.owner_deposit,
|
|
Reserved,
|
|
)?;
|
|
|
|
// Update account ownership information.
|
|
CollectionAccount::<T, I>::remove(&details.owner, &collection);
|
|
CollectionAccount::<T, I>::insert(&new_owner, &collection, ());
|
|
|
|
details.owner = new_owner.clone();
|
|
OwnershipAcceptance::<T, I>::remove(&new_owner);
|
|
frame_system::Pallet::<T>::dec_consumers(&new_owner);
|
|
|
|
// Emit `OwnerChanged` event.
|
|
Self::deposit_event(Event::OwnerChanged { collection, new_owner });
|
|
Ok(())
|
|
})
|
|
}
|
|
/// Set or unset the ownership acceptance for an account regarding a specific collection.
|
|
///
|
|
/// - `who`: The account for which to set or unset the ownership acceptance.
|
|
/// - `maybe_collection`: An optional collection ID to set the ownership acceptance.
|
|
///
|
|
/// If `maybe_collection` is `Some(collection)`, then the account `who` will accept
|
|
/// ownership transfers for the specified collection. If `maybe_collection` is `None`,
|
|
/// then the account `who` will unset the ownership acceptance, effectively refusing
|
|
/// ownership transfers for any collection.
|
|
pub(crate) fn do_set_accept_ownership(
|
|
who: T::AccountId,
|
|
maybe_collection: Option<T::CollectionId>,
|
|
) -> DispatchResult {
|
|
let exists = OwnershipAcceptance::<T, I>::contains_key(&who);
|
|
match (exists, maybe_collection.is_some()) {
|
|
(false, true) => {
|
|
frame_system::Pallet::<T>::inc_consumers(&who)?;
|
|
},
|
|
(true, false) => {
|
|
frame_system::Pallet::<T>::dec_consumers(&who);
|
|
},
|
|
_ => {},
|
|
}
|
|
if let Some(collection) = maybe_collection.as_ref() {
|
|
OwnershipAcceptance::<T, I>::insert(&who, collection);
|
|
} else {
|
|
OwnershipAcceptance::<T, I>::remove(&who);
|
|
}
|
|
|
|
// Emit `OwnershipAcceptanceChanged` event.
|
|
Self::deposit_event(Event::OwnershipAcceptanceChanged { who, maybe_collection });
|
|
Ok(())
|
|
}
|
|
|
|
/// Forcefully change the owner of a collection.
|
|
///
|
|
/// - `collection`: The ID of the collection to change ownership.
|
|
/// - `owner`: The new account that will become the owner of the collection.
|
|
///
|
|
/// This function allows for changing the ownership of a collection without any checks.
|
|
/// It moves the deposit to the new owner, updates the collection's owner, and emits
|
|
/// an `OwnerChanged` event.
|
|
pub(crate) fn do_force_collection_owner(
|
|
collection: T::CollectionId,
|
|
owner: T::AccountId,
|
|
) -> DispatchResult {
|
|
// Try to retrieve and mutate the collection details.
|
|
Collection::<T, I>::try_mutate(collection, |maybe_details| {
|
|
let details = maybe_details.as_mut().ok_or(Error::<T, I>::UnknownCollection)?;
|
|
if details.owner == owner {
|
|
return Ok(())
|
|
}
|
|
|
|
// Move the deposit to the new owner.
|
|
T::Currency::repatriate_reserved(
|
|
&details.owner,
|
|
&owner,
|
|
details.owner_deposit,
|
|
Reserved,
|
|
)?;
|
|
|
|
// Update collection accounts and set the new owner.
|
|
CollectionAccount::<T, I>::remove(&details.owner, &collection);
|
|
CollectionAccount::<T, I>::insert(&owner, &collection, ());
|
|
details.owner = owner.clone();
|
|
|
|
// Emit `OwnerChanged` event.
|
|
Self::deposit_event(Event::OwnerChanged { collection, new_owner: owner });
|
|
Ok(())
|
|
})
|
|
}
|
|
}
|