Files
pezkuwi-sdk/bizinikiwi/pezframe/nfts/src/features/metadata.rs
T
pezkuwichain b6d35f6faf chore: add Dijital Kurdistan Tech Institute to copyright headers
Updated 4763 files with dual copyright:
- Parity Technologies (UK) Ltd.
- Dijital Kurdistan Tech Institute
2025-12-27 21:28:36 +03:00

283 lines
11 KiB
Rust

// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
// 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 configure the metadata of collections and items.
use crate::*;
use alloc::vec::Vec;
use pezframe_support::pezpallet_prelude::*;
impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
/// Sets the metadata for a specific item within a collection.
///
/// - `maybe_check_origin`: An optional account ID that is allowed to set the metadata. If
/// `None`, it's considered the root account.
/// - `collection`: The ID of the collection to which the item belongs.
/// - `item`: The ID of the item to set the metadata for.
/// - `data`: The metadata to set for the item.
/// - `maybe_depositor`: An optional account ID that will provide the deposit for the metadata.
/// If `None`, the collection's owner provides the deposit.
///
/// Emits `ItemMetadataSet` event upon successful setting of the metadata.
/// Returns `Ok(())` on success, or one of the following dispatch errors:
/// - `UnknownCollection`: The specified collection does not exist.
/// - `UnknownItem`: The specified item does not exist within the collection.
/// - `LockedItemMetadata`: The metadata for the item is locked and cannot be modified.
/// - `NoPermission`: The caller does not have the required permission to set the metadata.
/// - `DepositExceeded`: The deposit amount exceeds the maximum allowed value.
pub(crate) fn do_set_item_metadata(
maybe_check_origin: Option<T::AccountId>,
collection: T::CollectionId,
item: T::ItemId,
data: BoundedVec<u8, T::StringLimit>,
maybe_depositor: Option<T::AccountId>,
) -> DispatchResult {
if let Some(check_origin) = &maybe_check_origin {
ensure!(
Self::has_role(&collection, &check_origin, CollectionRole::Admin),
Error::<T, I>::NoPermission
);
}
let is_root = maybe_check_origin.is_none();
let mut collection_details =
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
let item_config = Self::get_item_config(&collection, &item)?;
ensure!(
is_root || item_config.is_setting_enabled(ItemSetting::UnlockedMetadata),
Error::<T, I>::LockedItemMetadata
);
let collection_config = Self::get_collection_config(&collection)?;
ItemMetadataOf::<T, I>::try_mutate_exists(collection, item, |metadata| {
if metadata.is_none() {
collection_details.item_metadatas.saturating_inc();
}
let old_deposit = metadata
.take()
.map_or(ItemMetadataDeposit { account: None, amount: Zero::zero() }, |m| m.deposit);
let mut deposit = Zero::zero();
if collection_config.is_setting_enabled(CollectionSetting::DepositRequired) && !is_root
{
deposit = T::DepositPerByte::get()
.saturating_mul(((data.len()) as u32).into())
.saturating_add(T::MetadataDepositBase::get());
}
let depositor = maybe_depositor.clone().unwrap_or(collection_details.owner.clone());
let old_depositor = old_deposit.account.unwrap_or(collection_details.owner.clone());
if depositor != old_depositor {
T::Currency::unreserve(&old_depositor, old_deposit.amount);
T::Currency::reserve(&depositor, deposit)?;
} else if deposit > old_deposit.amount {
T::Currency::reserve(&depositor, deposit - old_deposit.amount)?;
} else if deposit < old_deposit.amount {
T::Currency::unreserve(&depositor, old_deposit.amount - deposit);
}
if maybe_depositor.is_none() {
collection_details.owner_deposit.saturating_accrue(deposit);
collection_details.owner_deposit.saturating_reduce(old_deposit.amount);
}
*metadata = Some(ItemMetadata {
deposit: ItemMetadataDeposit { account: maybe_depositor, amount: deposit },
data: data.clone(),
});
Collection::<T, I>::insert(&collection, &collection_details);
Self::deposit_event(Event::ItemMetadataSet { collection, item, data });
Ok(())
})
}
/// Clears the metadata for a specific item within a collection.
///
/// - `maybe_check_origin`: An optional account ID that is allowed to clear the metadata. If
/// `None`, it's considered the root account.
/// - `collection`: The ID of the collection to which the item belongs.
/// - `item`: The ID of the item for which to clear the metadata.
///
/// Emits `ItemMetadataCleared` event upon successful clearing of the metadata.
/// Returns `Ok(())` on success, or one of the following dispatch errors:
/// - `UnknownCollection`: The specified collection does not exist.
/// - `MetadataNotFound`: The metadata for the specified item was not found.
/// - `LockedItemMetadata`: The metadata for the item is locked and cannot be modified.
/// - `NoPermission`: The caller does not have the required permission to clear the metadata.
pub(crate) fn do_clear_item_metadata(
maybe_check_origin: Option<T::AccountId>,
collection: T::CollectionId,
item: T::ItemId,
) -> DispatchResult {
if let Some(check_origin) = &maybe_check_origin {
ensure!(
Self::has_role(&collection, &check_origin, CollectionRole::Admin),
Error::<T, I>::NoPermission
);
}
let is_root = maybe_check_origin.is_none();
let metadata = ItemMetadataOf::<T, I>::take(collection, item)
.ok_or(Error::<T, I>::MetadataNotFound)?;
let mut collection_details =
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
let depositor_account =
metadata.deposit.account.unwrap_or(collection_details.owner.clone());
// NOTE: if the item was previously burned, the ItemConfigOf record might not exist
let is_locked = Self::get_item_config(&collection, &item)
.map_or(false, |c| c.has_disabled_setting(ItemSetting::UnlockedMetadata));
ensure!(is_root || !is_locked, Error::<T, I>::LockedItemMetadata);
collection_details.item_metadatas.saturating_dec();
T::Currency::unreserve(&depositor_account, metadata.deposit.amount);
if depositor_account == collection_details.owner {
collection_details.owner_deposit.saturating_reduce(metadata.deposit.amount);
}
Collection::<T, I>::insert(&collection, &collection_details);
Self::deposit_event(Event::ItemMetadataCleared { collection, item });
Ok(())
}
/// Sets the metadata for a specific collection.
///
/// - `maybe_check_origin`: An optional account ID that is allowed to set the collection
/// metadata. If `None`, it's considered the root account.
/// - `collection`: The ID of the collection for which to set the metadata.
/// - `data`: The metadata to set for the collection.
///
/// Emits `CollectionMetadataSet` event upon successful setting of the metadata.
/// Returns `Ok(())` on success, or one of the following dispatch errors:
/// - `UnknownCollection`: The specified collection does not exist.
/// - `LockedCollectionMetadata`: The metadata for the collection is locked and cannot be
/// modified.
/// - `NoPermission`: The caller does not have the required permission to set the metadata.
pub(crate) fn do_set_collection_metadata(
maybe_check_origin: Option<T::AccountId>,
collection: T::CollectionId,
data: BoundedVec<u8, T::StringLimit>,
) -> DispatchResult {
if let Some(check_origin) = &maybe_check_origin {
ensure!(
Self::has_role(&collection, &check_origin, CollectionRole::Admin),
Error::<T, I>::NoPermission
);
}
let is_root = maybe_check_origin.is_none();
let collection_config = Self::get_collection_config(&collection)?;
ensure!(
is_root || collection_config.is_setting_enabled(CollectionSetting::UnlockedMetadata),
Error::<T, I>::LockedCollectionMetadata
);
let mut details =
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
CollectionMetadataOf::<T, I>::try_mutate_exists(collection, |metadata| {
let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit);
details.owner_deposit.saturating_reduce(old_deposit);
let mut deposit = Zero::zero();
if !is_root && collection_config.is_setting_enabled(CollectionSetting::DepositRequired)
{
deposit = T::DepositPerByte::get()
.saturating_mul(((data.len()) as u32).into())
.saturating_add(T::MetadataDepositBase::get());
}
if deposit > old_deposit {
T::Currency::reserve(&details.owner, deposit - old_deposit)?;
} else if deposit < old_deposit {
T::Currency::unreserve(&details.owner, old_deposit - deposit);
}
details.owner_deposit.saturating_accrue(deposit);
Collection::<T, I>::insert(&collection, details);
*metadata = Some(CollectionMetadata { deposit, data: data.clone() });
Self::deposit_event(Event::CollectionMetadataSet { collection, data });
Ok(())
})
}
/// Clears the metadata for a specific collection.
///
/// - `maybe_check_origin`: An optional account ID that is allowed to clear the collection
/// metadata. If `None`, it's considered the root account.
/// - `collection`: The ID of the collection for which to clear the metadata.
///
/// Emits `CollectionMetadataCleared` event upon successful clearing of the metadata.
/// Returns `Ok(())` on success, or one of the following dispatch errors:
/// - `UnknownCollection`: The specified collection does not exist.
/// - `MetadataNotFound`: The metadata for the collection was not found.
/// - `LockedCollectionMetadata`: The metadata for the collection is locked and cannot be
/// modified.
/// - `NoPermission`: The caller does not have the required permission to clear the metadata.
pub(crate) fn do_clear_collection_metadata(
maybe_check_origin: Option<T::AccountId>,
collection: T::CollectionId,
) -> DispatchResult {
if let Some(check_origin) = &maybe_check_origin {
ensure!(
Self::has_role(&collection, &check_origin, CollectionRole::Admin),
Error::<T, I>::NoPermission
);
}
let mut details =
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
let collection_config = Self::get_collection_config(&collection)?;
ensure!(
maybe_check_origin.is_none()
|| collection_config.is_setting_enabled(CollectionSetting::UnlockedMetadata),
Error::<T, I>::LockedCollectionMetadata
);
CollectionMetadataOf::<T, I>::try_mutate_exists(collection, |metadata| {
let deposit = metadata.take().ok_or(Error::<T, I>::UnknownCollection)?.deposit;
T::Currency::unreserve(&details.owner, deposit);
details.owner_deposit.saturating_reduce(deposit);
Collection::<T, I>::insert(&collection, details);
Self::deposit_event(Event::CollectionMetadataCleared { collection });
Ok(())
})
}
/// A helper method to construct metadata.
///
/// # Errors
///
/// This function returns an [`IncorrectMetadata`](crate::Error::IncorrectMetadata) dispatch
/// error if the provided metadata is too long.
pub fn construct_metadata(
metadata: Vec<u8>,
) -> Result<BoundedVec<u8, T::StringLimit>, DispatchError> {
Ok(BoundedVec::try_from(metadata).map_err(|_| Error::<T, I>::IncorrectMetadata)?)
}
}