// 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. use crate::*; use frame_support::pallet_prelude::*; impl, I: 'static> Pallet { pub fn do_mint( collection: T::CollectionId, item: T::ItemId, maybe_depositor: Option, mint_to: T::AccountId, item_config: ItemConfig, with_details_and_config: impl FnOnce( &CollectionDetailsFor, &CollectionConfigFor, ) -> DispatchResult, ) -> DispatchResult { ensure!(!Item::::contains_key(collection, item), Error::::AlreadyExists); Collection::::try_mutate( &collection, |maybe_collection_details| -> DispatchResult { let collection_details = maybe_collection_details.as_mut().ok_or(Error::::UnknownCollection)?; let collection_config = Self::get_collection_config(&collection)?; with_details_and_config(collection_details, &collection_config)?; if let Some(max_supply) = collection_config.max_supply { ensure!(collection_details.items < max_supply, Error::::MaxSupplyReached); } collection_details.items.saturating_inc(); let collection_config = Self::get_collection_config(&collection)?; let deposit_amount = match collection_config .is_setting_enabled(CollectionSetting::DepositRequired) { true => T::ItemDeposit::get(), false => Zero::zero(), }; let deposit_account = match maybe_depositor { None => collection_details.owner.clone(), Some(depositor) => depositor, }; let item_owner = mint_to.clone(); Account::::insert((&item_owner, &collection, &item), ()); if let Ok(existing_config) = ItemConfigOf::::try_get(&collection, &item) { ensure!(existing_config == item_config, Error::::InconsistentItemConfig); } else { ItemConfigOf::::insert(&collection, &item, item_config); collection_details.item_configs.saturating_inc(); } T::Currency::reserve(&deposit_account, deposit_amount)?; let deposit = ItemDeposit { account: deposit_account, amount: deposit_amount }; let details = ItemDetails { owner: item_owner, approvals: ApprovalsOf::::default(), deposit, }; Item::::insert(&collection, &item, details); Ok(()) }, )?; Self::deposit_event(Event::Issued { collection, item, owner: mint_to }); Ok(()) } pub(crate) fn do_mint_pre_signed( mint_to: T::AccountId, mint_data: PreSignedMintOf, signer: T::AccountId, ) -> DispatchResult { let PreSignedMint { collection, item, attributes, metadata, deadline, only_account } = mint_data; let metadata = Self::construct_metadata(metadata)?; ensure!( attributes.len() <= T::MaxAttributesPerCall::get() as usize, Error::::MaxAttributesLimitReached ); if let Some(account) = only_account { ensure!(account == mint_to, Error::::WrongOrigin); } let now = frame_system::Pallet::::block_number(); ensure!(deadline >= now, Error::::DeadlineExpired); ensure!( Self::has_role(&collection, &signer, CollectionRole::Issuer), Error::::NoPermission ); let item_config = ItemConfig { settings: Self::get_default_item_settings(&collection)? }; Self::do_mint( collection, item, Some(mint_to.clone()), mint_to.clone(), item_config, |_, _| Ok(()), )?; let admin_account = Self::find_account_by_role(&collection, CollectionRole::Admin); if let Some(admin_account) = admin_account { for (key, value) in attributes { Self::do_set_attribute( admin_account.clone(), collection, Some(item), AttributeNamespace::CollectionOwner, Self::construct_attribute_key(key)?, Self::construct_attribute_value(value)?, mint_to.clone(), )?; } if !metadata.len().is_zero() { Self::do_set_item_metadata( Some(admin_account.clone()), collection, item, metadata, Some(mint_to.clone()), )?; } } Ok(()) } pub fn do_burn( collection: T::CollectionId, item: T::ItemId, with_details: impl FnOnce(&ItemDetailsFor) -> DispatchResult, ) -> DispatchResult { ensure!(!T::Locker::is_locked(collection, item), Error::::ItemLocked); let item_config = Self::get_item_config(&collection, &item)?; // NOTE: if item's settings are not empty (e.g. item's metadata is locked) // then we keep the config record and don't remove it let remove_config = !item_config.has_disabled_settings(); let owner = Collection::::try_mutate( &collection, |maybe_collection_details| -> Result { let collection_details = maybe_collection_details.as_mut().ok_or(Error::::UnknownCollection)?; let details = Item::::get(&collection, &item) .ok_or(Error::::UnknownCollection)?; with_details(&details)?; // Return the deposit. T::Currency::unreserve(&details.deposit.account, details.deposit.amount); collection_details.items.saturating_dec(); if remove_config { collection_details.item_configs.saturating_dec(); } // Clear the metadata if it's not locked. if item_config.is_setting_enabled(ItemSetting::UnlockedMetadata) { if let Some(metadata) = ItemMetadataOf::::take(&collection, &item) { let depositor_account = metadata.deposit.account.unwrap_or(collection_details.owner.clone()); T::Currency::unreserve(&depositor_account, metadata.deposit.amount); collection_details.item_metadatas.saturating_dec(); if depositor_account == collection_details.owner { collection_details .owner_deposit .saturating_reduce(metadata.deposit.amount); } } } Ok(details.owner) }, )?; Item::::remove(&collection, &item); Account::::remove((&owner, &collection, &item)); ItemPriceOf::::remove(&collection, &item); PendingSwapOf::::remove(&collection, &item); ItemAttributesApprovalsOf::::remove(&collection, &item); if remove_config { ItemConfigOf::::remove(&collection, &item); } Self::deposit_event(Event::Burned { collection, item, owner }); Ok(()) } }