git.delta.rocks / unique-network / refs/commits / 00a765656f26

difftreelog

source

pallets/proxy-rmrk-equip/src/lib.rs7.6 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;3132pub use pallet::*;3334#[frame_support::pallet]35pub mod pallet {36	use super::*;3738	#[pallet::config]39	pub trait Config: frame_system::Config + pallet_rmrk_core::Config {40		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;41	}4243	#[pallet::storage]44	#[pallet::getter(fn internal_part_id)]45	pub type InernalPartId<T: Config> =46		StorageDoubleMap<_, Twox64Concat, CollectionId, Twox64Concat, RmrkPartId, TokenId>;4748	#[pallet::storage]49	#[pallet::getter(fn base_has_default_theme)]50	pub type BaseHasDefaultTheme<T: Config> =51		StorageMap<_, Twox64Concat, CollectionId, bool, ValueQuery>;5253	#[pallet::pallet]54	#[pallet::generate_store(pub(super) trait Store)]55	pub struct Pallet<T>(_);5657	#[pallet::event]58	#[pallet::generate_deposit(pub(super) fn deposit_event)]59	pub enum Event<T: Config> {60		BaseCreated {61			issuer: T::AccountId,62			base_id: RmrkBaseId,63		},64	}6566	#[pallet::error]67	pub enum Error<T> {68		PermissionError,69		NoAvailableBaseId,70		NoAvailablePartId,71		BaseDoesntExist,72		NeedsDefaultThemeFirst,73	}7475	#[pallet::call]76	impl<T: Config> Pallet<T> {77		/// Creates a new Base.78		/// Modeled after [base interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/base.md)79		///80		/// Parameters:81		/// - origin: Caller, will be assigned as the issuer of the Base82		/// - base_type: media type, e.g. "svg"83		/// - symbol: arbitrary client-chosen symbol84		/// - parts: array of Fixed and Slot parts composing the base, confined in length by85		///   RmrkPartsLimit86		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]87		#[transactional]88		pub fn create_base(89			origin: OriginFor<T>,90			base_type: RmrkString,91			symbol: RmrkString,92			parts: BoundedVec<RmrkPartType, RmrkPartsLimit>,93		) -> DispatchResult {94			let sender = ensure_signed(origin)?;95			let cross_sender = T::CrossAccountId::from_sub(sender.clone());9697			let data = CreateCollectionData {98				limits: None,99				token_prefix: symbol100					.into_inner()101					.try_into()102					.map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,103				..Default::default()104			};105106			let collection_id_res =107				<PalletNft<T>>::init_collection(cross_sender.clone(), data, true);108109			if let Err(DispatchError::Arithmetic(_)) = &collection_id_res {110				return Err(<Error<T>>::NoAvailableBaseId.into());111			}112113			let collection_id = collection_id_res?;114115			<PalletCommon<T>>::set_scoped_collection_properties(116				collection_id,117				PropertyScope::Rmrk,118				[119					<PalletCore<T>>::rmrk_property(CollectionType, &misc::CollectionType::Base)?,120					<PalletCore<T>>::rmrk_property(BaseType, &base_type)?,121				]122				.into_iter(),123			)?;124125			let collection = <PalletCore<T>>::get_nft_collection(collection_id)?;126127			for part in parts {128				let part_id = part.id();129				let part_token_id = Self::create_part(&cross_sender, &collection, part)?;130131				<InernalPartId<T>>::insert(collection_id, part_id, part_token_id);132133				<PalletNft<T>>::set_scoped_token_property(134					collection_id,135					part_token_id,136					PropertyScope::Rmrk,137					<PalletCore<T>>::rmrk_property(ExternalPartId, &part_id)?,138				)?;139			}140141			Self::deposit_event(Event::BaseCreated {142				issuer: sender,143				base_id: collection_id.0,144			});145146			Ok(())147		}148149		/// Adds a Theme to a Base.150		/// Modeled after [themeadd interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/themeadd.md)151		/// Themes are stored in the Themes storage152		/// A Theme named "default" is required prior to adding other Themes.153		///154		/// Parameters:155		/// - origin: The caller of the function, must be issuer of the base156		/// - base_id: The Base containing the Theme to be updated157		/// - theme: The Theme to add to the Base.  A Theme has a name and properties, which are an158		///   array of [key, value, inherit].159		///   - key: arbitrary BoundedString, defined by client160		///   - value: arbitrary BoundedString, defined by client161		///   - inherit: optional bool162		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]163		#[transactional]164		pub fn theme_add(165			origin: OriginFor<T>,166			base_id: RmrkBaseId,167			theme: RmrkTheme,168		) -> DispatchResult {169			let sender = ensure_signed(origin)?;170171			let sender = T::CrossAccountId::from_sub(sender);172			let owner = &sender;173174			let collection_id: CollectionId = base_id.into();175176			let collection = <PalletCore<T>>::get_typed_nft_collection(177				collection_id,178				misc::CollectionType::Base,179			)180			.map_err(|_| <Error<T>>::BaseDoesntExist)?;181			collection.check_is_external()?;182183			if theme.name.as_slice() == b"default" {184				<BaseHasDefaultTheme<T>>::insert(collection_id, true);185			} else if !Self::base_has_default_theme(collection_id) {186				return Err(<Error<T>>::NeedsDefaultThemeFirst.into());187			}188189			let token_id = <PalletCore<T>>::create_nft(190				&sender,191				owner,192				&collection,193				[194					<PalletCore<T>>::rmrk_property(TokenType, &NftType::Theme)?,195					<PalletCore<T>>::rmrk_property(ThemeName, &theme.name)?,196					<PalletCore<T>>::rmrk_property(ThemeInherit, &theme.inherit)?,197				]198				.into_iter(),199			)200			.map_err(|_| <Error<T>>::PermissionError)?;201202			for property in theme.properties {203				<PalletNft<T>>::set_scoped_token_property(204					collection_id,205					token_id,206					PropertyScope::Rmrk,207					<PalletCore<T>>::rmrk_property(208						UserProperty(property.key.as_slice()),209						&property.value,210					)?,211				)?;212			}213214			Ok(())215		}216	}217}218219impl<T: Config> Pallet<T> {220	fn create_part(221		sender: &T::CrossAccountId,222		collection: &NonfungibleHandle<T>,223		part: RmrkPartType,224	) -> Result<TokenId, DispatchError> {225		let owner = sender;226227		let src = part.src();228		let z_index = part.z_index();229230		let nft_type = match part {231			RmrkPartType::FixedPart(_) => NftType::FixedPart,232			RmrkPartType::SlotPart(_) => NftType::SlotPart,233		};234235		let token_id = <PalletCore<T>>::create_nft(236			sender,237			owner,238			collection,239			[240				<PalletCore<T>>::rmrk_property(TokenType, &nft_type)?,241				<PalletCore<T>>::rmrk_property(Src, &src)?,242				<PalletCore<T>>::rmrk_property(ZIndex, &z_index)?,243			]244			.into_iter(),245		)246		.map_err(|err| match err {247			DispatchError::Arithmetic(_) => <Error<T>>::NoAvailablePartId.into(),248			err => err,249		})?;250251		if let RmrkPartType::SlotPart(part) = part {252			<PalletNft<T>>::set_scoped_token_property(253				collection.id,254				token_id,255				PropertyScope::Rmrk,256				<PalletCore<T>>::rmrk_property(EquippableList, &part.equippable)?,257			)?;258		}259260		Ok(token_id)261	}262}