git.delta.rocks / unique-network / refs/commits / 9fdcfd82895a

difftreelog

source

pallets/proxy-rmrk-equip/src/lib.rs7.8 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![cfg_attr(not(feature = "std"), no_std)]1819use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};20use frame_system::{pallet_prelude::*, ensure_signed};21use sp_runtime::DispatchError;22use up_data_structs::*;23use pallet_common::{Pallet as PalletCommon, Error as CommonError};24use pallet_rmrk_core::{25	Pallet as PalletCore,26	misc::{self, *},27	property::RmrkProperty::*,28};29use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle};30use pallet_evm::account::CrossAccountId;31use weights::WeightInfo;3233pub use pallet::*;3435#[cfg(feature = "runtime-benchmarks")]36pub mod benchmarking;37pub mod weights;3839pub type SelfWeightOf<T> = <T as Config>::WeightInfo;4041#[frame_support::pallet]42pub mod pallet {43	use super::*;4445	#[pallet::config]46	pub trait Config: frame_system::Config + pallet_rmrk_core::Config {47		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;48		type WeightInfo: WeightInfo;49	}5051	#[pallet::storage]52	#[pallet::getter(fn internal_part_id)]53	pub type InernalPartId<T: Config> =54		StorageDoubleMap<_, Twox64Concat, CollectionId, Twox64Concat, RmrkPartId, TokenId>;5556	#[pallet::storage]57	#[pallet::getter(fn base_has_default_theme)]58	pub type BaseHasDefaultTheme<T: Config> =59		StorageMap<_, Twox64Concat, CollectionId, bool, ValueQuery>;6061	#[pallet::pallet]62	#[pallet::generate_store(pub(super) trait Store)]63	pub struct Pallet<T>(_);6465	#[pallet::event]66	#[pallet::generate_deposit(pub(super) fn deposit_event)]67	pub enum Event<T: Config> {68		BaseCreated {69			issuer: T::AccountId,70			base_id: RmrkBaseId,71		},72	}7374	#[pallet::error]75	pub enum Error<T> {76		PermissionError,77		NoAvailableBaseId,78		NoAvailablePartId,79		BaseDoesntExist,80		NeedsDefaultThemeFirst,81	}8283	#[pallet::call]84	impl<T: Config> Pallet<T> {85		/// Creates a new Base.86		/// Modeled after [base interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/base.md)87		///88		/// Parameters:89		/// - origin: Caller, will be assigned as the issuer of the Base90		/// - base_type: media type, e.g. "svg"91		/// - symbol: arbitrary client-chosen symbol92		/// - parts: array of Fixed and Slot parts composing the base, confined in length by93		///   RmrkPartsLimit94		#[transactional]95		#[pallet::weight(<SelfWeightOf<T>>::create_base(parts.len() as u32))]96		pub fn create_base(97			origin: OriginFor<T>,98			base_type: RmrkString,99			symbol: RmrkBaseSymbol,100			parts: BoundedVec<RmrkPartType, RmrkPartsLimit>,101		) -> DispatchResult {102			let sender = ensure_signed(origin)?;103			let cross_sender = T::CrossAccountId::from_sub(sender.clone());104105			let data = CreateCollectionData {106				limits: None,107				token_prefix: symbol108					.into_inner()109					.try_into()110					.map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,111				..Default::default()112			};113114			let collection_id_res =115				<PalletNft<T>>::init_collection(cross_sender.clone(), data, true);116117			if let Err(DispatchError::Arithmetic(_)) = &collection_id_res {118				return Err(<Error<T>>::NoAvailableBaseId.into());119			}120121			let collection_id = collection_id_res?;122123			<PalletCommon<T>>::set_scoped_collection_properties(124				collection_id,125				PropertyScope::Rmrk,126				[127					<PalletCore<T>>::rmrk_property(CollectionType, &misc::CollectionType::Base)?,128					<PalletCore<T>>::rmrk_property(BaseType, &base_type)?,129				]130				.into_iter(),131			)?;132133			let collection = <PalletCore<T>>::get_nft_collection(collection_id)?;134135			for part in parts {136				let part_id = part.id();137				let part_token_id = Self::create_part(&cross_sender, &collection, part)?;138139				<InernalPartId<T>>::insert(collection_id, part_id, part_token_id);140141				<PalletNft<T>>::set_scoped_token_property(142					collection_id,143					part_token_id,144					PropertyScope::Rmrk,145					<PalletCore<T>>::rmrk_property(ExternalPartId, &part_id)?,146				)?;147			}148149			Self::deposit_event(Event::BaseCreated {150				issuer: sender,151				base_id: collection_id.0,152			});153154			Ok(())155		}156157		/// Adds a Theme to a Base.158		/// Modeled after [themeadd interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/themeadd.md)159		/// Themes are stored in the Themes storage160		/// A Theme named "default" is required prior to adding other Themes.161		///162		/// Parameters:163		/// - origin: The caller of the function, must be issuer of the base164		/// - base_id: The Base containing the Theme to be updated165		/// - theme: The Theme to add to the Base.  A Theme has a name and properties, which are an166		///   array of [key, value, inherit].167		///   - key: arbitrary BoundedString, defined by client168		///   - value: arbitrary BoundedString, defined by client169		///   - inherit: optional bool170		#[transactional]171		#[pallet::weight(<SelfWeightOf<T>>::theme_add(theme.properties.len() as u32))]172		pub fn theme_add(173			origin: OriginFor<T>,174			base_id: RmrkBaseId,175			theme: RmrkBoundedTheme,176		) -> DispatchResult {177			let sender = ensure_signed(origin)?;178179			let sender = T::CrossAccountId::from_sub(sender);180			let owner = &sender;181182			let collection_id: CollectionId = base_id.into();183184			let collection = <PalletCore<T>>::get_typed_nft_collection(185				collection_id,186				misc::CollectionType::Base,187			)188			.map_err(|_| <Error<T>>::BaseDoesntExist)?;189			collection.check_is_external()?;190191			if theme.name.as_slice() == b"default" {192				<BaseHasDefaultTheme<T>>::insert(collection_id, true);193			} else if !Self::base_has_default_theme(collection_id) {194				return Err(<Error<T>>::NeedsDefaultThemeFirst.into());195			}196197			let token_id = <PalletCore<T>>::create_nft(198				&sender,199				owner,200				&collection,201				[202					<PalletCore<T>>::rmrk_property(TokenType, &NftType::Theme)?,203					<PalletCore<T>>::rmrk_property(ThemeName, &theme.name)?,204					<PalletCore<T>>::rmrk_property(ThemeInherit, &theme.inherit)?,205				]206				.into_iter(),207			)208			.map_err(|_| <Error<T>>::PermissionError)?;209210			for property in theme.properties {211				<PalletNft<T>>::set_scoped_token_property(212					collection_id,213					token_id,214					PropertyScope::Rmrk,215					<PalletCore<T>>::rmrk_property(216						UserProperty(property.key.as_slice()),217						&property.value,218					)?,219				)?;220			}221222			Ok(())223		}224	}225}226227impl<T: Config> Pallet<T> {228	fn create_part(229		sender: &T::CrossAccountId,230		collection: &NonfungibleHandle<T>,231		part: RmrkPartType,232	) -> Result<TokenId, DispatchError> {233		let owner = sender;234235		let src = part.src();236		let z_index = part.z_index();237238		let nft_type = match part {239			RmrkPartType::FixedPart(_) => NftType::FixedPart,240			RmrkPartType::SlotPart(_) => NftType::SlotPart,241		};242243		let token_id = <PalletCore<T>>::create_nft(244			sender,245			owner,246			collection,247			[248				<PalletCore<T>>::rmrk_property(TokenType, &nft_type)?,249				<PalletCore<T>>::rmrk_property(Src, &src)?,250				<PalletCore<T>>::rmrk_property(ZIndex, &z_index)?,251			]252			.into_iter(),253		)254		.map_err(|err| match err {255			DispatchError::Arithmetic(_) => <Error<T>>::NoAvailablePartId.into(),256			err => err,257		})?;258259		if let RmrkPartType::SlotPart(part) = part {260			<PalletNft<T>>::set_scoped_token_property(261				collection.id,262				token_id,263				PropertyScope::Rmrk,264				<PalletCore<T>>::rmrk_property(EquippableList, &part.equippable)?,265			)?;266		}267268		Ok(token_id)269	}270}