git.delta.rocks / unique-network / refs/commits / 8ccb2682a57d

difftreelog

refactor make collection limits fields optional

Yaroslav Bolyukin2021-11-04parent: #579977f.patch.diff
in: master

8 files changed

modifiedpallets/common/src/lib.rsdiffbeforeafterboth
before · pallets/common/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23use core::ops::{Deref, DerefMut};4use sp_std::vec::Vec;5use account::CrossAccountId;6use frame_support::{7	dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo},8	ensure, fail,9	traits::{Imbalance, Get, Currency},10};11use nft_data_structs::{12	COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,13	MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_COLLECTION_NAME_LENGTH, MAX_TOKEN_PREFIX_LENGTH,14	MetaUpdatePermission, Pays, PostDispatchInfo, TokenId, Weight, WithdrawReasons,15};16pub use pallet::*;17use sp_core::H160;18use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};19pub mod account;20#[cfg(feature = "runtime-benchmarks")]21pub mod benchmarking;22pub mod erc;23pub mod eth;2425#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]26pub struct CollectionHandle<T: Config> {27	pub id: CollectionId,28	collection: Collection<T>,29	pub recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,30}31impl<T: Config> CollectionHandle<T> {32	pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {33		<CollectionById<T>>::get(id).map(|collection| Self {34			id,35			collection,36			recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(37				eth::collection_id_to_address(id),38				gas_limit,39			),40		})41	}42	pub fn new(id: CollectionId) -> Option<Self> {43		Self::new_with_gas_limit(id, u64::MAX)44	}45	pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {46		Ok(Self::new(id).ok_or_else(|| <Error<T>>::CollectionNotFound)?)47	}48	pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {49		self.recorder.log_sub(log)50	}51	pub fn log_infallible(&self, log: impl evm_coder::ToLog) {52		self.recorder.log_infallible(log)53	}54	#[allow(dead_code)]55	fn consume_gas(&self, gas: u64) -> DispatchResult {56		self.recorder.consume_gas_sub(gas)57	}58	pub fn consume_sload(&self) -> DispatchResult {59		self.recorder.consume_sload_sub()60	}61	pub fn consume_sstores(&self, amount: usize) -> DispatchResult {62		self.recorder.consume_sstores_sub(amount)63	}64	pub fn consume_sstore(&self) -> DispatchResult {65		self.recorder.consume_sstore_sub()66	}67	pub fn consume_log(&self, topics: usize, data: usize) -> DispatchResult {68		self.recorder.consume_log_sub(topics, data)69	}70	pub fn submit_logs(self) -> DispatchResult {71		self.recorder.submit_logs()72	}73	pub fn save(self) -> DispatchResult {74		self.recorder.submit_logs()?;75		<CollectionById<T>>::insert(self.id, self.collection);76		Ok(())77	}78}79impl<T: Config> Deref for CollectionHandle<T> {80	type Target = Collection<T>;8182	fn deref(&self) -> &Self::Target {83		&self.collection84	}85}8687impl<T: Config> DerefMut for CollectionHandle<T> {88	fn deref_mut(&mut self) -> &mut Self::Target {89		&mut self.collection90	}91}9293impl<T: Config> CollectionHandle<T> {94	pub fn check_is_owner(&self, subject: &T::CrossAccountId) -> DispatchResult {95		ensure!(*subject.as_sub() == self.owner, <Error<T>>::NoPermission);96		Ok(())97	}98	pub fn is_owner_or_admin(&self, subject: &T::CrossAccountId) -> Result<bool, DispatchError> {99		self.consume_sload()?;100101		Ok(*subject.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, subject.as_sub())))102	}103	pub fn check_is_owner_or_admin(&self, subject: &T::CrossAccountId) -> DispatchResult {104		ensure!(self.is_owner_or_admin(subject)?, <Error<T>>::NoPermission);105		Ok(())106	}107	pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> Result<bool, DispatchError> {108		Ok(self.limits.owner_can_transfer && self.is_owner_or_admin(user)?)109	}110	pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> Result<bool, DispatchError> {111		Ok(self.limits.owner_can_transfer && self.is_owner_or_admin(user)?)112	}113	pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {114		self.consume_sload()?;115116		ensure!(117			<Allowlist<T>>::get((self.id, user.as_sub())),118			<Error<T>>::AddressNotInAllowlist119		);120		Ok(())121	}122123	pub fn check_can_update_meta(124		&self,125		subject: &T::CrossAccountId,126		item_owner: &T::CrossAccountId,127	) -> DispatchResult {128		match self.meta_update_permission {129			MetaUpdatePermission::ItemOwner => {130				ensure!(subject == item_owner, <Error<T>>::NoPermission);131				Ok(())132			}133			MetaUpdatePermission::Admin => self.check_is_owner_or_admin(subject),134			MetaUpdatePermission::None => fail!(<Error<T>>::NoPermission),135		}136	}137}138139#[frame_support::pallet]140pub mod pallet {141	use super::*;142	use frame_support::{pallet_prelude::*};143	use frame_support::{Blake2_128Concat, storage::Key};144	use account::{EvmBackwardsAddressMapping, CrossAccountId};145	use frame_support::traits::Currency;146	use nft_data_structs::TokenId;147	use scale_info::TypeInfo;148149	#[pallet::config]150	pub trait Config: frame_system::Config + pallet_evm_coder_substrate::Config + TypeInfo {151		type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;152153		type CrossAccountId: CrossAccountId<Self::AccountId>;154155		type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;156		type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;157158		type Currency: Currency<Self::AccountId>;159		type CollectionCreationPrice: Get<160			<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,161		>;162		type TreasuryAccountId: Get<Self::AccountId>;163	}164165	#[pallet::pallet]166	#[pallet::generate_store(pub(super) trait Store)]167	pub struct Pallet<T>(_);168169	#[pallet::event]170	#[pallet::generate_deposit(pub fn deposit_event)]171	pub enum Event<T: Config> {172		/// New collection was created173		///174		/// # Arguments175		///176		/// * collection_id: Globally unique identifier of newly created collection.177		///178		/// * mode: [CollectionMode] converted into u8.179		///180		/// * account_id: Collection owner.181		CollectionCreated(CollectionId, u8, T::AccountId),182183		/// New item was created.184		///185		/// # Arguments186		///187		/// * collection_id: Id of the collection where item was created.188		///189		/// * item_id: Id of an item. Unique within the collection.190		///191		/// * recipient: Owner of newly created item192		///193		/// * amount: Always 1 for NFT194		ItemCreated(CollectionId, TokenId, T::CrossAccountId, u128),195196		/// Collection item was burned.197		///198		/// # Arguments199		///200		/// * collection_id.201		///202		/// * item_id: Identifier of burned NFT.203		///204		/// * owner: which user has destroyed its tokens205		///206		/// * amount: Always 1 for NFT207		ItemDestroyed(CollectionId, TokenId, T::CrossAccountId, u128),208209		/// Item was transferred210		///211		/// * collection_id: Id of collection to which item is belong212		///213		/// * item_id: Id of an item214		///215		/// * sender: Original owner of item216		///217		/// * recipient: New owner of item218		///219		/// * amount: Always 1 for NFT220		Transfer(221			CollectionId,222			TokenId,223			T::CrossAccountId,224			T::CrossAccountId,225			u128,226		),227228		/// * collection_id229		///230		/// * item_id231		///232		/// * sender233		///234		/// * spender235		///236		/// * amount237		Approved(238			CollectionId,239			TokenId,240			T::CrossAccountId,241			T::CrossAccountId,242			u128,243		),244	}245246	#[pallet::error]247	pub enum Error<T> {248		/// This collection does not exist.249		CollectionNotFound,250		/// Sender parameter and item owner must be equal.251		MustBeTokenOwner,252		/// No permission to perform action253		NoPermission,254		/// Collection is not in mint mode.255		PublicMintingNotAllowed,256		/// Address is not in white list.257		AddressNotInAllowlist,258259		/// Collection name can not be longer than 63 char.260		CollectionNameLimitExceeded,261		/// Collection description can not be longer than 255 char.262		CollectionDescriptionLimitExceeded,263		/// Token prefix can not be longer than 15 char.264		CollectionTokenPrefixLimitExceeded,265		/// Total collections bound exceeded.266		TotalCollectionsLimitExceeded,267		/// variable_data exceeded data limit.268		TokenVariableDataLimitExceeded,269270		/// Collection settings not allowing items transferring271		TransferNotAllowed,272		/// Account token limit exceeded per collection273		AccountTokenLimitExceeded,274		/// Collection token limit exceeded275		CollectionTokenLimitExceeded,276		/// Metadata flag frozen277		MetadataFlagFrozen,278279		/// Item not exists.280		TokenNotFound,281		/// Item balance not enough.282		TokenValueTooLow,283		/// Requested value more than approved.284		TokenValueNotEnough,285		/// Tried to approve more than owned286		CantApproveMoreThanOwned,287288		/// Can't transfer tokens to ethereum zero address289		AddressIsZero,290		/// Target collection doesn't supports this operation291		UnsupportedOperation,292	}293294	#[pallet::storage]295	pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;296	#[pallet::storage]297	pub type DestroyedCollectionCount<T> =298		StorageValue<Value = CollectionId, QueryKind = ValueQuery>;299300	/// Collection info301	#[pallet::storage]302	pub type CollectionById<T> = StorageMap<303		Hasher = Blake2_128Concat,304		Key = CollectionId,305		Value = Collection<T>,306		QueryKind = OptionQuery,307	>;308309	/// List of collection admins310	#[pallet::storage]311	pub type IsAdmin<T: Config> = StorageNMap<312		Key = (313			Key<Blake2_128Concat, CollectionId>,314			Key<Blake2_128Concat, T::AccountId>,315		),316		Value = bool,317		QueryKind = ValueQuery,318	>;319320	/// Allowlisted collection users321	#[pallet::storage]322	pub type Allowlist<T: Config> = StorageNMap<323		Key = (324			Key<Blake2_128Concat, CollectionId>,325			Key<Blake2_128Concat, T::AccountId>,326		),327		Value = bool,328		QueryKind = ValueQuery,329	>;330}331332impl<T: Config> Pallet<T> {333	/// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens334	pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {335		ensure!(336			&T::CrossAccountId::from_eth(H160([0; 20])) != receiver,337			<Error<T>>::AddressIsZero338		);339		Ok(())340	}341}342343impl<T: Config> Pallet<T> {344	pub fn init_collection(data: Collection<T>) -> Result<CollectionId, DispatchError> {345		{346			ensure!(347				data.name.len() <= MAX_COLLECTION_NAME_LENGTH,348				Error::<T>::CollectionNameLimitExceeded349			);350			ensure!(351				data.description.len() <= MAX_COLLECTION_DESCRIPTION_LENGTH,352				Error::<T>::CollectionDescriptionLimitExceeded353			);354			ensure!(355				data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH,356				Error::<T>::CollectionTokenPrefixLimitExceeded357			);358		}359360		let created_count = <CreatedCollectionCount<T>>::get()361			.0362			.checked_add(1)363			.ok_or(ArithmeticError::Overflow)?;364		let destroyed_count = <DestroyedCollectionCount<T>>::get().0;365		let id = CollectionId(created_count);366367		// bound Total number of collections368		ensure!(369			created_count - destroyed_count < COLLECTION_NUMBER_LIMIT,370			<Error<T>>::TotalCollectionsLimitExceeded371		);372373		// =========374375		// Take a (non-refundable) deposit of collection creation376		{377			let mut imbalance =378				<<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();379			imbalance.subsume(380				<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(381					&T::TreasuryAccountId::get(),382					T::CollectionCreationPrice::get(),383				),384			);385			<T as Config>::Currency::settle(386				&data.owner,387				imbalance,388				WithdrawReasons::TRANSFER,389				ExistenceRequirement::KeepAlive,390			)391			.map_err(|_| Error::<T>::NoPermission)?;392		}393394		<CreatedCollectionCount<T>>::put(created_count);395		<Pallet<T>>::deposit_event(Event::CollectionCreated(396			id,397			data.mode.id(),398			data.owner.clone(),399		));400		<CollectionById<T>>::insert(id, data);401		Ok(id)402	}403404	pub fn destroy_collection(405		collection: CollectionHandle<T>,406		sender: &T::CrossAccountId,407	) -> DispatchResult {408		if !collection.limits.owner_can_destroy {409			fail!(Error::<T>::NoPermission);410		}411		collection.check_is_owner(&sender)?;412413		let destroyed_collections = <DestroyedCollectionCount<T>>::get()414			.0415			.checked_add(1)416			.ok_or(ArithmeticError::Overflow)?;417418		// =========419420		<DestroyedCollectionCount<T>>::put(destroyed_collections);421		<CollectionById<T>>::remove(collection.id);422		<IsAdmin<T>>::remove_prefix((collection.id,), None);423		<Allowlist<T>>::remove_prefix((collection.id,), None);424		Ok(())425	}426427	pub fn toggle_allowlist(428		collection: &CollectionHandle<T>,429		sender: &T::CrossAccountId,430		user: &T::CrossAccountId,431		allowed: bool,432	) -> DispatchResult {433		collection.check_is_owner_or_admin(&sender)?;434435		// =========436437		if allowed {438			<Allowlist<T>>::insert((collection.id, user.as_sub()), true);439		} else {440			<Allowlist<T>>::remove((collection.id, user.as_sub()));441		}442443		Ok(())444	}445}446447#[macro_export]448macro_rules! unsupported {449	() => {450		Err(<Error<T>>::UnsupportedOperation.into())451	};452}453454/// Worst cases455pub trait CommonWeightInfo {456	fn create_item() -> Weight;457	fn create_multiple_items(amount: u32) -> Weight;458	fn burn_item() -> Weight;459	fn transfer() -> Weight;460	fn approve() -> Weight;461	fn transfer_from() -> Weight;462	fn burn_from() -> Weight;463	fn set_variable_metadata(bytes: u32) -> Weight;464}465466pub trait CommonCollectionOperations<T: Config> {467	fn create_item(468		&self,469		sender: T::CrossAccountId,470		to: T::CrossAccountId,471		data: CreateItemData,472	) -> DispatchResultWithPostInfo;473	fn create_multiple_items(474		&self,475		sender: T::CrossAccountId,476		to: T::CrossAccountId,477		data: Vec<CreateItemData>,478	) -> DispatchResultWithPostInfo;479	fn burn_item(480		&self,481		sender: T::CrossAccountId,482		token: TokenId,483		amount: u128,484	) -> DispatchResultWithPostInfo;485486	fn transfer(487		&self,488		sender: T::CrossAccountId,489		to: T::CrossAccountId,490		token: TokenId,491		amount: u128,492	) -> DispatchResultWithPostInfo;493	fn approve(494		&self,495		sender: T::CrossAccountId,496		spender: T::CrossAccountId,497		token: TokenId,498		amount: u128,499	) -> DispatchResultWithPostInfo;500	fn transfer_from(501		&self,502		sender: T::CrossAccountId,503		from: T::CrossAccountId,504		to: T::CrossAccountId,505		token: TokenId,506		amount: u128,507	) -> DispatchResultWithPostInfo;508	fn burn_from(509		&self,510		sender: T::CrossAccountId,511		from: T::CrossAccountId,512		token: TokenId,513		amount: u128,514	) -> DispatchResultWithPostInfo;515516	fn set_variable_metadata(517		&self,518		sender: T::CrossAccountId,519		token: TokenId,520		data: Vec<u8>,521	) -> DispatchResultWithPostInfo;522523	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;524	fn token_exists(&self, token: TokenId) -> bool;525	fn last_token_id(&self) -> TokenId;526527	fn token_owner(&self, token: TokenId) -> T::CrossAccountId;528	fn const_metadata(&self, token: TokenId) -> Vec<u8>;529	fn variable_metadata(&self, token: TokenId) -> Vec<u8>;530531	/// How many tokens collection contains (Applicable to nonfungible/refungible)532	fn collection_tokens(&self) -> u32;533	/// Amount of different tokens account has (Applicable to nonfungible/refungible)534	fn account_balance(&self, account: T::CrossAccountId) -> u32;535	/// Amount of specific token account have (Applicable to fungible/refungible)536	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;537	fn allowance(538		&self,539		sender: T::CrossAccountId,540		spender: T::CrossAccountId,541		token: TokenId,542	) -> u128;543}544545// Flexible enough for implementing CommonCollectionOperations546pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {547	let post_info = PostDispatchInfo {548		actual_weight: Some(weight),549		pays_fee: Pays::Yes,550	};551	match res {552		Ok(()) => Ok(post_info),553		Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),554	}555}
modifiedpallets/fungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -157,8 +157,8 @@
 		amount: u128,
 	) -> DispatchResult {
 		ensure!(
-			collection.transfers_enabled,
-			<CommonError<T>>::TransferNotAllowed
+			collection.limits.transfers_enabled(),
+			<CommonError<T>>::TransferNotAllowed,
 		);
 
 		if collection.access == AccessMode::WhiteList {
modifiedpallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth
--- a/pallets/nft/src/eth/sponsoring.rs
+++ b/pallets/nft/src/eth/sponsoring.rs
@@ -43,11 +43,8 @@
 					let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;
 					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
 					let collection_limits = &collection.limits;
-					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
-						collection_limits.sponsor_transfer_timeout
-					} else {
-						NFT_SPONSOR_TRANSFER_TIMEOUT
-					};
+					let limit =
+						collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);
 
 					let mut sponsor = true;
 					if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {
@@ -74,11 +71,8 @@
 				UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {
 					let who = T::CrossAccountId::from_eth(*caller);
 					let collection_limits = &collection.limits;
-					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
-						collection_limits.sponsor_transfer_timeout
-					} else {
-						FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT
-					};
+					let limit = collection_limits
+						.sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
 
 					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
 					let mut sponsored = true;
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -37,8 +37,9 @@
 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, AccessMode, Collection, CreateItemData, CollectionLimits, CollectionId,
-	CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,
+	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,
 };
 use pallet_common::{
 	account::CrossAccountId, CollectionHandle, IsAdmin, Pallet as PalletCommon,
@@ -188,11 +189,6 @@
 
 			// Anyone can create a collection
 			let who = ensure_signed(origin)?;
-
-			let limits = CollectionLimits::<T::BlockNumber> {
-				sponsored_data_size: CUSTOM_DATA_LIMIT,
-				..Default::default()
-			};
 
 			// Create new collection
 			let new_collection = Collection::<T> {
@@ -208,8 +204,7 @@
 				sponsorship: SponsorshipState::Disabled,
 				variable_on_chain_schema: Vec::new(),
 				const_on_chain_schema: Vec::new(),
-				limits,
-				transfers_enabled: true,
+				limits: Default::default(),
 				meta_update_permission: Default::default(),
 			};
 
@@ -582,7 +577,7 @@
 
 			// =========
 
-			target_collection.transfers_enabled = value;
+			target_collection.limits.transfers_enabled = Some(value);
 			target_collection.save()
 		}
 
@@ -888,30 +883,63 @@
 		pub fn set_collection_limits(
 			origin,
 			collection_id: CollectionId,
-			new_limits: CollectionLimits<T::BlockNumber>,
+			new_limit: CollectionLimits,
 		) -> DispatchResult {
+			let mut new_limit = new_limit;
 			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
 			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
 			target_collection.check_is_owner(&sender)?;
-			let old_limits = &target_collection.limits;
+			let old_limit = &target_collection.limits;
 
-			// collection bounds
-			ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&
-				new_limits.account_token_ownership_limit.unwrap_or(0) <= MAX_TOKEN_OWNERSHIP &&
-				new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,
-				Error::<T>::CollectionLimitBoundsExceeded);
+			macro_rules! limit_default {
+				($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{
+					$(
+						if let Some($new) = $new.$field {
+							let $old = $old.$field($($arg)?);
+							let _ = $new;
+							let _ = $old;
+							$check
+						} else {
+							$new.$field = $old.$field
+						}
+					)*
+				}};
+			}
 
-			// token_limit   check  prev
-			ensure!(old_limits.token_limit >= new_limits.token_limit, <CommonError<T>>::CollectionTokenLimitExceeded);
-			ensure!(new_limits.token_limit > 0, <CommonError<T>>::CollectionTokenLimitExceeded);
-
-			ensure!(
-				(old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&
-				(old_limits.owner_can_destroy || !new_limits.owner_can_destroy),
-				Error::<T>::OwnerPermissionsCantBeReverted,
+			limit_default!(old_limit, new_limit,
+				account_token_ownership_limit => ensure!(
+					new_limit <= MAX_TOKEN_OWNERSHIP,
+					<Error<T>>::CollectionLimitBoundsExceeded,
+				),
+				sponsor_transfer_timeout(match target_collection.mode {
+					CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
+					CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+					CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+				}) => ensure!(
+					new_limit <= MAX_SPONSOR_TIMEOUT,
+					<Error<T>>::CollectionLimitBoundsExceeded,
+				),
+				sponsored_data_size => ensure!(
+					new_limit <= CUSTOM_DATA_LIMIT,
+					<Error<T>>::CollectionLimitBoundsExceeded,
+				),
+				token_limit => ensure!(
+					old_limit >= new_limit && new_limit > 0,
+					<CommonError<T>>::CollectionTokenLimitExceeded
+				),
+				owner_can_transfer => ensure!(
+					old_limit || !new_limit,
+					<Error<T>>::OwnerPermissionsCantBeReverted,
+				),
+				owner_can_destroy => ensure!(
+					old_limit || !new_limit,
+					<Error<T>>::OwnerPermissionsCantBeReverted,
+				),
+				sponsored_data_rate_limit => {},
+				transfers_enabled => {},
 			);
 
-			target_collection.limits = new_limits;
+			target_collection.limits = new_limit;
 
 			target_collection.save()
 		}
modifiedpallets/nft/src/sponsorship.rsdiffbeforeafterboth
--- a/pallets/nft/src/sponsorship.rs
+++ b/pallets/nft/src/sponsorship.rs
@@ -26,7 +26,13 @@
 		// sponsor timeout
 		let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
 
-		let limit = collection.limits.sponsor_transfer_timeout;
+		let limit = collection
+			.limits
+			.sponsor_transfer_timeout(match _properties {
+				CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,
+				CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+				CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+			});
 		if CreateItemBasket::<T>::contains_key((collection_id, &who)) {
 			let last_tx_block = CreateItemBasket::<T>::get((collection_id, &who));
 			let limit_time = last_tx_block + limit.into();
@@ -37,7 +43,7 @@
 		CreateItemBasket::<T>::insert((collection_id, who.clone()), block_number);
 
 		// check free create limit
-		if collection.limits.sponsored_data_size >= (_properties.data_size() as u32) {
+		if collection.limits.sponsored_data_size() >= (_properties.data_size() as u32) {
 			collection.sponsorship.sponsor().cloned()
 		} else {
 			None
@@ -61,11 +67,8 @@
 			sponsor_transfer = match collection_mode {
 				CollectionMode::NFT => {
 					// get correct limit
-					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
-						collection_limits.sponsor_transfer_timeout
-					} else {
-						NFT_SPONSOR_TRANSFER_TIMEOUT
-					};
+					let limit =
+						collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);
 
 					let mut sponsored = true;
 					if NftTransferBasket::<T>::contains_key(collection_id, item_id) {
@@ -83,11 +86,8 @@
 				}
 				CollectionMode::Fungible(_) => {
 					// get correct limit
-					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
-						collection_limits.sponsor_transfer_timeout
-					} else {
-						FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT
-					};
+					let limit = collection_limits
+						.sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
 
 					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
 					let mut sponsored = true;
@@ -106,11 +106,8 @@
 				}
 				CollectionMode::ReFungible => {
 					// get correct limit
-					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
-						collection_limits.sponsor_transfer_timeout
-					} else {
-						REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT
-					};
+					let limit = collection_limits
+						.sponsor_transfer_timeout(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
 
 					let mut sponsored = true;
 					if ReFungibleTransferBasket::<T>::contains_key(collection_id, item_id) {
@@ -150,13 +147,13 @@
 			// Can't sponsor fungible collection, this tx will be rejected
 			// as invalid
 			!matches!(collection.mode, CollectionMode::Fungible(_)) &&
-			data.len() <= collection.limits.sponsored_data_size as usize
+			data.len() <= collection.limits.sponsored_data_size() as usize
 		{
-			if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {
+			if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit() {
 				let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
 
 				if VariableMetaDataBasket::<T>::get(collection_id, item_id)
-					.map(|last_block| block_number - last_block > rate_limit)
+					.map(|last_block| block_number - last_block > rate_limit.into())
 					.unwrap_or(true)
 				{
 					sponsor_metadata_changes = true;
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -164,7 +164,7 @@
 			.ok_or_else(|| <CommonError<T>>::TokenNotFound)?;
 		ensure!(
 			&token_data.owner == sender
-				|| (collection.limits.owner_can_transfer
+				|| (collection.limits.owner_can_transfer()
 					&& collection.is_owner_or_admin(sender)?),
 			<CommonError<T>>::NoPermission
 		);
@@ -215,7 +215,7 @@
 		token: TokenId,
 	) -> DispatchResult {
 		ensure!(
-			collection.transfers_enabled,
+			collection.limits.transfers_enabled(),
 			<CommonError<T>>::TransferNotAllowed
 		);
 
@@ -223,7 +223,8 @@
 			.ok_or_else(|| <CommonError<T>>::TokenNotFound)?;
 		ensure!(
 			&token_data.owner == from
-				|| (collection.limits.owner_can_transfer && collection.is_owner_or_admin(from)?),
+				|| (collection.limits.owner_can_transfer()
+					&& collection.is_owner_or_admin(from)?),
 			<CommonError<T>>::NoPermission
 		);
 
@@ -327,7 +328,7 @@
 			.checked_add(data.len() as u32)
 			.ok_or(ArithmeticError::Overflow)?;
 		ensure!(
-			tokens_minted < collection.limits.token_limit,
+			tokens_minted < collection.limits.token_limit(),
 			<CommonError<T>>::CollectionTokenLimitExceeded
 		);
 		collection.consume_sstore()?;
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -268,7 +268,7 @@
 		amount: u128,
 	) -> DispatchResult {
 		ensure!(
-			collection.transfers_enabled,
+			collection.limits.transfers_enabled(),
 			<CommonError<T>>::TransferNotAllowed
 		);
 
@@ -404,7 +404,7 @@
 			.checked_add(data.len() as u32)
 			.ok_or(ArithmeticError::Overflow)?;
 		ensure!(
-			tokens_minted < collection.limits.token_limit,
+			tokens_minted < collection.limits.token_limit(),
 			<CommonError<T>>::CollectionTokenLimitExceeded
 		);
 
modifiedprimitives/nft/src/lib.rsdiffbeforeafterboth
--- a/primitives/nft/src/lib.rs
+++ b/primitives/nft/src/lib.rs
@@ -42,6 +42,7 @@
 	10
 };
 pub const COLLECTION_ADMINS_LIMIT: u64 = 5;
+pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;
 pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {
 	1000000
 } else {
@@ -217,11 +218,10 @@
 	pub offchain_schema: Vec<u8>,
 	pub schema_version: SchemaVersion,
 	pub sponsorship: SponsorshipState<T::AccountId>,
-	pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions
-	pub variable_on_chain_schema: Vec<u8>,        //
-	pub const_on_chain_schema: Vec<u8>,           //
+	pub limits: CollectionLimits,          // Collection private restrictions
+	pub variable_on_chain_schema: Vec<u8>, //
+	pub const_on_chain_schema: Vec<u8>,    //
 	pub meta_update_permission: MetaUpdatePermission,
-	pub transfers_enabled: bool,
 }
 
 #[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]
@@ -246,42 +246,57 @@
 	pub variable_data: Vec<u8>,
 }
 
-#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]
+#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo)]
 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
-pub struct CollectionLimits<BlockNumber: Encode + Decode> {
+pub struct CollectionLimits {
 	pub account_token_ownership_limit: Option<u32>,
-	pub sponsored_data_size: u32,
+	pub sponsored_data_size: Option<u32>,
 	/// None - setVariableMetadata is not sponsored
 	/// Some(v) - setVariableMetadata is sponsored
 	///           if there is v block between txs
-	pub sponsored_data_rate_limit: Option<BlockNumber>,
-	pub token_limit: u32,
+	pub sponsored_data_rate_limit: Option<u32>,
+	pub token_limit: Option<u32>,
 
 	// Timeouts for item types in passed blocks
-	pub sponsor_transfer_timeout: u32,
-	pub owner_can_transfer: bool,
-	pub owner_can_destroy: bool,
+	pub sponsor_transfer_timeout: Option<u32>,
+	pub owner_can_transfer: Option<bool>,
+	pub owner_can_destroy: Option<bool>,
+	pub transfers_enabled: Option<bool>,
 }
 
-impl<BlockNumber: Encode + Decode> CollectionLimits<BlockNumber> {
+impl CollectionLimits {
 	pub fn account_token_ownership_limit(&self) -> u32 {
 		self.account_token_ownership_limit
 			.unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)
-			.min(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)
+			.min(MAX_TOKEN_OWNERSHIP)
 	}
-}
-
-impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {
-	fn default() -> Self {
-		Self {
-			account_token_ownership_limit: Some(10_000_000),
-			token_limit: u32::max_value(),
-			sponsored_data_size: u32::MAX,
-			sponsored_data_rate_limit: None,
-			sponsor_transfer_timeout: 14400,
-			owner_can_transfer: true,
-			owner_can_destroy: true,
-		}
+	pub fn sponsored_data_size(&self) -> u32 {
+		self.sponsored_data_size
+			.unwrap_or(CUSTOM_DATA_LIMIT)
+			.min(CUSTOM_DATA_LIMIT)
+	}
+	pub fn token_limit(&self) -> u32 {
+		self.token_limit
+			.unwrap_or(COLLECTION_TOKEN_LIMIT)
+			.min(COLLECTION_TOKEN_LIMIT)
+	}
+	pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {
+		self.sponsor_transfer_timeout
+			.unwrap_or(default)
+			.min(MAX_SPONSOR_TIMEOUT)
+	}
+	pub fn owner_can_transfer(&self) -> bool {
+		self.owner_can_transfer.unwrap_or(true)
+	}
+	pub fn owner_can_destroy(&self) -> bool {
+		self.owner_can_destroy.unwrap_or(true)
+	}
+	pub fn transfers_enabled(&self) -> bool {
+		self.transfers_enabled.unwrap_or(true)
+	}
+	pub fn sponsored_data_rate_limit(&self) -> Option<u32> {
+		self.sponsored_data_rate_limit
+			.map(|v| v.min(MAX_SPONSOR_TIMEOUT))
 	}
 }