git.delta.rocks / unique-network / refs/commits / ae0e886a6664

difftreelog

feat track and limit admin amount

Yaroslav Bolyukin2021-11-04parent: #98884c8.patch.diff
in: master

3 files changed

modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -11,7 +11,8 @@
 use nft_data_structs::{
 	COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,
 	MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_COLLECTION_NAME_LENGTH, MAX_TOKEN_PREFIX_LENGTH,
-	MetaUpdatePermission, Pays, PostDispatchInfo, TokenId, Weight, WithdrawReasons,
+	COLLECTION_ADMINS_LIMIT, MetaUpdatePermission, Pays, PostDispatchInfo, TokenId, Weight,
+	WithdrawReasons,
 };
 pub use pallet::*;
 use sp_core::H160;
@@ -165,6 +166,13 @@
 	#[pallet::generate_store(pub(super) trait Store)]
 	pub struct Pallet<T>(_);
 
+	#[pallet::extra_constants]
+	impl<T: Config> Pallet<T> {
+		pub fn collection_admins_limit() -> u32 {
+			COLLECTION_ADMINS_LIMIT
+		}
+	}
+
 	#[pallet::event]
 	#[pallet::generate_deposit(pub fn deposit_event)]
 	pub enum Event<T: Config> {
@@ -265,6 +273,8 @@
 		TotalCollectionsLimitExceeded,
 		/// variable_data exceeded data limit.
 		TokenVariableDataLimitExceeded,
+		/// Exceeded max admin amount
+		CollectionAdminAmountExceeded,
 
 		/// Collection settings not allowing items transferring
 		TransferNotAllowed,
@@ -305,6 +315,14 @@
 		QueryKind = OptionQuery,
 	>;
 
+	#[pallet::storage]
+	pub type AdminAmount<T> = StorageMap<
+		Hasher = Blake2_128Concat,
+		Key = CollectionId,
+		Value = u32,
+		QueryKind = ValueQuery,
+	>;
+
 	/// List of collection admins
 	#[pallet::storage]
 	pub type IsAdmin<T: Config> = StorageNMap<
@@ -419,6 +437,7 @@
 
 		<DestroyedCollectionCount<T>>::put(destroyed_collections);
 		<CollectionById<T>>::remove(collection.id);
+		<AdminAmount<T>>::remove(collection.id);
 		<IsAdmin<T>>::remove_prefix((collection.id,), None);
 		<Allowlist<T>>::remove_prefix((collection.id,), None);
 		Ok(())
@@ -435,9 +454,44 @@
 		// =========
 
 		if allowed {
-			<Allowlist<T>>::insert((collection.id, user.as_sub()), true);
+			<Allowlist<T>>::insert((collection.id, user), true);
+		} else {
+			<Allowlist<T>>::remove((collection.id, user));
+		}
+
+		Ok(())
+	}
+
+	pub fn toggle_admin(
+		collection: &CollectionHandle<T>,
+		sender: &T::CrossAccountId,
+		user: &T::CrossAccountId,
+		admin: bool,
+	) -> DispatchResult {
+		collection.check_is_owner_or_admin(&sender)?;
+
+		let was_admin = <IsAdmin<T>>::get((collection.id, user));
+		if was_admin == admin {
+			return Ok(());
+		}
+		let amount = <AdminAmount<T>>::get(collection.id);
+
+		if admin {
+			let amount = amount
+				.checked_add(1)
+				.ok_or(<Error<T>>::CollectionAdminAmountExceeded)?;
+			ensure!(
+				amount <= Self::collection_admins_limit(),
+				<Error<T>>::CollectionAdminAmountExceeded,
+			);
+
+			// =========
+
+			<AdminAmount<T>>::insert(collection.id, amount);
+			<IsAdmin<T>>::insert((collection.id, user), true);
 		} else {
-			<Allowlist<T>>::remove((collection.id, user.as_sub()));
+			<AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));
+			<IsAdmin<T>>::remove((collection.id, user));
 		}
 
 		Ok(())
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -36,8 +36,8 @@
 use sp_runtime::{sp_std::prelude::Vec};
 use nft_data_structs::{
 	MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, CUSTOM_DATA_LIMIT,
-	VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, COLLECTION_ADMINS_LIMIT,
-	OFFCHAIN_SCHEMA_LIMIT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+	VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, OFFCHAIN_SCHEMA_LIMIT,
+	FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
 	NFT_SPONSOR_TRANSFER_TIMEOUT, AccessMode, Collection, CreateItemData, CollectionLimits,
 	CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,
 };
@@ -156,7 +156,6 @@
 	where
 		origin: T::Origin
 	{
-		const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;
 		type Error = Error<T>;
 
 		fn on_initialize(_now: T::BlockNumber) -> Weight {
@@ -406,12 +405,9 @@
 		#[transactional]
 		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {
 			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
-
 			let collection = <CollectionHandle<T>>::try_get(collection_id)?;
-			collection.check_is_owner_or_admin(&sender)?;
 
-			<IsAdmin<T>>::insert((collection_id, new_admin_id.as_sub()), true);
-			Ok(())
+			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)
 		}
 
 		/// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.
@@ -430,12 +426,9 @@
 		#[transactional]
 		pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {
 			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
-
 			let collection = <CollectionHandle<T>>::try_get(collection_id)?;
-			collection.check_is_owner_or_admin(&sender)?;
 
-			<IsAdmin<T>>::remove((collection_id, account_id.as_sub()));
-			Ok(())
+			<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)
 		}
 
 		/// # Permissions
modifiedprimitives/nft/src/lib.rsdiffbeforeafterboth
before · primitives/nft/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23use core::convert::{TryFrom, TryInto};45#[cfg(feature = "serde")]6pub use serde::{Serialize, Deserialize};78use sp_core::U256;9use sp_runtime::{ArithmeticError, sp_std::prelude::Vec};10use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};11pub use frame_support::{12	BoundedVec, construct_runtime, decl_event, decl_module, decl_storage, decl_error,13	dispatch::DispatchResult,14	ensure, fail, parameter_types,15	traits::{16		Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,17		Randomness, IsSubType, WithdrawReasons,18	},19	weights::{20		constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},21		DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,22		WeightToFeePolynomial, DispatchClass,23	},24	StorageValue, transactional,25};26use derivative::Derivative;27use scale_info::TypeInfo;2829pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;30pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;31pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;32pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;3334pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {35	10000036} else {37	1038};39pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {40	204841} else {42	1043};44pub const COLLECTION_ADMINS_LIMIT: u64 = 5;45pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;46pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {47	100000048} else {49	1050};5152// Timeouts for item types in passed blocks53pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;54pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;55pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;5657// Schema limits58pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 1024;59pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;60pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;6162pub const MAX_COLLECTION_NAME_LENGTH: usize = 64;63pub const MAX_COLLECTION_DESCRIPTION_LENGTH: usize = 256;64pub const MAX_TOKEN_PREFIX_LENGTH: usize = 16;6566/// How much items can be created per single67/// create_many call68pub const MAX_ITEMS_PER_BATCH: u32 = 200;6970parameter_types! {71	pub const CustomDataLimit: u32 = CUSTOM_DATA_LIMIT;72}7374#[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Default, TypeInfo)]75#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]76pub struct CollectionId(pub u32);77impl EncodeLike<u32> for CollectionId {}78impl EncodeLike<CollectionId> for u32 {}7980#[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Default, TypeInfo)]81#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]82pub struct TokenId(pub u32);83impl EncodeLike<u32> for TokenId {}84impl EncodeLike<TokenId> for u32 {}8586impl TokenId {87	pub fn try_next(self) -> Result<TokenId, ArithmeticError> {88		self.089			.checked_add(1)90			.ok_or(ArithmeticError::Overflow)91			.map(Self)92	}93}9495impl From<TokenId> for U256 {96	fn from(t: TokenId) -> Self {97		t.0.into()98	}99}100101impl TryFrom<U256> for TokenId {102	type Error = &'static str;103104	fn try_from(value: U256) -> Result<Self, Self::Error> {105		Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))106	}107}108109pub struct OverflowError;110impl From<OverflowError> for &'static str {111	fn from(_: OverflowError) -> Self {112		"overflow occured"113	}114}115116pub type DecimalPoints = u8;117118#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo)]119#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]120pub enum CollectionMode {121	NFT,122	// decimal points123	Fungible(DecimalPoints),124	ReFungible,125}126127impl CollectionMode {128	pub fn id(&self) -> u8 {129		match self {130			CollectionMode::NFT => 1,131			CollectionMode::Fungible(_) => 2,132			CollectionMode::ReFungible => 3,133		}134	}135}136137pub trait SponsoringResolve<AccountId, Call> {138	fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;139}140141#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo)]142#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]143pub enum AccessMode {144	Normal,145	WhiteList,146}147impl Default for AccessMode {148	fn default() -> Self {149		Self::Normal150	}151}152153#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo)]154#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]155pub enum SchemaVersion {156	ImageURL,157	Unique,158}159impl Default for SchemaVersion {160	fn default() -> Self {161		Self::ImageURL162	}163}164165#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]166#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]167pub struct Ownership<AccountId> {168	pub owner: AccountId,169	pub fraction: u128,170}171172#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]173#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]174pub enum SponsorshipState<AccountId> {175	/// The fees are applied to the transaction sender176	Disabled,177	Unconfirmed(AccountId),178	/// Transactions are sponsored by specified account179	Confirmed(AccountId),180}181182impl<AccountId> SponsorshipState<AccountId> {183	pub fn sponsor(&self) -> Option<&AccountId> {184		match self {185			Self::Confirmed(sponsor) => Some(sponsor),186			_ => None,187		}188	}189190	pub fn pending_sponsor(&self) -> Option<&AccountId> {191		match self {192			Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),193			_ => None,194		}195	}196197	pub fn confirmed(&self) -> bool {198		matches!(self, Self::Confirmed(_))199	}200}201202impl<T> Default for SponsorshipState<T> {203	fn default() -> Self {204		Self::Disabled205	}206}207208#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]209#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]210pub struct Collection<T: frame_system::Config> {211	pub owner: T::AccountId,212	pub mode: CollectionMode,213	pub access: AccessMode,214	pub name: Vec<u16>,        // 64 include null escape char215	pub description: Vec<u16>, // 256 include null escape char216	pub token_prefix: Vec<u8>, // 16 include null escape char217	pub mint_mode: bool,218	pub offchain_schema: Vec<u8>,219	pub schema_version: SchemaVersion,220	pub sponsorship: SponsorshipState<T::AccountId>,221	pub limits: CollectionLimits,          // Collection private restrictions222	pub variable_on_chain_schema: Vec<u8>, //223	pub const_on_chain_schema: Vec<u8>,    //224	pub meta_update_permission: MetaUpdatePermission,225}226227#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]228#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]229pub struct NftItemType<AccountId> {230	pub owner: AccountId,231	pub const_data: Vec<u8>,232	pub variable_data: Vec<u8>,233}234235#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]236#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]237pub struct FungibleItemType {238	pub value: u128,239}240241#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]242#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]243pub struct ReFungibleItemType<AccountId> {244	pub owner: Vec<Ownership<AccountId>>,245	pub const_data: Vec<u8>,246	pub variable_data: Vec<u8>,247}248249#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo)]250#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]251pub struct CollectionLimits {252	pub account_token_ownership_limit: Option<u32>,253	pub sponsored_data_size: Option<u32>,254	/// None - setVariableMetadata is not sponsored255	/// Some(v) - setVariableMetadata is sponsored256	///           if there is v block between txs257	pub sponsored_data_rate_limit: Option<u32>,258	pub token_limit: Option<u32>,259260	// Timeouts for item types in passed blocks261	pub sponsor_transfer_timeout: Option<u32>,262	pub owner_can_transfer: Option<bool>,263	pub owner_can_destroy: Option<bool>,264	pub transfers_enabled: Option<bool>,265}266267impl CollectionLimits {268	pub fn account_token_ownership_limit(&self) -> u32 {269		self.account_token_ownership_limit270			.unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)271			.min(MAX_TOKEN_OWNERSHIP)272	}273	pub fn sponsored_data_size(&self) -> u32 {274		self.sponsored_data_size275			.unwrap_or(CUSTOM_DATA_LIMIT)276			.min(CUSTOM_DATA_LIMIT)277	}278	pub fn token_limit(&self) -> u32 {279		self.token_limit280			.unwrap_or(COLLECTION_TOKEN_LIMIT)281			.min(COLLECTION_TOKEN_LIMIT)282	}283	pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {284		self.sponsor_transfer_timeout285			.unwrap_or(default)286			.min(MAX_SPONSOR_TIMEOUT)287	}288	pub fn owner_can_transfer(&self) -> bool {289		self.owner_can_transfer.unwrap_or(true)290	}291	pub fn owner_can_destroy(&self) -> bool {292		self.owner_can_destroy.unwrap_or(true)293	}294	pub fn transfers_enabled(&self) -> bool {295		self.transfers_enabled.unwrap_or(true)296	}297	pub fn sponsored_data_rate_limit(&self) -> Option<u32> {298		self.sponsored_data_rate_limit299			.map(|v| v.min(MAX_SPONSOR_TIMEOUT))300	}301}302303/// BoundedVec doesn't supports serde304#[cfg(feature = "serde1")]305mod bounded_serde {306	use core::convert::TryFrom;307	use frame_support::{BoundedVec, traits::Get};308	use serde::{309		ser::{self, Serialize},310		de::{self, Deserialize, Error},311	};312	use sp_std::vec::Vec;313314	pub fn serialize<D, V, S>(value: &BoundedVec<V, S>, serializer: D) -> Result<D::Ok, D::Error>315	where316		D: ser::Serializer,317		V: Serialize,318	{319		let vec: &Vec<_> = &value;320		vec.serialize(serializer)321	}322323	pub fn deserialize<'de, D, V, S>(deserializer: D) -> Result<BoundedVec<V, S>, D::Error>324	where325		D: de::Deserializer<'de>,326		V: de::Deserialize<'de>,327		S: Get<u32>,328	{329		// TODO: Implement custom visitor, which will limit vec size at parse time? Will serde only be used by chainspec?330		let vec = <Vec<V>>::deserialize(deserializer)?;331		let len = vec.len();332		TryFrom::try_from(vec).map_err(|_| D::Error::invalid_length(len, &"lesser size"))333	}334}335336#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]337#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]338#[derivative(Debug)]339pub struct CreateNftData {340	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]341	#[derivative(Debug = "ignore")]342	pub const_data: BoundedVec<u8, CustomDataLimit>,343	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]344	#[derivative(Debug = "ignore")]345	pub variable_data: BoundedVec<u8, CustomDataLimit>,346}347348#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]349#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]350pub struct CreateFungibleData {351	pub value: u128,352}353354#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]355#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]356#[derivative(Debug)]357pub struct CreateReFungibleData {358	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]359	#[derivative(Debug = "ignore")]360	pub const_data: BoundedVec<u8, CustomDataLimit>,361	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]362	#[derivative(Debug = "ignore")]363	pub variable_data: BoundedVec<u8, CustomDataLimit>,364	pub pieces: u128,365}366367#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]368#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]369pub enum MetaUpdatePermission {370	ItemOwner,371	Admin,372	None,373}374375impl Default for MetaUpdatePermission {376	fn default() -> Self {377		Self::ItemOwner378	}379}380381#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]382#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]383pub enum CreateItemData {384	NFT(CreateNftData),385	Fungible(CreateFungibleData),386	ReFungible(CreateReFungibleData),387}388389impl CreateItemData {390	pub fn data_size(&self) -> usize {391		match self {392			CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),393			CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),394			_ => 0,395		}396	}397}398399impl From<CreateNftData> for CreateItemData {400	fn from(item: CreateNftData) -> Self {401		CreateItemData::NFT(item)402	}403}404405impl From<CreateReFungibleData> for CreateItemData {406	fn from(item: CreateReFungibleData) -> Self {407		CreateItemData::ReFungible(item)408	}409}410411impl From<CreateFungibleData> for CreateItemData {412	fn from(item: CreateFungibleData) -> Self {413		CreateItemData::Fungible(item)414	}415}
after · primitives/nft/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23use core::convert::{TryFrom, TryInto};45#[cfg(feature = "serde")]6pub use serde::{Serialize, Deserialize};78use sp_core::U256;9use sp_runtime::{ArithmeticError, sp_std::prelude::Vec};10use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};11pub use frame_support::{12	BoundedVec, construct_runtime, decl_event, decl_module, decl_storage, decl_error,13	dispatch::DispatchResult,14	ensure, fail, parameter_types,15	traits::{16		Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,17		Randomness, IsSubType, WithdrawReasons,18	},19	weights::{20		constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},21		DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,22		WeightToFeePolynomial, DispatchClass,23	},24	StorageValue, transactional,25};26use derivative::Derivative;27use scale_info::TypeInfo;2829pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;30pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;31pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;32pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;3334pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {35	10000036} else {37	1038};39pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {40	204841} else {42	1043};44pub const COLLECTION_ADMINS_LIMIT: u32 = 5;45pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;46pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {47	100000048} else {49	1050};5152// Timeouts for item types in passed blocks53pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;54pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;55pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;5657// Schema limits58pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 1024;59pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;60pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;6162pub const MAX_COLLECTION_NAME_LENGTH: usize = 64;63pub const MAX_COLLECTION_DESCRIPTION_LENGTH: usize = 256;64pub const MAX_TOKEN_PREFIX_LENGTH: usize = 16;6566/// How much items can be created per single67/// create_many call68pub const MAX_ITEMS_PER_BATCH: u32 = 200;6970parameter_types! {71	pub const CustomDataLimit: u32 = CUSTOM_DATA_LIMIT;72}7374#[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Default, TypeInfo)]75#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]76pub struct CollectionId(pub u32);77impl EncodeLike<u32> for CollectionId {}78impl EncodeLike<CollectionId> for u32 {}7980#[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Default, TypeInfo)]81#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]82pub struct TokenId(pub u32);83impl EncodeLike<u32> for TokenId {}84impl EncodeLike<TokenId> for u32 {}8586impl TokenId {87	pub fn try_next(self) -> Result<TokenId, ArithmeticError> {88		self.089			.checked_add(1)90			.ok_or(ArithmeticError::Overflow)91			.map(Self)92	}93}9495impl From<TokenId> for U256 {96	fn from(t: TokenId) -> Self {97		t.0.into()98	}99}100101impl TryFrom<U256> for TokenId {102	type Error = &'static str;103104	fn try_from(value: U256) -> Result<Self, Self::Error> {105		Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))106	}107}108109pub struct OverflowError;110impl From<OverflowError> for &'static str {111	fn from(_: OverflowError) -> Self {112		"overflow occured"113	}114}115116pub type DecimalPoints = u8;117118#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo)]119#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]120pub enum CollectionMode {121	NFT,122	// decimal points123	Fungible(DecimalPoints),124	ReFungible,125}126127impl CollectionMode {128	pub fn id(&self) -> u8 {129		match self {130			CollectionMode::NFT => 1,131			CollectionMode::Fungible(_) => 2,132			CollectionMode::ReFungible => 3,133		}134	}135}136137pub trait SponsoringResolve<AccountId, Call> {138	fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;139}140141#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo)]142#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]143pub enum AccessMode {144	Normal,145	WhiteList,146}147impl Default for AccessMode {148	fn default() -> Self {149		Self::Normal150	}151}152153#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo)]154#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]155pub enum SchemaVersion {156	ImageURL,157	Unique,158}159impl Default for SchemaVersion {160	fn default() -> Self {161		Self::ImageURL162	}163}164165#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]166#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]167pub struct Ownership<AccountId> {168	pub owner: AccountId,169	pub fraction: u128,170}171172#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]173#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]174pub enum SponsorshipState<AccountId> {175	/// The fees are applied to the transaction sender176	Disabled,177	Unconfirmed(AccountId),178	/// Transactions are sponsored by specified account179	Confirmed(AccountId),180}181182impl<AccountId> SponsorshipState<AccountId> {183	pub fn sponsor(&self) -> Option<&AccountId> {184		match self {185			Self::Confirmed(sponsor) => Some(sponsor),186			_ => None,187		}188	}189190	pub fn pending_sponsor(&self) -> Option<&AccountId> {191		match self {192			Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),193			_ => None,194		}195	}196197	pub fn confirmed(&self) -> bool {198		matches!(self, Self::Confirmed(_))199	}200}201202impl<T> Default for SponsorshipState<T> {203	fn default() -> Self {204		Self::Disabled205	}206}207208#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]209#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]210pub struct Collection<T: frame_system::Config> {211	pub owner: T::AccountId,212	pub mode: CollectionMode,213	pub access: AccessMode,214	pub name: Vec<u16>,        // 64 include null escape char215	pub description: Vec<u16>, // 256 include null escape char216	pub token_prefix: Vec<u8>, // 16 include null escape char217	pub mint_mode: bool,218	pub offchain_schema: Vec<u8>,219	pub schema_version: SchemaVersion,220	pub sponsorship: SponsorshipState<T::AccountId>,221	pub limits: CollectionLimits,          // Collection private restrictions222	pub variable_on_chain_schema: Vec<u8>, //223	pub const_on_chain_schema: Vec<u8>,    //224	pub meta_update_permission: MetaUpdatePermission,225}226227#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]228#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]229pub struct NftItemType<AccountId> {230	pub owner: AccountId,231	pub const_data: Vec<u8>,232	pub variable_data: Vec<u8>,233}234235#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]236#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]237pub struct FungibleItemType {238	pub value: u128,239}240241#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]242#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]243pub struct ReFungibleItemType<AccountId> {244	pub owner: Vec<Ownership<AccountId>>,245	pub const_data: Vec<u8>,246	pub variable_data: Vec<u8>,247}248249#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo)]250#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]251pub struct CollectionLimits {252	pub account_token_ownership_limit: Option<u32>,253	pub sponsored_data_size: Option<u32>,254	/// None - setVariableMetadata is not sponsored255	/// Some(v) - setVariableMetadata is sponsored256	///           if there is v block between txs257	pub sponsored_data_rate_limit: Option<u32>,258	pub token_limit: Option<u32>,259260	// Timeouts for item types in passed blocks261	pub sponsor_transfer_timeout: Option<u32>,262	pub owner_can_transfer: Option<bool>,263	pub owner_can_destroy: Option<bool>,264	pub transfers_enabled: Option<bool>,265}266267impl CollectionLimits {268	pub fn account_token_ownership_limit(&self) -> u32 {269		self.account_token_ownership_limit270			.unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)271			.min(MAX_TOKEN_OWNERSHIP)272	}273	pub fn sponsored_data_size(&self) -> u32 {274		self.sponsored_data_size275			.unwrap_or(CUSTOM_DATA_LIMIT)276			.min(CUSTOM_DATA_LIMIT)277	}278	pub fn token_limit(&self) -> u32 {279		self.token_limit280			.unwrap_or(COLLECTION_TOKEN_LIMIT)281			.min(COLLECTION_TOKEN_LIMIT)282	}283	pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {284		self.sponsor_transfer_timeout285			.unwrap_or(default)286			.min(MAX_SPONSOR_TIMEOUT)287	}288	pub fn owner_can_transfer(&self) -> bool {289		self.owner_can_transfer.unwrap_or(true)290	}291	pub fn owner_can_destroy(&self) -> bool {292		self.owner_can_destroy.unwrap_or(true)293	}294	pub fn transfers_enabled(&self) -> bool {295		self.transfers_enabled.unwrap_or(true)296	}297	pub fn sponsored_data_rate_limit(&self) -> Option<u32> {298		self.sponsored_data_rate_limit299			.map(|v| v.min(MAX_SPONSOR_TIMEOUT))300	}301}302303/// BoundedVec doesn't supports serde304#[cfg(feature = "serde1")]305mod bounded_serde {306	use core::convert::TryFrom;307	use frame_support::{BoundedVec, traits::Get};308	use serde::{309		ser::{self, Serialize},310		de::{self, Deserialize, Error},311	};312	use sp_std::vec::Vec;313314	pub fn serialize<D, V, S>(value: &BoundedVec<V, S>, serializer: D) -> Result<D::Ok, D::Error>315	where316		D: ser::Serializer,317		V: Serialize,318	{319		let vec: &Vec<_> = &value;320		vec.serialize(serializer)321	}322323	pub fn deserialize<'de, D, V, S>(deserializer: D) -> Result<BoundedVec<V, S>, D::Error>324	where325		D: de::Deserializer<'de>,326		V: de::Deserialize<'de>,327		S: Get<u32>,328	{329		// TODO: Implement custom visitor, which will limit vec size at parse time? Will serde only be used by chainspec?330		let vec = <Vec<V>>::deserialize(deserializer)?;331		let len = vec.len();332		TryFrom::try_from(vec).map_err(|_| D::Error::invalid_length(len, &"lesser size"))333	}334}335336#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]337#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]338#[derivative(Debug)]339pub struct CreateNftData {340	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]341	#[derivative(Debug = "ignore")]342	pub const_data: BoundedVec<u8, CustomDataLimit>,343	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]344	#[derivative(Debug = "ignore")]345	pub variable_data: BoundedVec<u8, CustomDataLimit>,346}347348#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]349#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]350pub struct CreateFungibleData {351	pub value: u128,352}353354#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]355#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]356#[derivative(Debug)]357pub struct CreateReFungibleData {358	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]359	#[derivative(Debug = "ignore")]360	pub const_data: BoundedVec<u8, CustomDataLimit>,361	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]362	#[derivative(Debug = "ignore")]363	pub variable_data: BoundedVec<u8, CustomDataLimit>,364	pub pieces: u128,365}366367#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]368#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]369pub enum MetaUpdatePermission {370	ItemOwner,371	Admin,372	None,373}374375impl Default for MetaUpdatePermission {376	fn default() -> Self {377		Self::ItemOwner378	}379}380381#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]382#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]383pub enum CreateItemData {384	NFT(CreateNftData),385	Fungible(CreateFungibleData),386	ReFungible(CreateReFungibleData),387}388389impl CreateItemData {390	pub fn data_size(&self) -> usize {391		match self {392			CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),393			CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),394			_ => 0,395		}396	}397}398399impl From<CreateNftData> for CreateItemData {400	fn from(item: CreateNftData) -> Self {401		CreateItemData::NFT(item)402	}403}404405impl From<CreateReFungibleData> for CreateItemData {406	fn from(item: CreateReFungibleData) -> Self {407		CreateItemData::ReFungible(item)408	}409}410411impl From<CreateFungibleData> for CreateItemData {412	fn from(item: CreateFungibleData) -> Self {413		CreateItemData::Fungible(item)414	}415}