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

difftreelog

CORE-386 Add methodt to evm

Trubnikov Sergey2022-05-31parent: #9e257c4.patch.diff
in: master

8 files changed

modifiedpallets/common/src/erc.rsdiffbeforeafterboth
--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -47,8 +47,9 @@
 }
 
 #[solidity_interface(name = "Collection")]
-impl<T: Config> CollectionHandle<T>
-// where
+
+impl<T: Config> CollectionHandle<T> 
+// where 
 // 	T::AccountId: From<H256>
 {
 	fn set_collection_property(&mut self, caller: caller, key: string, value: bytes) -> Result<()> {
@@ -192,7 +193,7 @@
 	// 	Ok(())
 	// }
 
-	fn add_collection_admin(&self, caller: caller, new_admin: address) -> Result<void> {
+	fn add_admin(&self, caller: caller, new_admin: address) -> Result<void> {
 		let caller = T::CrossAccountId::from_eth(caller);
 		self.check_is_owner_or_admin(&caller)
 			.map_err(dispatch_to_evm::<T>)?;
@@ -202,16 +203,17 @@
 		Ok(())
 	}
 
-	fn remove_collection_admin(&self, caller: caller, admin: address) -> Result<void> {
+	fn remove_admin(&self, caller: caller, admin: address) -> Result<void> {
 		let caller = T::CrossAccountId::from_eth(caller);
 		self.check_is_owner_or_admin(&caller)
 			.map_err(dispatch_to_evm::<T>)?;
 		let admin = T::CrossAccountId::from_eth(admin);
-		<Pallet<T>>::toggle_admin(&self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;
+		<Pallet<T>>::toggle_admin(&self, &caller, &admin, false)
+			.map_err(dispatch_to_evm::<T>)?;
 		Ok(())
 	}
 
-	#[solidity(rename_selector = "setCollectionNesting")]
+	#[solidity(rename_selector = "setNesting")]
 	fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {
 		let caller = T::CrossAccountId::from_eth(caller);
 		self.check_is_owner_or_admin(&caller)
@@ -224,22 +226,13 @@
 		Ok(())
 	}
 
-	#[solidity(rename_selector = "setCollectionNesting")]
-	fn set_nesting(
-		&mut self,
-		caller: caller,
-		enable: bool,
-		collections: Vec<address>,
-	) -> Result<void> {
+	#[solidity(rename_selector = "setNesting")]
+	fn set_nesting(&mut self, caller: caller, enable: bool, collections: Vec<address>) -> Result<void> {
 		if collections.is_empty() {
 			return Err("No addresses provided".into());
 		}
 		if collections.len() >= OwnerRestrictedSet::bound() {
-			return Err(Error::Revert(format!(
-				"Out of bound: {} >= {}",
-				collections.len(),
-				OwnerRestrictedSet::bound()
-			)));
+			return Err(Error::Revert(format!("Out of bound: {} >= {}", collections.len(), OwnerRestrictedSet::bound())));
 		}
 		let caller = T::CrossAccountId::from_eth(caller);
 		self.check_is_owner_or_admin(&caller)
@@ -249,48 +242,14 @@
 			true => {
 				let mut bv = OwnerRestrictedSet::new();
 				for i in collections {
-					bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or(Error::Revert(
-						"Can't convert address into collection id".into(),
-					))?)
-					.map_err(|e| Error::Revert(format!("{:?}", e)))?;
+					bv.try_insert(
+						crate::eth::map_eth_to_id(&i)
+							.ok_or(Error::Revert("Can't convert address into collection id".into()))?
+					).map_err(|e| Error::Revert(format!("{:?}", e)))?;
 				}
-				NestingRule::OwnerRestricted(bv)
+				NestingRule::OwnerRestricted (bv)
 			}
-		});
-		save(self);
-		Ok(())
-	}
-
-	fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {
-		let caller = T::CrossAccountId::from_eth(caller);
-		self.check_is_owner_or_admin(&caller)
-			.map_err(dispatch_to_evm::<T>)?;
-		self.collection.permissions.access = Some(match mode {
-			0 => AccessMode::Normal,
-			1 => AccessMode::AllowList,
-			_ => return Err("Not supported access mode".into()),
 		});
-		save(self);
-		Ok(())
-	}
-
-	fn add_to_collection_allow_list(&self, caller: caller, user: address) -> Result<void> {
-		let caller = check_is_owner_or_admin(caller, self)?;
-		let user = T::CrossAccountId::from_eth(user);
-		<Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;
-		Ok(())
-	}
-
-	fn remove_from_collection_allow_list(&self, caller: caller, user: address) -> Result<void> {
-		let caller = check_is_owner_or_admin(caller, self)?;
-		let user = T::CrossAccountId::from_eth(user);
-		<Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;
-		Ok(())
-	}
-
-	fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {
-		check_is_owner_or_admin(caller, self)?;
-		self.collection.permissions.mint_mode = Some(mode);
 		save(self);
 		Ok(())
 	}
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
before · pallets/common/src/lib.rs
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)]1819extern crate alloc;2021use core::ops::{Deref, DerefMut};22use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};23use sp_std::vec::Vec;24use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};25use evm_coder::ToLog;26use frame_support::{27	dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Weight, PostDispatchInfo},28	ensure,29	traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},30	weights::Pays,31	transactional,32};33use pallet_evm::GasWeightMapping;34use up_data_structs::{35	COLLECTION_NUMBER_LIMIT,36	Collection,37	RpcCollection,38	CollectionId,39	CreateItemData,40	MAX_TOKEN_PREFIX_LENGTH,41	COLLECTION_ADMINS_LIMIT,42	TokenId,43	TokenChild,44	CollectionStats,45	MAX_TOKEN_OWNERSHIP,46	CollectionMode,47	NFT_SPONSOR_TRANSFER_TIMEOUT,48	FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,49	REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,50	MAX_SPONSOR_TIMEOUT,51	CUSTOM_DATA_LIMIT,52	CollectionLimits,53	CreateCollectionData,54	SponsorshipState,55	CreateItemExData,56	SponsoringRateLimit,57	budget::Budget,58	PhantomType,59	Property,60	Properties,61	PropertiesPermissionMap,62	PropertyKey,63	PropertyValue,64	PropertyPermission,65	PropertiesError,66	PropertyKeyPermission,67	TokenData,68	TrySetProperty,69	PropertyScope,70	// RMRK71	RmrkCollectionInfo,72	RmrkInstanceInfo,73	RmrkResourceInfo,74	RmrkPropertyInfo,75	RmrkBaseInfo,76	RmrkPartType,77	RmrkTheme,78	RmrkNftChild,79	CollectionPermissions,80	SchemaVersion,81};8283pub use pallet::*;84use sp_core::H160;85use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};86#[cfg(feature = "runtime-benchmarks")]87pub mod benchmarking;88pub mod dispatch;89pub mod erc;90pub mod eth;91pub mod weights;9293pub type SelfWeightOf<T> = <T as Config>::WeightInfo;9495#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]96pub struct CollectionHandle<T: Config> {97	pub id: CollectionId,98	collection: Collection<T::AccountId>,99	pub recorder: SubstrateRecorder<T>,100}101impl<T: Config> WithRecorder<T> for CollectionHandle<T> {102	fn recorder(&self) -> &SubstrateRecorder<T> {103		&self.recorder104	}105	fn into_recorder(self) -> SubstrateRecorder<T> {106		self.recorder107	}108}109impl<T: Config> CollectionHandle<T> {110	pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {111		<CollectionById<T>>::get(id).map(|collection| Self {112			id,113			collection,114			recorder: SubstrateRecorder::new(gas_limit),115		})116	}117118	pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {119		<CollectionById<T>>::get(id).map(|collection| Self {120			id,121			collection,122			recorder,123		})124	}125126	pub fn new(id: CollectionId) -> Option<Self> {127		Self::new_with_gas_limit(id, u64::MAX)128	}129130	pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {131		Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)132	}133134	pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {135		self.recorder136			.consume_gas(T::GasWeightMapping::weight_to_gas(137				<T as frame_system::Config>::DbWeight::get()138					.read139					.saturating_mul(reads),140			))141	}142143	pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {144		self.recorder145			.consume_gas(T::GasWeightMapping::weight_to_gas(146				<T as frame_system::Config>::DbWeight::get()147					.write148					.saturating_mul(writes),149			))150	}151	pub fn save(self) -> Result<(), DispatchError> {152		<CollectionById<T>>::insert(self.id, self.collection);153		Ok(())154	}155156	pub fn set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {157		self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor);158		Ok(())159	}160161	pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> Result<bool, DispatchError> {162		if self.collection.sponsorship.pending_sponsor() != Some(sender) {163			return Ok(false);164		}165166		self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());167		Ok(true)168	}169170	/// Checks that the collection was created with, and must be operated upon through **Unique API**.171	/// Now check only the `external_collection` flag and if it's **true**, then return `CollectionIsExternal` error.172	pub fn check_is_internal(&self) -> DispatchResult {173		if self.external_collection {174			return Err(<Error<T>>::CollectionIsExternal)?;175		}176177		Ok(())178	}179180	/// Checks that the collection was created with, and must be operated upon through an **assimilated API**.181	/// Now check only the `external_collection` flag and if it's **false**, then return `CollectionIsInternal` error.182	pub fn check_is_external(&self) -> DispatchResult {183		if !self.external_collection {184			return Err(<Error<T>>::CollectionIsInternal)?;185		}186187		Ok(())188	}189}190191impl<T: Config> Deref for CollectionHandle<T> {192	type Target = Collection<T::AccountId>;193194	fn deref(&self) -> &Self::Target {195		&self.collection196	}197}198199impl<T: Config> DerefMut for CollectionHandle<T> {200	fn deref_mut(&mut self) -> &mut Self::Target {201		&mut self.collection202	}203}204205impl<T: Config> CollectionHandle<T> {206	pub fn check_is_owner(&self, subject: &T::CrossAccountId) -> DispatchResult {207		ensure!(*subject.as_sub() == self.owner, <Error<T>>::NoPermission);208		Ok(())209	}210	pub fn is_owner_or_admin(&self, subject: &T::CrossAccountId) -> bool {211		*subject.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, subject))212	}213	pub fn check_is_owner_or_admin(&self, subject: &T::CrossAccountId) -> DispatchResult {214		ensure!(self.is_owner_or_admin(subject), <Error<T>>::NoPermission);215		Ok(())216	}217	pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> bool {218		self.limits.owner_can_transfer() && self.is_owner_or_admin(user)219	}220	pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {221		self.limits.owner_can_transfer() && self.is_owner_or_admin(user)222	}223	pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {224		ensure!(225			<Allowlist<T>>::get((self.id, user)),226			<Error<T>>::AddressNotInAllowlist227		);228		Ok(())229	}230}231232#[frame_support::pallet]233pub mod pallet {234	use super::*;235	use pallet_evm::account;236	use dispatch::CollectionDispatch;237	use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};238	use frame_system::pallet_prelude::*;239	use frame_support::traits::Currency;240	use up_data_structs::{TokenId, mapping::TokenAddressMapping};241	use scale_info::TypeInfo;242	use weights::WeightInfo;243244	#[pallet::config]245	pub trait Config:246		frame_system::Config247		+ pallet_evm_coder_substrate::Config248		+ pallet_evm::Config249		+ TypeInfo250		+ account::Config251	{252		type WeightInfo: WeightInfo;253		type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;254255		type Currency: Currency<Self::AccountId>;256257		#[pallet::constant]258		type CollectionCreationPrice: Get<259			<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,260		>;261		type CollectionDispatch: CollectionDispatch<Self>;262263		type TreasuryAccountId: Get<Self::AccountId>;264		type ContractAddress: Get<H160>;265266		type EvmTokenAddressMapping: TokenAddressMapping<H160>;267		type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;268	}269270	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);271272	#[pallet::pallet]273	#[pallet::storage_version(STORAGE_VERSION)]274	#[pallet::generate_store(pub(super) trait Store)]275	pub struct Pallet<T>(_);276277	#[pallet::extra_constants]278	impl<T: Config> Pallet<T> {279		pub fn collection_admins_limit() -> u32 {280			COLLECTION_ADMINS_LIMIT281		}282	}283284	#[pallet::event]285	#[pallet::generate_deposit(pub fn deposit_event)]286	pub enum Event<T: Config> {287		/// New collection was created288		///289		/// # Arguments290		///291		/// * collection_id: Globally unique identifier of newly created collection.292		///293		/// * mode: [CollectionMode] converted into u8.294		///295		/// * account_id: Collection owner.296		CollectionCreated(CollectionId, u8, T::AccountId),297298		/// New collection was destroyed299		///300		/// # Arguments301		///302		/// * collection_id: Globally unique identifier of collection.303		CollectionDestroyed(CollectionId),304305		/// New item was created.306		///307		/// # Arguments308		///309		/// * collection_id: Id of the collection where item was created.310		///311		/// * item_id: Id of an item. Unique within the collection.312		///313		/// * recipient: Owner of newly created item314		///315		/// * amount: Always 1 for NFT316		ItemCreated(CollectionId, TokenId, T::CrossAccountId, u128),317318		/// Collection item was burned.319		///320		/// # Arguments321		///322		/// * collection_id.323		///324		/// * item_id: Identifier of burned NFT.325		///326		/// * owner: which user has destroyed its tokens327		///328		/// * amount: Always 1 for NFT329		ItemDestroyed(CollectionId, TokenId, T::CrossAccountId, u128),330331		/// Item was transferred332		///333		/// * collection_id: Id of collection to which item is belong334		///335		/// * item_id: Id of an item336		///337		/// * sender: Original owner of item338		///339		/// * recipient: New owner of item340		///341		/// * amount: Always 1 for NFT342		Transfer(343			CollectionId,344			TokenId,345			T::CrossAccountId,346			T::CrossAccountId,347			u128,348		),349350		/// * collection_id351		///352		/// * item_id353		///354		/// * sender355		///356		/// * spender357		///358		/// * amount359		Approved(360			CollectionId,361			TokenId,362			T::CrossAccountId,363			T::CrossAccountId,364			u128,365		),366367		CollectionPropertySet(CollectionId, PropertyKey),368369		CollectionPropertyDeleted(CollectionId, PropertyKey),370371		TokenPropertySet(CollectionId, TokenId, PropertyKey),372373		TokenPropertyDeleted(CollectionId, TokenId, PropertyKey),374375		PropertyPermissionSet(CollectionId, PropertyKey),376	}377378	#[pallet::error]379	pub enum Error<T> {380		/// This collection does not exist.381		CollectionNotFound,382		/// Sender parameter and item owner must be equal.383		MustBeTokenOwner,384		/// No permission to perform action385		NoPermission,386		/// Destroying only empty collections is allowed387		CantDestroyNotEmptyCollection,388		/// Collection is not in mint mode.389		PublicMintingNotAllowed,390		/// Address is not in allow list.391		AddressNotInAllowlist,392393		/// Collection name can not be longer than 63 char.394		CollectionNameLimitExceeded,395		/// Collection description can not be longer than 255 char.396		CollectionDescriptionLimitExceeded,397		/// Token prefix can not be longer than 15 char.398		CollectionTokenPrefixLimitExceeded,399		/// Total collections bound exceeded.400		TotalCollectionsLimitExceeded,401		/// Exceeded max admin count402		CollectionAdminCountExceeded,403		/// Collection limit bounds per collection exceeded404		CollectionLimitBoundsExceeded,405		/// Tried to enable permissions which are only permitted to be disabled406		OwnerPermissionsCantBeReverted,407		/// Collection settings not allowing items transferring408		TransferNotAllowed,409		/// Account token limit exceeded per collection410		AccountTokenLimitExceeded,411		/// Collection token limit exceeded412		CollectionTokenLimitExceeded,413		/// Metadata flag frozen414		MetadataFlagFrozen,415416		/// Item not exists.417		TokenNotFound,418		/// Item balance not enough.419		TokenValueTooLow,420		/// Requested value more than approved.421		ApprovedValueTooLow,422		/// Tried to approve more than owned423		CantApproveMoreThanOwned,424425		/// Can't transfer tokens to ethereum zero address426		AddressIsZero,427		/// Target collection doesn't supports this operation428		UnsupportedOperation,429430		/// Not sufficient founds to perform action431		NotSufficientFounds,432433		/// Collection has nesting disabled434		NestingIsDisabled,435		/// Only owner may nest tokens under this collection436		OnlyOwnerAllowedToNest,437		/// Only tokens from specific collections may nest tokens under this438		SourceCollectionIsNotAllowedToNest,439440		/// Tried to store more data than allowed in collection field441		CollectionFieldSizeExceeded,442443		/// Tried to store more property data than allowed444		NoSpaceForProperty,445446		/// Tried to store more property keys than allowed447		PropertyLimitReached,448449		/// Property key is too long450		PropertyKeyIsTooLong,451452		/// Only ASCII letters, digits, and '_', '-' are allowed453		InvalidCharacterInPropertyKey,454455		/// Empty property keys are forbidden456		EmptyPropertyKey,457458		/// Tried to access an external collection with an internal API459		CollectionIsExternal,460461		/// Tried to access an internal collection with an external API462		CollectionIsInternal,463	}464465	#[pallet::storage]466	pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;467	#[pallet::storage]468	pub type DestroyedCollectionCount<T> =469		StorageValue<Value = CollectionId, QueryKind = ValueQuery>;470471	/// Collection info472	#[pallet::storage]473	pub type CollectionById<T> = StorageMap<474		Hasher = Blake2_128Concat,475		Key = CollectionId,476		Value = Collection<<T as frame_system::Config>::AccountId>,477		QueryKind = OptionQuery,478	>;479480	/// Collection properties481	#[pallet::storage]482	#[pallet::getter(fn collection_properties)]483	pub type CollectionProperties<T> = StorageMap<484		Hasher = Blake2_128Concat,485		Key = CollectionId,486		Value = Properties,487		QueryKind = ValueQuery,488		OnEmpty = up_data_structs::CollectionProperties,489	>;490491	#[pallet::storage]492	#[pallet::getter(fn property_permissions)]493	pub type CollectionPropertyPermissions<T> = StorageMap<494		Hasher = Blake2_128Concat,495		Key = CollectionId,496		Value = PropertiesPermissionMap,497		QueryKind = ValueQuery,498	>;499500	#[pallet::storage]501	pub type AdminAmount<T> = StorageMap<502		Hasher = Blake2_128Concat,503		Key = CollectionId,504		Value = u32,505		QueryKind = ValueQuery,506	>;507508	/// List of collection admins509	#[pallet::storage]510	pub type IsAdmin<T: Config> = StorageNMap<511		Key = (512			Key<Blake2_128Concat, CollectionId>,513			Key<Blake2_128Concat, T::CrossAccountId>,514		),515		Value = bool,516		QueryKind = ValueQuery,517	>;518519	/// Allowlisted collection users520	#[pallet::storage]521	pub type Allowlist<T: Config> = StorageNMap<522		Key = (523			Key<Blake2_128Concat, CollectionId>,524			Key<Blake2_128Concat, T::CrossAccountId>,525		),526		Value = bool,527		QueryKind = ValueQuery,528	>;529530	/// Not used by code, exists only to provide some types to metadata531	#[pallet::storage]532	pub type DummyStorageValue<T: Config> = StorageValue<533		Value = (534			CollectionStats,535			CollectionId,536			TokenId,537			TokenChild,538			PhantomType<(539				TokenData<T::CrossAccountId>,540				RpcCollection<T::AccountId>,541				// RMRK542				RmrkCollectionInfo<T::AccountId>,543				RmrkInstanceInfo<T::AccountId>,544				RmrkResourceInfo,545				RmrkPropertyInfo,546				RmrkBaseInfo<T::AccountId>,547				RmrkPartType,548				RmrkTheme,549				RmrkNftChild,550			)>,551		),552		QueryKind = OptionQuery,553	>;554555	#[pallet::hooks]556	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {557		fn on_runtime_upgrade() -> Weight {558			if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {559				use up_data_structs::{CollectionVersion1, CollectionVersion2};560				<CollectionById<T>>::translate::<CollectionVersion1<T::AccountId>, _>(|id, v| {561					let mut props = Vec::new();562					if !v.offchain_schema.is_empty() {563						props.push(Property {564							key: b"_old_offchainSchema".to_vec().try_into().unwrap(),565							value: v566								.offchain_schema567								.clone()568								.into_inner()569								.try_into()570								.expect("offchain schema too big"),571						});572					}573					if !v.variable_on_chain_schema.is_empty() {574						props.push(Property {575							key: b"_old_variableOnChainSchema".to_vec().try_into().unwrap(),576							value: v577								.variable_on_chain_schema578								.clone()579								.into_inner()580								.try_into()581								.expect("offchain schema too big"),582						});583					}584					if !v.const_on_chain_schema.is_empty() {585						props.push(Property {586							key: b"_old_constOnChainSchema".to_vec().try_into().unwrap(),587							value: v588								.const_on_chain_schema589								.clone()590								.into_inner()591								.try_into()592								.expect("offchain schema too big"),593						});594					}595					props.push(Property {596						key: b"_old_schemaVersion".to_vec().try_into().unwrap(),597						value: match v.schema_version {598							SchemaVersion::ImageURL => b"ImageUrl".as_slice(),599							SchemaVersion::Unique => b"Unique".as_slice(),600						}601						.to_vec()602						.try_into()603						.unwrap(),604					});605					Self::set_scoped_collection_properties(606						id,607						PropertyScope::None,608						props.into_iter(),609					)610					.expect("existing data larger than properties");611					let mut new = CollectionVersion2::from(v.clone());612					new.permissions.access = Some(v.access);613					new.permissions.mint_mode = Some(v.mint_mode);614					Some(new)615				});616			}617618			0619		}620	}621}622623impl<T: Config> Pallet<T> {624	/// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens625	pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {626		ensure!(627			&T::CrossAccountId::from_eth(H160([0; 20])) != receiver,628			<Error<T>>::AddressIsZero629		);630		Ok(())631	}632	pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {633		<IsAdmin<T>>::iter_prefix((collection,))634			.map(|(a, _)| a)635			.collect()636	}637	pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {638		<Allowlist<T>>::iter_prefix((collection,))639			.map(|(a, _)| a)640			.collect()641	}642	pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {643		<Allowlist<T>>::get((collection, user))644	}645	pub fn collection_stats() -> CollectionStats {646		let created = <CreatedCollectionCount<T>>::get();647		let destroyed = <DestroyedCollectionCount<T>>::get();648		CollectionStats {649			created: created.0,650			destroyed: destroyed.0,651			alive: created.0 - destroyed.0,652		}653	}654655	pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {656		let collection = <CollectionById<T>>::get(collection);657		if collection.is_none() {658			return None;659		}660661		let collection = collection.unwrap();662		let limits = collection.limits;663		let effective_limits = CollectionLimits {664			account_token_ownership_limit: Some(limits.account_token_ownership_limit()),665			sponsored_data_size: Some(limits.sponsored_data_size()),666			sponsored_data_rate_limit: Some(667				limits668					.sponsored_data_rate_limit669					.unwrap_or(SponsoringRateLimit::SponsoringDisabled),670			),671			token_limit: Some(limits.token_limit()),672			sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(673				match collection.mode {674					CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,675					CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,676					CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,677				},678			)),679			sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),680			owner_can_transfer: Some(limits.owner_can_transfer()),681			owner_can_destroy: Some(limits.owner_can_destroy()),682			transfers_enabled: Some(limits.transfers_enabled()),683		};684685		Some(effective_limits)686	}687688	pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {689		let Collection {690			name,691			description,692			owner,693			mode,694			token_prefix,695			sponsorship,696			limits,697			permissions,698			external_collection,699		} = <CollectionById<T>>::get(collection)?;700701		let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)702			.into_iter()703			.map(|(key, permission)| PropertyKeyPermission { key, permission })704			.collect();705706		let properties = <CollectionProperties<T>>::get(collection)707			.into_iter()708			.map(|(key, value)| Property { key, value })709			.collect();710711		let permissions = CollectionPermissions {712			access: Some(permissions.access()),713			mint_mode: Some(permissions.mint_mode()),714			nesting: Some(permissions.nesting().clone()),715		};716717		Some(RpcCollection {718			name: name.into_inner(),719			description: description.into_inner(),720			owner,721			mode,722			token_prefix: token_prefix.into_inner(),723			sponsorship,724			limits,725			permissions,726			token_property_permissions,727			properties,728			read_only: external_collection,729		})730	}731}732733macro_rules! limit_default {734	($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{735		$(736			if let Some($new) = $new.$field {737				let $old = $old.$field($($arg)?);738				let _ = $new;739				let _ = $old;740				$check741			} else {742				$new.$field = $old.$field743			}744		)*745	}};746}747macro_rules! limit_default_clone {748	($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{749		$(750			if let Some($new) = $new.$field.clone() {751				let $old = $old.$field($($arg)?);752				let _ = $new;753				let _ = $old;754				$check755			} else {756				$new.$field = $old.$field.clone()757			}758		)*759	}};760}761762impl<T: Config> Pallet<T> {763	pub fn init_collection(764		owner: T::CrossAccountId,765		data: CreateCollectionData<T::AccountId>,766		is_external: bool,767	) -> Result<CollectionId, DispatchError> {768		{769			ensure!(770				data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,771				Error::<T>::CollectionTokenPrefixLimitExceeded772			);773		}774775		let created_count = <CreatedCollectionCount<T>>::get()776			.0777			.checked_add(1)778			.ok_or(ArithmeticError::Overflow)?;779		let destroyed_count = <DestroyedCollectionCount<T>>::get().0;780		let id = CollectionId(created_count);781782		// bound Total number of collections783		ensure!(784			created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,785			<Error<T>>::TotalCollectionsLimitExceeded786		);787788		// =========789790		let collection = Collection {791			owner: owner.as_sub().clone(),792			name: data.name,793			mode: data.mode.clone(),794			description: data.description,795			token_prefix: data.token_prefix,796			sponsorship: data797				.pending_sponsor798				.map(SponsorshipState::Unconfirmed)799				.unwrap_or_default(),800			limits: data801				.limits802				.map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))803				.unwrap_or_else(|| Ok(CollectionLimits::default()))?,804			permissions: data805				.permissions806				.map(|permissions| {807					Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)808				})809				.unwrap_or_else(|| Ok(CollectionPermissions::default()))?,810			external_collection: is_external,811		};812813		let mut collection_properties = up_data_structs::CollectionProperties::get();814		collection_properties815			.try_set_from_iter(data.properties.into_iter())816			.map_err(<Error<T>>::from)?;817818		CollectionProperties::<T>::insert(id, collection_properties);819820		let mut token_props_permissions = PropertiesPermissionMap::new();821		token_props_permissions822			.try_set_from_iter(data.token_property_permissions.into_iter())823			.map_err(<Error<T>>::from)?;824825		CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);826827		// Take a (non-refundable) deposit of collection creation828		{829			let mut imbalance =830				<<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();831			imbalance.subsume(832				<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(833					&T::TreasuryAccountId::get(),834					T::CollectionCreationPrice::get(),835				),836			);837			<T as Config>::Currency::settle(838				&owner.as_sub(),839				imbalance,840				WithdrawReasons::TRANSFER,841				ExistenceRequirement::KeepAlive,842			)843			.map_err(|_| Error::<T>::NotSufficientFounds)?;844		}845846		<CreatedCollectionCount<T>>::put(created_count);847		<Pallet<T>>::deposit_event(Event::CollectionCreated(848			id,849			data.mode.id(),850			owner.as_sub().clone(),851		));852		<PalletEvm<T>>::deposit_log(853			erc::CollectionHelpersEvents::CollectionCreated {854				owner: *owner.as_eth(),855				collection_id: eth::collection_id_to_address(id),856			}857			.to_log(T::ContractAddress::get()),858		);859		<CollectionById<T>>::insert(id, collection);860		Ok(id)861	}862863	pub fn destroy_collection(864		collection: CollectionHandle<T>,865		sender: &T::CrossAccountId,866	) -> DispatchResult {867		ensure!(868			collection.limits.owner_can_destroy(),869			<Error<T>>::NoPermission,870		);871		collection.check_is_owner(sender)?;872873		let destroyed_collections = <DestroyedCollectionCount<T>>::get()874			.0875			.checked_add(1)876			.ok_or(ArithmeticError::Overflow)?;877878		// =========879880		<DestroyedCollectionCount<T>>::put(destroyed_collections);881		<CollectionById<T>>::remove(collection.id);882		<AdminAmount<T>>::remove(collection.id);883		<IsAdmin<T>>::remove_prefix((collection.id,), None);884		<Allowlist<T>>::remove_prefix((collection.id,), None);885		<CollectionProperties<T>>::remove(collection.id);886887		<Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));888		Ok(())889	}890891	pub fn set_collection_property(892		collection: &CollectionHandle<T>,893		sender: &T::CrossAccountId,894		property: Property,895	) -> DispatchResult {896		collection.check_is_owner_or_admin(sender)?;897898		CollectionProperties::<T>::try_mutate(collection.id, |properties| {899			let property = property.clone();900			properties.try_set(property.key, property.value)901		})902		.map_err(<Error<T>>::from)?;903904		Self::deposit_event(Event::CollectionPropertySet(collection.id, property.key));905906		Ok(())907	}908909	pub fn set_scoped_collection_property(910		collection_id: CollectionId,911		scope: PropertyScope,912		property: Property,913	) -> DispatchResult {914		CollectionProperties::<T>::try_mutate(collection_id, |properties| {915			properties.try_scoped_set(scope, property.key, property.value)916		})917		.map_err(<Error<T>>::from)?;918919		Ok(())920	}921922	pub fn set_scoped_collection_properties(923		collection_id: CollectionId,924		scope: PropertyScope,925		properties: impl Iterator<Item = Property>,926	) -> DispatchResult {927		CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {928			stored_properties.try_scoped_set_from_iter(scope, properties)929		})930		.map_err(<Error<T>>::from)?;931932		Ok(())933	}934935	#[transactional]936	pub fn set_collection_properties(937		collection: &CollectionHandle<T>,938		sender: &T::CrossAccountId,939		properties: Vec<Property>,940	) -> DispatchResult {941		for property in properties {942			Self::set_collection_property(collection, sender, property)?;943		}944945		Ok(())946	}947948	pub fn delete_collection_property(949		collection: &CollectionHandle<T>,950		sender: &T::CrossAccountId,951		property_key: PropertyKey,952	) -> DispatchResult {953		collection.check_is_owner_or_admin(sender)?;954955		CollectionProperties::<T>::try_mutate(collection.id, |properties| {956			properties.remove(&property_key)957		})958		.map_err(<Error<T>>::from)?;959960		Self::deposit_event(Event::CollectionPropertyDeleted(961			collection.id,962			property_key,963		));964965		Ok(())966	}967968	#[transactional]969	pub fn delete_collection_properties(970		collection: &CollectionHandle<T>,971		sender: &T::CrossAccountId,972		property_keys: Vec<PropertyKey>,973	) -> DispatchResult {974		for key in property_keys {975			Self::delete_collection_property(collection, sender, key)?;976		}977978		Ok(())979	}980981	// For migrations982	pub fn set_property_permission_unchecked(983		collection: CollectionId,984		property_permission: PropertyKeyPermission,985	) -> DispatchResult {986		<CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {987			permissions.try_set(property_permission.key, property_permission.permission)988		})989		.map_err(<Error<T>>::from)?;990		Ok(())991	}992993	pub fn set_property_permission(994		collection: &CollectionHandle<T>,995		sender: &T::CrossAccountId,996		property_permission: PropertyKeyPermission,997	) -> DispatchResult {998		collection.check_is_owner_or_admin(sender)?;9991000		let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1001		let current_permission = all_permissions.get(&property_permission.key);1002		if matches![1003			current_permission,1004			Some(PropertyPermission { mutable: false, .. })1005		] {1006			return Err(<Error<T>>::NoPermission.into());1007		}10081009		CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1010			let property_permission = property_permission.clone();1011			permissions.try_set(property_permission.key, property_permission.permission)1012		})1013		.map_err(<Error<T>>::from)?;10141015		Self::deposit_event(Event::PropertyPermissionSet(1016			collection.id,1017			property_permission.key,1018		));10191020		Ok(())1021	}10221023	#[transactional]1024	pub fn set_property_permissions(1025		collection: &CollectionHandle<T>,1026		sender: &T::CrossAccountId,1027		property_permissions: Vec<PropertyKeyPermission>,1028	) -> DispatchResult {1029		for prop_pemission in property_permissions {1030			Self::set_property_permission(collection, sender, prop_pemission)?;1031		}10321033		Ok(())1034	}10351036	pub fn get_collection_property(1037		collection_id: CollectionId,1038		key: &PropertyKey,1039	) -> Option<PropertyValue> {1040		Self::collection_properties(collection_id).get(key).cloned()1041	}10421043	pub fn bytes_keys_to_property_keys(1044		keys: Vec<Vec<u8>>,1045	) -> Result<Vec<PropertyKey>, DispatchError> {1046		keys.into_iter()1047			.map(|key| -> Result<PropertyKey, DispatchError> {1048				key.try_into()1049					.map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1050			})1051			.collect::<Result<Vec<PropertyKey>, DispatchError>>()1052	}10531054	pub fn filter_collection_properties(1055		collection_id: CollectionId,1056		keys: Option<Vec<PropertyKey>>,1057	) -> Result<Vec<Property>, DispatchError> {1058		let properties = Self::collection_properties(collection_id);10591060		let properties = keys1061			.map(|keys| {1062				keys.into_iter()1063					.filter_map(|key| {1064						properties.get(&key).map(|value| Property {1065							key,1066							value: value.clone(),1067						})1068					})1069					.collect()1070			})1071			.unwrap_or_else(|| {1072				properties1073					.into_iter()1074					.map(|(key, value)| Property { key, value })1075					.collect()1076			});10771078		Ok(properties)1079	}10801081	pub fn filter_property_permissions(1082		collection_id: CollectionId,1083		keys: Option<Vec<PropertyKey>>,1084	) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1085		let permissions = Self::property_permissions(collection_id);10861087		let key_permissions = keys1088			.map(|keys| {1089				keys.into_iter()1090					.filter_map(|key| {1091						permissions1092							.get(&key)1093							.map(|permission| PropertyKeyPermission {1094								key,1095								permission: permission.clone(),1096							})1097					})1098					.collect()1099			})1100			.unwrap_or_else(|| {1101				permissions1102					.into_iter()1103					.map(|(key, permission)| PropertyKeyPermission { key, permission })1104					.collect()1105			});11061107		Ok(key_permissions)1108	}11091110	pub fn toggle_allowlist(1111		collection: &CollectionHandle<T>,1112		sender: &T::CrossAccountId,1113		user: &T::CrossAccountId,1114		allowed: bool,1115	) -> DispatchResult {1116		collection.check_is_owner_or_admin(sender)?;11171118		// =========11191120		if allowed {1121			<Allowlist<T>>::insert((collection.id, user), true);1122		} else {1123			<Allowlist<T>>::remove((collection.id, user));1124		}11251126		Ok(())1127	}11281129	pub fn toggle_admin(1130		collection: &CollectionHandle<T>,1131		sender: &T::CrossAccountId,1132		user: &T::CrossAccountId,1133		admin: bool,1134	) -> DispatchResult {1135		collection.check_is_owner_or_admin(sender)?;11361137		let was_admin = <IsAdmin<T>>::get((collection.id, user));1138		if was_admin == admin {1139			return Ok(());1140		}1141		let amount = <AdminAmount<T>>::get(collection.id);11421143		if admin {1144			let amount = amount1145				.checked_add(1)1146				.ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1147			ensure!(1148				amount <= Self::collection_admins_limit(),1149				<Error<T>>::CollectionAdminCountExceeded,1150			);11511152			// =========11531154			<AdminAmount<T>>::insert(collection.id, amount);1155			<IsAdmin<T>>::insert((collection.id, user), true);1156		} else {1157			<AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1158			<IsAdmin<T>>::remove((collection.id, user));1159		}11601161		Ok(())1162	}11631164	pub fn clamp_limits(1165		mode: CollectionMode,1166		old_limit: &CollectionLimits,1167		mut new_limit: CollectionLimits,1168	) -> Result<CollectionLimits, DispatchError> {1169		limit_default!(old_limit, new_limit,1170			account_token_ownership_limit => ensure!(1171				new_limit <= MAX_TOKEN_OWNERSHIP,1172				<Error<T>>::CollectionLimitBoundsExceeded,1173			),1174			sponsored_data_size => ensure!(1175				new_limit <= CUSTOM_DATA_LIMIT,1176				<Error<T>>::CollectionLimitBoundsExceeded,1177			),11781179			sponsored_data_rate_limit => {},1180			token_limit => ensure!(1181				old_limit >= new_limit && new_limit > 0,1182				<Error<T>>::CollectionTokenLimitExceeded1183			),11841185			sponsor_transfer_timeout(match mode {1186				CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1187				CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1188				CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1189			}) => ensure!(1190				new_limit <= MAX_SPONSOR_TIMEOUT,1191				<Error<T>>::CollectionLimitBoundsExceeded,1192			),1193			sponsor_approve_timeout => {},1194			owner_can_transfer => ensure!(1195				old_limit || !new_limit,1196				<Error<T>>::OwnerPermissionsCantBeReverted,1197			),1198			owner_can_destroy => ensure!(1199				old_limit || !new_limit,1200				<Error<T>>::OwnerPermissionsCantBeReverted,1201			),1202			transfers_enabled => {},1203		);1204		Ok(new_limit)1205	}12061207	pub fn clamp_permissions(1208		_mode: CollectionMode,1209		old_limit: &CollectionPermissions,1210		mut new_limit: CollectionPermissions,1211	) -> Result<CollectionPermissions, DispatchError> {1212		limit_default_clone!(old_limit, new_limit,1213			access => {},1214			mint_mode => {},1215			nesting => {},1216		);1217		Ok(new_limit)1218	}1219}12201221#[macro_export]1222macro_rules! unsupported {1223	() => {1224		Err(<Error<T>>::UnsupportedOperation.into())1225	};1226}12271228/// Worst cases1229pub trait CommonWeightInfo<CrossAccountId> {1230	fn create_item() -> Weight;1231	fn create_multiple_items(amount: &[CreateItemData]) -> Weight;1232	fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;1233	fn burn_item() -> Weight;1234	fn set_collection_properties(amount: u32) -> Weight;1235	fn delete_collection_properties(amount: u32) -> Weight;1236	fn set_token_properties(amount: u32) -> Weight;1237	fn delete_token_properties(amount: u32) -> Weight;1238	fn set_property_permissions(amount: u32) -> Weight;1239	fn transfer() -> Weight;1240	fn approve() -> Weight;1241	fn transfer_from() -> Weight;1242	fn burn_from() -> Weight;12431244	/// Differs from burn_item in case of Fungible and Refungible, as it should burn1245	/// whole users's balance1246	///1247	/// This method shouldn't be used directly, as it doesn't count breadth price, use `burn_recursively` instead1248	fn burn_recursively_self_raw() -> Weight;1249	/// Cost of iterating over `amount` children while burning, without counting child burning itself1250	///1251	/// This method shouldn't be used directly, as it doesn't count depth price, use `burn_recursively` instead1252	fn burn_recursively_breadth_raw(amount: u32) -> Weight;12531254	fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1255		Self::burn_recursively_self_raw()1256			.saturating_mul(max_selfs.max(1) as u64)1257			.saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1258	}1259}12601261pub trait CommonCollectionOperations<T: Config> {1262	fn create_item(1263		&self,1264		sender: T::CrossAccountId,1265		to: T::CrossAccountId,1266		data: CreateItemData,1267		nesting_budget: &dyn Budget,1268	) -> DispatchResultWithPostInfo;1269	fn create_multiple_items(1270		&self,1271		sender: T::CrossAccountId,1272		to: T::CrossAccountId,1273		data: Vec<CreateItemData>,1274		nesting_budget: &dyn Budget,1275	) -> DispatchResultWithPostInfo;1276	fn create_multiple_items_ex(1277		&self,1278		sender: T::CrossAccountId,1279		data: CreateItemExData<T::CrossAccountId>,1280		nesting_budget: &dyn Budget,1281	) -> DispatchResultWithPostInfo;1282	fn burn_item(1283		&self,1284		sender: T::CrossAccountId,1285		token: TokenId,1286		amount: u128,1287	) -> DispatchResultWithPostInfo;1288	fn burn_item_recursively(1289		&self,1290		sender: T::CrossAccountId,1291		token: TokenId,1292		self_budget: &dyn Budget,1293		breadth_budget: &dyn Budget,1294	) -> DispatchResultWithPostInfo;1295	fn set_collection_properties(1296		&self,1297		sender: T::CrossAccountId,1298		properties: Vec<Property>,1299	) -> DispatchResultWithPostInfo;1300	fn delete_collection_properties(1301		&self,1302		sender: &T::CrossAccountId,1303		property_keys: Vec<PropertyKey>,1304	) -> DispatchResultWithPostInfo;1305	fn set_token_properties(1306		&self,1307		sender: T::CrossAccountId,1308		token_id: TokenId,1309		property: Vec<Property>,1310	) -> DispatchResultWithPostInfo;1311	fn delete_token_properties(1312		&self,1313		sender: T::CrossAccountId,1314		token_id: TokenId,1315		property_keys: Vec<PropertyKey>,1316	) -> DispatchResultWithPostInfo;1317	fn set_property_permissions(1318		&self,1319		sender: &T::CrossAccountId,1320		property_permissions: Vec<PropertyKeyPermission>,1321	) -> DispatchResultWithPostInfo;1322	fn transfer(1323		&self,1324		sender: T::CrossAccountId,1325		to: T::CrossAccountId,1326		token: TokenId,1327		amount: u128,1328		nesting_budget: &dyn Budget,1329	) -> DispatchResultWithPostInfo;1330	fn approve(1331		&self,1332		sender: T::CrossAccountId,1333		spender: T::CrossAccountId,1334		token: TokenId,1335		amount: u128,1336	) -> DispatchResultWithPostInfo;1337	fn transfer_from(1338		&self,1339		sender: T::CrossAccountId,1340		from: T::CrossAccountId,1341		to: T::CrossAccountId,1342		token: TokenId,1343		amount: u128,1344		nesting_budget: &dyn Budget,1345	) -> DispatchResultWithPostInfo;1346	fn burn_from(1347		&self,1348		sender: T::CrossAccountId,1349		from: T::CrossAccountId,1350		token: TokenId,1351		amount: u128,1352		nesting_budget: &dyn Budget,1353	) -> DispatchResultWithPostInfo;13541355	fn check_nesting(1356		&self,1357		sender: T::CrossAccountId,1358		from: (CollectionId, TokenId),1359		under: TokenId,1360		budget: &dyn Budget,1361	) -> DispatchResult;13621363	fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));13641365	fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));13661367	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;1368	fn collection_tokens(&self) -> Vec<TokenId>;1369	fn token_exists(&self, token: TokenId) -> bool;1370	fn last_token_id(&self) -> TokenId;13711372	fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;1373	fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;1374	fn token_properties(&self, token_id: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;1375	/// Amount of unique collection tokens1376	fn total_supply(&self) -> u32;1377	/// Amount of different tokens account has (Applicable to nonfungible/refungible)1378	fn account_balance(&self, account: T::CrossAccountId) -> u32;1379	/// Amount of specific token account have (Applicable to fungible/refungible)1380	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;1381	fn allowance(1382		&self,1383		sender: T::CrossAccountId,1384		spender: T::CrossAccountId,1385		token: TokenId,1386	) -> u128;1387}13881389// Flexible enough for implementing CommonCollectionOperations1390pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {1391	let post_info = PostDispatchInfo {1392		actual_weight: Some(weight),1393		pays_fee: Pays::Yes,1394	};1395	match res {1396		Ok(()) => Ok(post_info),1397		Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),1398	}1399}14001401impl<T: Config> From<PropertiesError> for Error<T> {1402	fn from(error: PropertiesError) -> Self {1403		match error {1404			PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,1405			PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,1406			PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,1407			PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,1408			PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,1409		}1410	}1411}
after · pallets/common/src/lib.rs
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)]1819extern crate alloc;2021use core::ops::{Deref, DerefMut};22use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};23use sp_std::vec::Vec;24use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};25use evm_coder::ToLog;26use frame_support::{27	dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Weight, PostDispatchInfo},28	ensure,29	traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},30	weights::Pays,31	transactional,32};33use pallet_evm::GasWeightMapping;34use up_data_structs::{35	COLLECTION_NUMBER_LIMIT,36	Collection,37	RpcCollection,38	CollectionId,39	CreateItemData,40	MAX_TOKEN_PREFIX_LENGTH,41	COLLECTION_ADMINS_LIMIT,42	TokenId,43	TokenChild,44	CollectionStats,45	MAX_TOKEN_OWNERSHIP,46	CollectionMode,47	NFT_SPONSOR_TRANSFER_TIMEOUT,48	FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,49	REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,50	MAX_SPONSOR_TIMEOUT,51	CUSTOM_DATA_LIMIT,52	CollectionLimits,53	CreateCollectionData,54	SponsorshipState,55	CreateItemExData,56	SponsoringRateLimit,57	budget::Budget,58	PhantomType,59	Property,60	Properties,61	PropertiesPermissionMap,62	PropertyKey,63	PropertyValue,64	PropertyPermission,65	PropertiesError,66	PropertyKeyPermission,67	TokenData,68	TrySetProperty,69	PropertyScope,70	// RMRK71	RmrkCollectionInfo,72	RmrkInstanceInfo,73	RmrkResourceInfo,74	RmrkPropertyInfo,75	RmrkBaseInfo,76	RmrkPartType,77	RmrkTheme,78	RmrkNftChild,79	CollectionPermissions,80	SchemaVersion,81};8283pub use pallet::*;84use sp_core::H160;85use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};86#[cfg(feature = "runtime-benchmarks")]87pub mod benchmarking;88pub mod dispatch;89pub mod erc;90pub mod eth;91pub mod weights;9293pub type SelfWeightOf<T> = <T as Config>::WeightInfo;9495#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]96pub struct CollectionHandle<T: Config> {97	pub id: CollectionId,98	collection: Collection<T::AccountId>,99	pub recorder: SubstrateRecorder<T>,100}101impl<T: Config> WithRecorder<T> for CollectionHandle<T> {102	fn recorder(&self) -> &SubstrateRecorder<T> {103		&self.recorder104	}105	fn into_recorder(self) -> SubstrateRecorder<T> {106		self.recorder107	}108}109impl<T: Config> CollectionHandle<T> {110	pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {111		<CollectionById<T>>::get(id).map(|collection| Self {112			id,113			collection,114			recorder: SubstrateRecorder::new(gas_limit),115		})116	}117118	pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {119		<CollectionById<T>>::get(id).map(|collection| Self {120			id,121			collection,122			recorder,123		})124	}125126	pub fn new(id: CollectionId) -> Option<Self> {127		Self::new_with_gas_limit(id, u64::MAX)128	}129130	pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {131		Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)132	}133134	pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {135		self.recorder136			.consume_gas(T::GasWeightMapping::weight_to_gas(137				<T as frame_system::Config>::DbWeight::get()138					.read139					.saturating_mul(reads),140			))141	}142143	pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {144		self.recorder145			.consume_gas(T::GasWeightMapping::weight_to_gas(146				<T as frame_system::Config>::DbWeight::get()147					.write148					.saturating_mul(writes),149			))150	}151152	pub fn save(self) -> DispatchResult {153		<CollectionById<T>>::insert(self.id, self.collection);154		Ok(())155	}156157	pub fn set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {158		self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor);159		Ok(())160	}161162	pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> Result<bool, DispatchError> {163		if self.collection.sponsorship.pending_sponsor() != Some(sender) {164			return Ok(false);165		}166167		self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());168		Ok(true)169	}170171	/// Checks that the collection was created with, and must be operated upon through **Unique API**.172	/// Now check only the `external_collection` flag and if it's **true**, then return `CollectionIsExternal` error.173	pub fn check_is_internal(&self) -> DispatchResult {174		if self.external_collection {175			return Err(<Error<T>>::CollectionIsExternal)?;176		}177178		Ok(())179	}180181	/// Checks that the collection was created with, and must be operated upon through an **assimilated API**.182	/// Now check only the `external_collection` flag and if it's **false**, then return `CollectionIsInternal` error.183	pub fn check_is_external(&self) -> DispatchResult {184		if !self.external_collection {185			return Err(<Error<T>>::CollectionIsInternal)?;186		}187188		Ok(())189	}190}191192impl<T: Config> Deref for CollectionHandle<T> {193	type Target = Collection<T::AccountId>;194195	fn deref(&self) -> &Self::Target {196		&self.collection197	}198}199200impl<T: Config> DerefMut for CollectionHandle<T> {201	fn deref_mut(&mut self) -> &mut Self::Target {202		&mut self.collection203	}204}205206impl<T: Config> CollectionHandle<T> {207	pub fn check_is_owner(&self, subject: &T::CrossAccountId) -> DispatchResult {208		ensure!(*subject.as_sub() == self.owner, <Error<T>>::NoPermission);209		Ok(())210	}211	pub fn is_owner_or_admin(&self, subject: &T::CrossAccountId) -> bool {212		*subject.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, subject))213	}214	pub fn check_is_owner_or_admin(&self, subject: &T::CrossAccountId) -> DispatchResult {215		ensure!(self.is_owner_or_admin(subject), <Error<T>>::NoPermission);216		Ok(())217	}218	pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> bool {219		self.limits.owner_can_transfer() && self.is_owner_or_admin(user)220	}221	pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {222		self.limits.owner_can_transfer() && self.is_owner_or_admin(user)223	}224	pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {225		ensure!(226			<Allowlist<T>>::get((self.id, user)),227			<Error<T>>::AddressNotInAllowlist228		);229		Ok(())230	}231}232233#[frame_support::pallet]234pub mod pallet {235	use super::*;236	use pallet_evm::account;237	use dispatch::CollectionDispatch;238	use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};239	use frame_system::pallet_prelude::*;240	use frame_support::traits::Currency;241	use up_data_structs::{TokenId, mapping::TokenAddressMapping};242	use scale_info::TypeInfo;243	use weights::WeightInfo;244245	#[pallet::config]246	pub trait Config:247		frame_system::Config248		+ pallet_evm_coder_substrate::Config249		+ pallet_evm::Config250		+ TypeInfo251		+ account::Config252	{253		type WeightInfo: WeightInfo;254		type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;255256		type Currency: Currency<Self::AccountId>;257258		#[pallet::constant]259		type CollectionCreationPrice: Get<260			<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,261		>;262		type CollectionDispatch: CollectionDispatch<Self>;263264		type TreasuryAccountId: Get<Self::AccountId>;265		type ContractAddress: Get<H160>;266267		type EvmTokenAddressMapping: TokenAddressMapping<H160>;268		type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;269	}270271	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);272273	#[pallet::pallet]274	#[pallet::storage_version(STORAGE_VERSION)]275	#[pallet::generate_store(pub(super) trait Store)]276	pub struct Pallet<T>(_);277278	#[pallet::extra_constants]279	impl<T: Config> Pallet<T> {280		pub fn collection_admins_limit() -> u32 {281			COLLECTION_ADMINS_LIMIT282		}283	}284285	#[pallet::event]286	#[pallet::generate_deposit(pub fn deposit_event)]287	pub enum Event<T: Config> {288		/// New collection was created289		///290		/// # Arguments291		///292		/// * collection_id: Globally unique identifier of newly created collection.293		///294		/// * mode: [CollectionMode] converted into u8.295		///296		/// * account_id: Collection owner.297		CollectionCreated(CollectionId, u8, T::AccountId),298299		/// New collection was destroyed300		///301		/// # Arguments302		///303		/// * collection_id: Globally unique identifier of collection.304		CollectionDestroyed(CollectionId),305306		/// New item was created.307		///308		/// # Arguments309		///310		/// * collection_id: Id of the collection where item was created.311		///312		/// * item_id: Id of an item. Unique within the collection.313		///314		/// * recipient: Owner of newly created item315		///316		/// * amount: Always 1 for NFT317		ItemCreated(CollectionId, TokenId, T::CrossAccountId, u128),318319		/// Collection item was burned.320		///321		/// # Arguments322		///323		/// * collection_id.324		///325		/// * item_id: Identifier of burned NFT.326		///327		/// * owner: which user has destroyed its tokens328		///329		/// * amount: Always 1 for NFT330		ItemDestroyed(CollectionId, TokenId, T::CrossAccountId, u128),331332		/// Item was transferred333		///334		/// * collection_id: Id of collection to which item is belong335		///336		/// * item_id: Id of an item337		///338		/// * sender: Original owner of item339		///340		/// * recipient: New owner of item341		///342		/// * amount: Always 1 for NFT343		Transfer(344			CollectionId,345			TokenId,346			T::CrossAccountId,347			T::CrossAccountId,348			u128,349		),350351		/// * collection_id352		///353		/// * item_id354		///355		/// * sender356		///357		/// * spender358		///359		/// * amount360		Approved(361			CollectionId,362			TokenId,363			T::CrossAccountId,364			T::CrossAccountId,365			u128,366		),367368		CollectionPropertySet(CollectionId, PropertyKey),369370		CollectionPropertyDeleted(CollectionId, PropertyKey),371372		TokenPropertySet(CollectionId, TokenId, PropertyKey),373374		TokenPropertyDeleted(CollectionId, TokenId, PropertyKey),375376		PropertyPermissionSet(CollectionId, PropertyKey),377	}378379	#[pallet::error]380	pub enum Error<T> {381		/// This collection does not exist.382		CollectionNotFound,383		/// Sender parameter and item owner must be equal.384		MustBeTokenOwner,385		/// No permission to perform action386		NoPermission,387		/// Destroying only empty collections is allowed388		CantDestroyNotEmptyCollection,389		/// Collection is not in mint mode.390		PublicMintingNotAllowed,391		/// Address is not in allow list.392		AddressNotInAllowlist,393394		/// Collection name can not be longer than 63 char.395		CollectionNameLimitExceeded,396		/// Collection description can not be longer than 255 char.397		CollectionDescriptionLimitExceeded,398		/// Token prefix can not be longer than 15 char.399		CollectionTokenPrefixLimitExceeded,400		/// Total collections bound exceeded.401		TotalCollectionsLimitExceeded,402		/// Exceeded max admin count403		CollectionAdminCountExceeded,404		/// Collection limit bounds per collection exceeded405		CollectionLimitBoundsExceeded,406		/// Tried to enable permissions which are only permitted to be disabled407		OwnerPermissionsCantBeReverted,408		/// Collection settings not allowing items transferring409		TransferNotAllowed,410		/// Account token limit exceeded per collection411		AccountTokenLimitExceeded,412		/// Collection token limit exceeded413		CollectionTokenLimitExceeded,414		/// Metadata flag frozen415		MetadataFlagFrozen,416417		/// Item not exists.418		TokenNotFound,419		/// Item balance not enough.420		TokenValueTooLow,421		/// Requested value more than approved.422		ApprovedValueTooLow,423		/// Tried to approve more than owned424		CantApproveMoreThanOwned,425426		/// Can't transfer tokens to ethereum zero address427		AddressIsZero,428		/// Target collection doesn't supports this operation429		UnsupportedOperation,430431		/// Not sufficient founds to perform action432		NotSufficientFounds,433434		/// Collection has nesting disabled435		NestingIsDisabled,436		/// Only owner may nest tokens under this collection437		OnlyOwnerAllowedToNest,438		/// Only tokens from specific collections may nest tokens under this439		SourceCollectionIsNotAllowedToNest,440441		/// Tried to store more data than allowed in collection field442		CollectionFieldSizeExceeded,443444		/// Tried to store more property data than allowed445		NoSpaceForProperty,446447		/// Tried to store more property keys than allowed448		PropertyLimitReached,449450		/// Property key is too long451		PropertyKeyIsTooLong,452453		/// Only ASCII letters, digits, and '_', '-' are allowed454		InvalidCharacterInPropertyKey,455456		/// Empty property keys are forbidden457		EmptyPropertyKey,458459		/// Tried to access an external collection with an internal API460		CollectionIsExternal,461462		/// Tried to access an internal collection with an external API463		CollectionIsInternal,464	}465466	#[pallet::storage]467	pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;468	#[pallet::storage]469	pub type DestroyedCollectionCount<T> =470		StorageValue<Value = CollectionId, QueryKind = ValueQuery>;471472	/// Collection info473	#[pallet::storage]474	pub type CollectionById<T> = StorageMap<475		Hasher = Blake2_128Concat,476		Key = CollectionId,477		Value = Collection<<T as frame_system::Config>::AccountId>,478		QueryKind = OptionQuery,479	>;480481	/// Collection properties482	#[pallet::storage]483	#[pallet::getter(fn collection_properties)]484	pub type CollectionProperties<T> = StorageMap<485		Hasher = Blake2_128Concat,486		Key = CollectionId,487		Value = Properties,488		QueryKind = ValueQuery,489		OnEmpty = up_data_structs::CollectionProperties,490	>;491492	#[pallet::storage]493	#[pallet::getter(fn property_permissions)]494	pub type CollectionPropertyPermissions<T> = StorageMap<495		Hasher = Blake2_128Concat,496		Key = CollectionId,497		Value = PropertiesPermissionMap,498		QueryKind = ValueQuery,499	>;500501	#[pallet::storage]502	pub type AdminAmount<T> = StorageMap<503		Hasher = Blake2_128Concat,504		Key = CollectionId,505		Value = u32,506		QueryKind = ValueQuery,507	>;508509	/// List of collection admins510	#[pallet::storage]511	pub type IsAdmin<T: Config> = StorageNMap<512		Key = (513			Key<Blake2_128Concat, CollectionId>,514			Key<Blake2_128Concat, T::CrossAccountId>,515		),516		Value = bool,517		QueryKind = ValueQuery,518	>;519520	/// Allowlisted collection users521	#[pallet::storage]522	pub type Allowlist<T: Config> = StorageNMap<523		Key = (524			Key<Blake2_128Concat, CollectionId>,525			Key<Blake2_128Concat, T::CrossAccountId>,526		),527		Value = bool,528		QueryKind = ValueQuery,529	>;530531	/// Not used by code, exists only to provide some types to metadata532	#[pallet::storage]533	pub type DummyStorageValue<T: Config> = StorageValue<534		Value = (535			CollectionStats,536			CollectionId,537			TokenId,538			TokenChild,539			PhantomType<(540				TokenData<T::CrossAccountId>,541				RpcCollection<T::AccountId>,542				// RMRK543				RmrkCollectionInfo<T::AccountId>,544				RmrkInstanceInfo<T::AccountId>,545				RmrkResourceInfo,546				RmrkPropertyInfo,547				RmrkBaseInfo<T::AccountId>,548				RmrkPartType,549				RmrkTheme,550				RmrkNftChild,551			)>,552		),553		QueryKind = OptionQuery,554	>;555556	#[pallet::hooks]557	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {558		fn on_runtime_upgrade() -> Weight {559			if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {560				use up_data_structs::{CollectionVersion1, CollectionVersion2};561				<CollectionById<T>>::translate::<CollectionVersion1<T::AccountId>, _>(|id, v| {562					let mut props = Vec::new();563					if !v.offchain_schema.is_empty() {564						props.push(Property {565							key: b"_old_offchainSchema".to_vec().try_into().unwrap(),566							value: v567								.offchain_schema568								.clone()569								.into_inner()570								.try_into()571								.expect("offchain schema too big"),572						});573					}574					if !v.variable_on_chain_schema.is_empty() {575						props.push(Property {576							key: b"_old_variableOnChainSchema".to_vec().try_into().unwrap(),577							value: v578								.variable_on_chain_schema579								.clone()580								.into_inner()581								.try_into()582								.expect("offchain schema too big"),583						});584					}585					if !v.const_on_chain_schema.is_empty() {586						props.push(Property {587							key: b"_old_constOnChainSchema".to_vec().try_into().unwrap(),588							value: v589								.const_on_chain_schema590								.clone()591								.into_inner()592								.try_into()593								.expect("offchain schema too big"),594						});595					}596					props.push(Property {597						key: b"_old_schemaVersion".to_vec().try_into().unwrap(),598						value: match v.schema_version {599							SchemaVersion::ImageURL => b"ImageUrl".as_slice(),600							SchemaVersion::Unique => b"Unique".as_slice(),601						}602						.to_vec()603						.try_into()604						.unwrap(),605					});606					Self::set_scoped_collection_properties(607						id,608						PropertyScope::None,609						props.into_iter(),610					)611					.expect("existing data larger than properties");612					let mut new = CollectionVersion2::from(v.clone());613					new.permissions.access = Some(v.access);614					new.permissions.mint_mode = Some(v.mint_mode);615					Some(new)616				});617			}618619			0620		}621	}622}623624impl<T: Config> Pallet<T> {625	/// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens626	pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {627		ensure!(628			&T::CrossAccountId::from_eth(H160([0; 20])) != receiver,629			<Error<T>>::AddressIsZero630		);631		Ok(())632	}633	pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {634		<IsAdmin<T>>::iter_prefix((collection,))635			.map(|(a, _)| a)636			.collect()637	}638	pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {639		<Allowlist<T>>::iter_prefix((collection,))640			.map(|(a, _)| a)641			.collect()642	}643	pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {644		<Allowlist<T>>::get((collection, user))645	}646	pub fn collection_stats() -> CollectionStats {647		let created = <CreatedCollectionCount<T>>::get();648		let destroyed = <DestroyedCollectionCount<T>>::get();649		CollectionStats {650			created: created.0,651			destroyed: destroyed.0,652			alive: created.0 - destroyed.0,653		}654	}655656	pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {657		let collection = <CollectionById<T>>::get(collection);658		if collection.is_none() {659			return None;660		}661662		let collection = collection.unwrap();663		let limits = collection.limits;664		let effective_limits = CollectionLimits {665			account_token_ownership_limit: Some(limits.account_token_ownership_limit()),666			sponsored_data_size: Some(limits.sponsored_data_size()),667			sponsored_data_rate_limit: Some(668				limits669					.sponsored_data_rate_limit670					.unwrap_or(SponsoringRateLimit::SponsoringDisabled),671			),672			token_limit: Some(limits.token_limit()),673			sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(674				match collection.mode {675					CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,676					CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,677					CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,678				},679			)),680			sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),681			owner_can_transfer: Some(limits.owner_can_transfer()),682			owner_can_destroy: Some(limits.owner_can_destroy()),683			transfers_enabled: Some(limits.transfers_enabled()),684		};685686		Some(effective_limits)687	}688689	pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {690		let Collection {691			name,692			description,693			owner,694			mode,695			token_prefix,696			sponsorship,697			limits,698			permissions,699			external_collection,700		} = <CollectionById<T>>::get(collection)?;701702		let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)703			.into_iter()704			.map(|(key, permission)| PropertyKeyPermission { key, permission })705			.collect();706707		let properties = <CollectionProperties<T>>::get(collection)708			.into_iter()709			.map(|(key, value)| Property { key, value })710			.collect();711712		let permissions = CollectionPermissions {713			access: Some(permissions.access()),714			mint_mode: Some(permissions.mint_mode()),715			nesting: Some(permissions.nesting().clone()),716		};717718		Some(RpcCollection {719			name: name.into_inner(),720			description: description.into_inner(),721			owner,722			mode,723			token_prefix: token_prefix.into_inner(),724			sponsorship,725			limits,726			permissions,727			token_property_permissions,728			properties,729			read_only: external_collection,730		})731	}732}733734macro_rules! limit_default {735	($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{736		$(737			if let Some($new) = $new.$field {738				let $old = $old.$field($($arg)?);739				let _ = $new;740				let _ = $old;741				$check742			} else {743				$new.$field = $old.$field744			}745		)*746	}};747}748macro_rules! limit_default_clone {749	($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{750		$(751			if let Some($new) = $new.$field.clone() {752				let $old = $old.$field($($arg)?);753				let _ = $new;754				let _ = $old;755				$check756			} else {757				$new.$field = $old.$field.clone()758			}759		)*760	}};761}762763impl<T: Config> Pallet<T> {764	pub fn init_collection(765		owner: T::CrossAccountId,766		data: CreateCollectionData<T::AccountId>,767		is_external: bool,768	) -> Result<CollectionId, DispatchError> {769		{770			ensure!(771				data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,772				Error::<T>::CollectionTokenPrefixLimitExceeded773			);774		}775776		let created_count = <CreatedCollectionCount<T>>::get()777			.0778			.checked_add(1)779			.ok_or(ArithmeticError::Overflow)?;780		let destroyed_count = <DestroyedCollectionCount<T>>::get().0;781		let id = CollectionId(created_count);782783		// bound Total number of collections784		ensure!(785			created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,786			<Error<T>>::TotalCollectionsLimitExceeded787		);788789		// =========790791		let collection = Collection {792			owner: owner.as_sub().clone(),793			name: data.name,794			mode: data.mode.clone(),795			description: data.description,796			token_prefix: data.token_prefix,797			sponsorship: data798				.pending_sponsor799				.map(SponsorshipState::Unconfirmed)800				.unwrap_or_default(),801			limits: data802				.limits803				.map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))804				.unwrap_or_else(|| Ok(CollectionLimits::default()))?,805			permissions: data806				.permissions807				.map(|permissions| {808					Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)809				})810				.unwrap_or_else(|| Ok(CollectionPermissions::default()))?,811			external_collection: is_external,812		};813814		let mut collection_properties = up_data_structs::CollectionProperties::get();815		collection_properties816			.try_set_from_iter(data.properties.into_iter())817			.map_err(<Error<T>>::from)?;818819		CollectionProperties::<T>::insert(id, collection_properties);820821		let mut token_props_permissions = PropertiesPermissionMap::new();822		token_props_permissions823			.try_set_from_iter(data.token_property_permissions.into_iter())824			.map_err(<Error<T>>::from)?;825826		CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);827828		// Take a (non-refundable) deposit of collection creation829		{830			let mut imbalance =831				<<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();832			imbalance.subsume(833				<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(834					&T::TreasuryAccountId::get(),835					T::CollectionCreationPrice::get(),836				),837			);838			<T as Config>::Currency::settle(839				&owner.as_sub(),840				imbalance,841				WithdrawReasons::TRANSFER,842				ExistenceRequirement::KeepAlive,843			)844			.map_err(|_| Error::<T>::NotSufficientFounds)?;845		}846847		<CreatedCollectionCount<T>>::put(created_count);848		<Pallet<T>>::deposit_event(Event::CollectionCreated(849			id,850			data.mode.id(),851			owner.as_sub().clone(),852		));853		<PalletEvm<T>>::deposit_log(854			erc::CollectionHelpersEvents::CollectionCreated {855				owner: *owner.as_eth(),856				collection_id: eth::collection_id_to_address(id),857			}858			.to_log(T::ContractAddress::get()),859		);860		<CollectionById<T>>::insert(id, collection);861		Ok(id)862	}863864	pub fn destroy_collection(865		collection: CollectionHandle<T>,866		sender: &T::CrossAccountId,867	) -> DispatchResult {868		ensure!(869			collection.limits.owner_can_destroy(),870			<Error<T>>::NoPermission,871		);872		collection.check_is_owner(sender)?;873874		let destroyed_collections = <DestroyedCollectionCount<T>>::get()875			.0876			.checked_add(1)877			.ok_or(ArithmeticError::Overflow)?;878879		// =========880881		<DestroyedCollectionCount<T>>::put(destroyed_collections);882		<CollectionById<T>>::remove(collection.id);883		<AdminAmount<T>>::remove(collection.id);884		<IsAdmin<T>>::remove_prefix((collection.id,), None);885		<Allowlist<T>>::remove_prefix((collection.id,), None);886		<CollectionProperties<T>>::remove(collection.id);887888		<Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));889		Ok(())890	}891892	pub fn set_collection_property(893		collection: &CollectionHandle<T>,894		sender: &T::CrossAccountId,895		property: Property,896	) -> DispatchResult {897		collection.check_is_owner_or_admin(sender)?;898899		CollectionProperties::<T>::try_mutate(collection.id, |properties| {900			let property = property.clone();901			properties.try_set(property.key, property.value)902		})903		.map_err(<Error<T>>::from)?;904905		Self::deposit_event(Event::CollectionPropertySet(collection.id, property.key));906907		Ok(())908	}909910	pub fn set_scoped_collection_property(911		collection_id: CollectionId,912		scope: PropertyScope,913		property: Property,914	) -> DispatchResult {915		CollectionProperties::<T>::try_mutate(collection_id, |properties| {916			properties.try_scoped_set(scope, property.key, property.value)917		})918		.map_err(<Error<T>>::from)?;919920		Ok(())921	}922923	pub fn set_scoped_collection_properties(924		collection_id: CollectionId,925		scope: PropertyScope,926		properties: impl Iterator<Item = Property>,927	) -> DispatchResult {928		CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {929			stored_properties.try_scoped_set_from_iter(scope, properties)930		})931		.map_err(<Error<T>>::from)?;932933		Ok(())934	}935936	#[transactional]937	pub fn set_collection_properties(938		collection: &CollectionHandle<T>,939		sender: &T::CrossAccountId,940		properties: Vec<Property>,941	) -> DispatchResult {942		for property in properties {943			Self::set_collection_property(collection, sender, property)?;944		}945946		Ok(())947	}948949	pub fn delete_collection_property(950		collection: &CollectionHandle<T>,951		sender: &T::CrossAccountId,952		property_key: PropertyKey,953	) -> DispatchResult {954		collection.check_is_owner_or_admin(sender)?;955956		CollectionProperties::<T>::try_mutate(collection.id, |properties| {957			properties.remove(&property_key)958		})959		.map_err(<Error<T>>::from)?;960961		Self::deposit_event(Event::CollectionPropertyDeleted(962			collection.id,963			property_key,964		));965966		Ok(())967	}968969	#[transactional]970	pub fn delete_collection_properties(971		collection: &CollectionHandle<T>,972		sender: &T::CrossAccountId,973		property_keys: Vec<PropertyKey>,974	) -> DispatchResult {975		for key in property_keys {976			Self::delete_collection_property(collection, sender, key)?;977		}978979		Ok(())980	}981982	// For migrations983	pub fn set_property_permission_unchecked(984		collection: CollectionId,985		property_permission: PropertyKeyPermission,986	) -> DispatchResult {987		<CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {988			permissions.try_set(property_permission.key, property_permission.permission)989		})990		.map_err(<Error<T>>::from)?;991		Ok(())992	}993994	pub fn set_property_permission(995		collection: &CollectionHandle<T>,996		sender: &T::CrossAccountId,997		property_permission: PropertyKeyPermission,998	) -> DispatchResult {999		collection.check_is_owner_or_admin(sender)?;10001001		let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1002		let current_permission = all_permissions.get(&property_permission.key);1003		if matches![1004			current_permission,1005			Some(PropertyPermission { mutable: false, .. })1006		] {1007			return Err(<Error<T>>::NoPermission.into());1008		}10091010		CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1011			let property_permission = property_permission.clone();1012			permissions.try_set(property_permission.key, property_permission.permission)1013		})1014		.map_err(<Error<T>>::from)?;10151016		Self::deposit_event(Event::PropertyPermissionSet(1017			collection.id,1018			property_permission.key,1019		));10201021		Ok(())1022	}10231024	#[transactional]1025	pub fn set_property_permissions(1026		collection: &CollectionHandle<T>,1027		sender: &T::CrossAccountId,1028		property_permissions: Vec<PropertyKeyPermission>,1029	) -> DispatchResult {1030		for prop_pemission in property_permissions {1031			Self::set_property_permission(collection, sender, prop_pemission)?;1032		}10331034		Ok(())1035	}10361037	pub fn get_collection_property(1038		collection_id: CollectionId,1039		key: &PropertyKey,1040	) -> Option<PropertyValue> {1041		Self::collection_properties(collection_id).get(key).cloned()1042	}10431044	pub fn bytes_keys_to_property_keys(1045		keys: Vec<Vec<u8>>,1046	) -> Result<Vec<PropertyKey>, DispatchError> {1047		keys.into_iter()1048			.map(|key| -> Result<PropertyKey, DispatchError> {1049				key.try_into()1050					.map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1051			})1052			.collect::<Result<Vec<PropertyKey>, DispatchError>>()1053	}10541055	pub fn filter_collection_properties(1056		collection_id: CollectionId,1057		keys: Option<Vec<PropertyKey>>,1058	) -> Result<Vec<Property>, DispatchError> {1059		let properties = Self::collection_properties(collection_id);10601061		let properties = keys1062			.map(|keys| {1063				keys.into_iter()1064					.filter_map(|key| {1065						properties.get(&key).map(|value| Property {1066							key,1067							value: value.clone(),1068						})1069					})1070					.collect()1071			})1072			.unwrap_or_else(|| {1073				properties1074					.into_iter()1075					.map(|(key, value)| Property { key, value })1076					.collect()1077			});10781079		Ok(properties)1080	}10811082	pub fn filter_property_permissions(1083		collection_id: CollectionId,1084		keys: Option<Vec<PropertyKey>>,1085	) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1086		let permissions = Self::property_permissions(collection_id);10871088		let key_permissions = keys1089			.map(|keys| {1090				keys.into_iter()1091					.filter_map(|key| {1092						permissions1093							.get(&key)1094							.map(|permission| PropertyKeyPermission {1095								key,1096								permission: permission.clone(),1097							})1098					})1099					.collect()1100			})1101			.unwrap_or_else(|| {1102				permissions1103					.into_iter()1104					.map(|(key, permission)| PropertyKeyPermission { key, permission })1105					.collect()1106			});11071108		Ok(key_permissions)1109	}11101111	pub fn toggle_allowlist(1112		collection: &CollectionHandle<T>,1113		sender: &T::CrossAccountId,1114		user: &T::CrossAccountId,1115		allowed: bool,1116	) -> DispatchResult {1117		collection.check_is_owner_or_admin(sender)?;11181119		// =========11201121		if allowed {1122			<Allowlist<T>>::insert((collection.id, user), true);1123		} else {1124			<Allowlist<T>>::remove((collection.id, user));1125		}11261127		Ok(())1128	}11291130	pub fn toggle_admin(1131		collection: &CollectionHandle<T>,1132		sender: &T::CrossAccountId,1133		user: &T::CrossAccountId,1134		admin: bool,1135	) -> DispatchResult {1136		collection.check_is_owner_or_admin(sender)?;11371138		let was_admin = <IsAdmin<T>>::get((collection.id, user));1139		if was_admin == admin {1140			return Ok(());1141		}1142		let amount = <AdminAmount<T>>::get(collection.id);11431144		if admin {1145			let amount = amount1146				.checked_add(1)1147				.ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1148			ensure!(1149				amount <= Self::collection_admins_limit(),1150				<Error<T>>::CollectionAdminCountExceeded,1151			);11521153			// =========11541155			<AdminAmount<T>>::insert(collection.id, amount);1156			<IsAdmin<T>>::insert((collection.id, user), true);1157		} else {1158			<AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1159			<IsAdmin<T>>::remove((collection.id, user));1160		}11611162		Ok(())1163	}11641165	pub fn clamp_limits(1166		mode: CollectionMode,1167		old_limit: &CollectionLimits,1168		mut new_limit: CollectionLimits,1169	) -> Result<CollectionLimits, DispatchError> {1170		limit_default!(old_limit, new_limit,1171			account_token_ownership_limit => ensure!(1172				new_limit <= MAX_TOKEN_OWNERSHIP,1173				<Error<T>>::CollectionLimitBoundsExceeded,1174			),1175			sponsored_data_size => ensure!(1176				new_limit <= CUSTOM_DATA_LIMIT,1177				<Error<T>>::CollectionLimitBoundsExceeded,1178			),11791180			sponsored_data_rate_limit => {},1181			token_limit => ensure!(1182				old_limit >= new_limit && new_limit > 0,1183				<Error<T>>::CollectionTokenLimitExceeded1184			),11851186			sponsor_transfer_timeout(match mode {1187				CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1188				CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1189				CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1190			}) => ensure!(1191				new_limit <= MAX_SPONSOR_TIMEOUT,1192				<Error<T>>::CollectionLimitBoundsExceeded,1193			),1194			sponsor_approve_timeout => {},1195			owner_can_transfer => ensure!(1196				old_limit || !new_limit,1197				<Error<T>>::OwnerPermissionsCantBeReverted,1198			),1199			owner_can_destroy => ensure!(1200				old_limit || !new_limit,1201				<Error<T>>::OwnerPermissionsCantBeReverted,1202			),1203			transfers_enabled => {},1204		);1205		Ok(new_limit)1206	}12071208	pub fn clamp_permissions(1209		_mode: CollectionMode,1210		old_limit: &CollectionPermissions,1211		mut new_limit: CollectionPermissions,1212	) -> Result<CollectionPermissions, DispatchError> {1213		limit_default_clone!(old_limit, new_limit,1214			access => {},1215			mint_mode => {},1216			nesting => {},1217		);1218		Ok(new_limit)1219	}1220}12211222#[macro_export]1223macro_rules! unsupported {1224	() => {1225		Err(<Error<T>>::UnsupportedOperation.into())1226	};1227}12281229/// Worst cases1230pub trait CommonWeightInfo<CrossAccountId> {1231	fn create_item() -> Weight;1232	fn create_multiple_items(amount: &[CreateItemData]) -> Weight;1233	fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;1234	fn burn_item() -> Weight;1235	fn set_collection_properties(amount: u32) -> Weight;1236	fn delete_collection_properties(amount: u32) -> Weight;1237	fn set_token_properties(amount: u32) -> Weight;1238	fn delete_token_properties(amount: u32) -> Weight;1239	fn set_property_permissions(amount: u32) -> Weight;1240	fn transfer() -> Weight;1241	fn approve() -> Weight;1242	fn transfer_from() -> Weight;1243	fn burn_from() -> Weight;12441245	/// Differs from burn_item in case of Fungible and Refungible, as it should burn1246	/// whole users's balance1247	///1248	/// This method shouldn't be used directly, as it doesn't count breadth price, use `burn_recursively` instead1249	fn burn_recursively_self_raw() -> Weight;1250	/// Cost of iterating over `amount` children while burning, without counting child burning itself1251	///1252	/// This method shouldn't be used directly, as it doesn't count depth price, use `burn_recursively` instead1253	fn burn_recursively_breadth_raw(amount: u32) -> Weight;12541255	fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1256		Self::burn_recursively_self_raw()1257			.saturating_mul(max_selfs.max(1) as u64)1258			.saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1259	}1260}12611262pub trait CommonCollectionOperations<T: Config> {1263	fn create_item(1264		&self,1265		sender: T::CrossAccountId,1266		to: T::CrossAccountId,1267		data: CreateItemData,1268		nesting_budget: &dyn Budget,1269	) -> DispatchResultWithPostInfo;1270	fn create_multiple_items(1271		&self,1272		sender: T::CrossAccountId,1273		to: T::CrossAccountId,1274		data: Vec<CreateItemData>,1275		nesting_budget: &dyn Budget,1276	) -> DispatchResultWithPostInfo;1277	fn create_multiple_items_ex(1278		&self,1279		sender: T::CrossAccountId,1280		data: CreateItemExData<T::CrossAccountId>,1281		nesting_budget: &dyn Budget,1282	) -> DispatchResultWithPostInfo;1283	fn burn_item(1284		&self,1285		sender: T::CrossAccountId,1286		token: TokenId,1287		amount: u128,1288	) -> DispatchResultWithPostInfo;1289	fn burn_item_recursively(1290		&self,1291		sender: T::CrossAccountId,1292		token: TokenId,1293		self_budget: &dyn Budget,1294		breadth_budget: &dyn Budget,1295	) -> DispatchResultWithPostInfo;1296	fn set_collection_properties(1297		&self,1298		sender: T::CrossAccountId,1299		properties: Vec<Property>,1300	) -> DispatchResultWithPostInfo;1301	fn delete_collection_properties(1302		&self,1303		sender: &T::CrossAccountId,1304		property_keys: Vec<PropertyKey>,1305	) -> DispatchResultWithPostInfo;1306	fn set_token_properties(1307		&self,1308		sender: T::CrossAccountId,1309		token_id: TokenId,1310		property: Vec<Property>,1311	) -> DispatchResultWithPostInfo;1312	fn delete_token_properties(1313		&self,1314		sender: T::CrossAccountId,1315		token_id: TokenId,1316		property_keys: Vec<PropertyKey>,1317	) -> DispatchResultWithPostInfo;1318	fn set_property_permissions(1319		&self,1320		sender: &T::CrossAccountId,1321		property_permissions: Vec<PropertyKeyPermission>,1322	) -> DispatchResultWithPostInfo;1323	fn transfer(1324		&self,1325		sender: T::CrossAccountId,1326		to: T::CrossAccountId,1327		token: TokenId,1328		amount: u128,1329		nesting_budget: &dyn Budget,1330	) -> DispatchResultWithPostInfo;1331	fn approve(1332		&self,1333		sender: T::CrossAccountId,1334		spender: T::CrossAccountId,1335		token: TokenId,1336		amount: u128,1337	) -> DispatchResultWithPostInfo;1338	fn transfer_from(1339		&self,1340		sender: T::CrossAccountId,1341		from: T::CrossAccountId,1342		to: T::CrossAccountId,1343		token: TokenId,1344		amount: u128,1345		nesting_budget: &dyn Budget,1346	) -> DispatchResultWithPostInfo;1347	fn burn_from(1348		&self,1349		sender: T::CrossAccountId,1350		from: T::CrossAccountId,1351		token: TokenId,1352		amount: u128,1353		nesting_budget: &dyn Budget,1354	) -> DispatchResultWithPostInfo;13551356	fn check_nesting(1357		&self,1358		sender: T::CrossAccountId,1359		from: (CollectionId, TokenId),1360		under: TokenId,1361		budget: &dyn Budget,1362	) -> DispatchResult;13631364	fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));13651366	fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));13671368	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;1369	fn collection_tokens(&self) -> Vec<TokenId>;1370	fn token_exists(&self, token: TokenId) -> bool;1371	fn last_token_id(&self) -> TokenId;13721373	fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;1374	fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;1375	fn token_properties(&self, token_id: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;1376	/// Amount of unique collection tokens1377	fn total_supply(&self) -> u32;1378	/// Amount of different tokens account has (Applicable to nonfungible/refungible)1379	fn account_balance(&self, account: T::CrossAccountId) -> u32;1380	/// Amount of specific token account have (Applicable to fungible/refungible)1381	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;1382	fn allowance(1383		&self,1384		sender: T::CrossAccountId,1385		spender: T::CrossAccountId,1386		token: TokenId,1387	) -> u128;1388}13891390// Flexible enough for implementing CommonCollectionOperations1391pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {1392	let post_info = PostDispatchInfo {1393		actual_weight: Some(weight),1394		pays_fee: Pays::Yes,1395	};1396	match res {1397		Ok(()) => Ok(post_info),1398		Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),1399	}1400}14011402impl<T: Config> From<PropertiesError> for Error<T> {1403	fn from(error: PropertiesError) -> Self {1404		match error {1405			PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,1406			PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,1407			PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,1408			PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,1409			PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,1410		}1411	}1412}
modifiedpallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth
--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -51,6 +51,105 @@
 	event MintingFinished();
 }
 
+// Selector: 3a54513b
+contract Collection is Dummy, ERC165 {
+	// Selector: setCollectionProperty(string,bytes) 2f073f66
+	function setCollectionProperty(string memory key, bytes memory value)
+		public
+	{
+		require(false, stub_error);
+		key;
+		value;
+		dummy = 0;
+	}
+
+	// Selector: deleteCollectionProperty(string) 7b7debce
+	function deleteCollectionProperty(string memory key) public {
+		require(false, stub_error);
+		key;
+		dummy = 0;
+	}
+
+	// Throws error if key not found
+	//
+	// Selector: collectionProperty(string) cf24fd6d
+	function collectionProperty(string memory key)
+		public
+		view
+		returns (bytes memory)
+	{
+		require(false, stub_error);
+		key;
+		dummy;
+		return hex"";
+	}
+
+	// Selector: ethSetSponsor(address) 8f9af356
+	function ethSetSponsor(address sponsor) public {
+		require(false, stub_error);
+		sponsor;
+		dummy = 0;
+	}
+
+	// Selector: ethConfirmSponsorship() a8580d1a
+	function ethConfirmSponsorship() public {
+		require(false, stub_error);
+		dummy = 0;
+	}
+
+	// Selector: setLimit(string,uint32) 68db30ca
+	function setLimit(string memory limit, uint32 value) public {
+		require(false, stub_error);
+		limit;
+		value;
+		dummy = 0;
+	}
+
+	// Selector: setLimit(string,bool) ea67e4c2
+	function setLimit(string memory limit, bool value) public {
+		require(false, stub_error);
+		limit;
+		value;
+		dummy = 0;
+	}
+
+	// Selector: contractAddress() f6b4dfb4
+	function contractAddress() public view returns (address) {
+		require(false, stub_error);
+		dummy;
+		return 0x0000000000000000000000000000000000000000;
+	}
+
+	// Selector: addAdmin(address) 70480275
+	function addAdmin(address newAdmin) public view {
+		require(false, stub_error);
+		newAdmin;
+		dummy;
+	}
+
+	// Selector: removeAdmin(address) 1785f53c
+	function removeAdmin(address admin) public view {
+		require(false, stub_error);
+		admin;
+		dummy;
+	}
+
+	// Selector: setNesting(bool) e8fc50dd
+	function setNesting(bool enable) public {
+		require(false, stub_error);
+		enable;
+		dummy = 0;
+	}
+
+	// Selector: setNesting(bool,address[]) 7df12a9a
+	function setNesting(bool enable, address[] memory collections) public {
+		require(false, stub_error);
+		enable;
+		collections;
+		dummy = 0;
+	}
+}
+
 // Selector: 41369377
 contract TokenProperties is Dummy, ERC165 {
 	// Selector: setTokenPropertyPermission(string,bool,bool,bool) 222d97fa
@@ -294,135 +393,6 @@
 		require(false, stub_error);
 		dummy = 0;
 		return false;
-	}
-}
-
-// Selector: 6aea9834
-contract Collection is Dummy, ERC165 {
-	// Selector: setCollectionProperty(string,bytes) 2f073f66
-	function setCollectionProperty(string memory key, bytes memory value)
-		public
-	{
-		require(false, stub_error);
-		key;
-		value;
-		dummy = 0;
-	}
-
-	// Selector: deleteCollectionProperty(string) 7b7debce
-	function deleteCollectionProperty(string memory key) public {
-		require(false, stub_error);
-		key;
-		dummy = 0;
-	}
-
-	// Throws error if key not found
-	//
-	// Selector: collectionProperty(string) cf24fd6d
-	function collectionProperty(string memory key)
-		public
-		view
-		returns (bytes memory)
-	{
-		require(false, stub_error);
-		key;
-		dummy;
-		return hex"";
-	}
-
-	// Selector: setCollectionSponsor(address) 7623402e
-	function setCollectionSponsor(address sponsor) public {
-		require(false, stub_error);
-		sponsor;
-		dummy = 0;
-	}
-
-	// Selector: confirmCollectionSponsorship() 3c50e97a
-	function confirmCollectionSponsorship() public {
-		require(false, stub_error);
-		dummy = 0;
-	}
-
-	// Selector: setCollectionLimit(string,uint32) 6a3841db
-	function setCollectionLimit(string memory limit, uint32 value) public {
-		require(false, stub_error);
-		limit;
-		value;
-		dummy = 0;
-	}
-
-	// Selector: setCollectionLimit(string,bool) 993b7fba
-	function setCollectionLimit(string memory limit, bool value) public {
-		require(false, stub_error);
-		limit;
-		value;
-		dummy = 0;
-	}
-
-	// Selector: contractAddress() f6b4dfb4
-	function contractAddress() public view returns (address) {
-		require(false, stub_error);
-		dummy;
-		return 0x0000000000000000000000000000000000000000;
-	}
-
-	// Selector: addCollectionAdmin(address) 92e462c7
-	function addCollectionAdmin(address newAdmin) public view {
-		require(false, stub_error);
-		newAdmin;
-		dummy;
-	}
-
-	// Selector: removeCollectionAdmin(address) fafd7b42
-	function removeCollectionAdmin(address admin) public view {
-		require(false, stub_error);
-		admin;
-		dummy;
-	}
-
-	// Selector: setCollectionNesting(bool) 112d4586
-	function setCollectionNesting(bool enable) public {
-		require(false, stub_error);
-		enable;
-		dummy = 0;
-	}
-
-	// Selector: setCollectionNesting(bool,address[]) 64872396
-	function setCollectionNesting(bool enable, address[] memory collections)
-		public
-	{
-		require(false, stub_error);
-		enable;
-		collections;
-		dummy = 0;
-	}
-
-	// Selector: setCollectionAccess(uint8) 41835d4c
-	function setCollectionAccess(uint8 mode) public {
-		require(false, stub_error);
-		mode;
-		dummy = 0;
-	}
-
-	// Selector: addToCollectionAllowList(address) 67844fe6
-	function addToCollectionAllowList(address user) public view {
-		require(false, stub_error);
-		user;
-		dummy;
-	}
-
-	// Selector: removeFromCollectionAllowList(address) 85c51acb
-	function removeFromCollectionAllowList(address user) public view {
-		require(false, stub_error);
-		user;
-		dummy;
-	}
-
-	// Selector: setCollectionMintMode(bool) 00018e84
-	function setCollectionMintMode(bool mode) public {
-		require(false, stub_error);
-		mode;
-		dummy = 0;
 	}
 }
 
modifiedtests/src/eth/api/UniqueNFT.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -42,6 +42,51 @@
 	event MintingFinished();
 }
 
+// Selector: 3a54513b
+interface Collection is Dummy, ERC165 {
+	// Selector: setCollectionProperty(string,bytes) 2f073f66
+	function setCollectionProperty(string memory key, bytes memory value)
+		external;
+
+	// Selector: deleteCollectionProperty(string) 7b7debce
+	function deleteCollectionProperty(string memory key) external;
+
+	// Throws error if key not found
+	//
+	// Selector: collectionProperty(string) cf24fd6d
+	function collectionProperty(string memory key)
+		external
+		view
+		returns (bytes memory);
+
+	// Selector: ethSetSponsor(address) 8f9af356
+	function ethSetSponsor(address sponsor) external;
+
+	// Selector: ethConfirmSponsorship() a8580d1a
+	function ethConfirmSponsorship() external;
+
+	// Selector: setLimit(string,uint32) 68db30ca
+	function setLimit(string memory limit, uint32 value) external;
+
+	// Selector: setLimit(string,bool) ea67e4c2
+	function setLimit(string memory limit, bool value) external;
+
+	// Selector: contractAddress() f6b4dfb4
+	function contractAddress() external view returns (address);
+
+	// Selector: addAdmin(address) 70480275
+	function addAdmin(address newAdmin) external view;
+
+	// Selector: removeAdmin(address) 1785f53c
+	function removeAdmin(address admin) external view;
+
+	// Selector: setNesting(bool) e8fc50dd
+	function setNesting(bool enable) external;
+
+	// Selector: setNesting(bool,address[]) 7df12a9a
+	function setNesting(bool enable, address[] memory collections) external;
+}
+
 // Selector: 41369377
 interface TokenProperties is Dummy, ERC165 {
 	// Selector: setTokenPropertyPermission(string,bool,bool,bool) 222d97fa
@@ -172,64 +217,6 @@
 	//
 	// Selector: finishMinting() 7d64bcb4
 	function finishMinting() external returns (bool);
-}
-
-// Selector: 6aea9834
-interface Collection is Dummy, ERC165 {
-	// Selector: setCollectionProperty(string,bytes) 2f073f66
-	function setCollectionProperty(string memory key, bytes memory value)
-		external;
-
-	// Selector: deleteCollectionProperty(string) 7b7debce
-	function deleteCollectionProperty(string memory key) external;
-
-	// Throws error if key not found
-	//
-	// Selector: collectionProperty(string) cf24fd6d
-	function collectionProperty(string memory key)
-		external
-		view
-		returns (bytes memory);
-
-	// Selector: setCollectionSponsor(address) 7623402e
-	function setCollectionSponsor(address sponsor) external;
-
-	// Selector: confirmCollectionSponsorship() 3c50e97a
-	function confirmCollectionSponsorship() external;
-
-	// Selector: setCollectionLimit(string,uint32) 6a3841db
-	function setCollectionLimit(string memory limit, uint32 value) external;
-
-	// Selector: setCollectionLimit(string,bool) 993b7fba
-	function setCollectionLimit(string memory limit, bool value) external;
-
-	// Selector: contractAddress() f6b4dfb4
-	function contractAddress() external view returns (address);
-
-	// Selector: addCollectionAdmin(address) 92e462c7
-	function addCollectionAdmin(address newAdmin) external view;
-
-	// Selector: removeCollectionAdmin(address) fafd7b42
-	function removeCollectionAdmin(address admin) external view;
-
-	// Selector: setCollectionNesting(bool) 112d4586
-	function setCollectionNesting(bool enable) external;
-
-	// Selector: setCollectionNesting(bool,address[]) 64872396
-	function setCollectionNesting(bool enable, address[] memory collections)
-		external;
-
-	// Selector: setCollectionAccess(uint8) 41835d4c
-	function setCollectionAccess(uint8 mode) external;
-
-	// Selector: addToCollectionAllowList(address) 67844fe6
-	function addToCollectionAllowList(address user) external view;
-
-	// Selector: removeFromCollectionAllowList(address) 85c51acb
-	function removeFromCollectionAllowList(address user) external view;
-
-	// Selector: setCollectionMintMode(bool) 00018e84
-	function setCollectionMintMode(bool mode) external;
 }
 
 // Selector: 780e9d63
modifiedtests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/nonFungibleAbi.json
+++ b/tests/src/eth/nonFungibleAbi.json
@@ -84,16 +84,7 @@
     "inputs": [
       { "internalType": "address", "name": "newAdmin", "type": "address" }
     ],
-    "name": "addCollectionAdmin",
-    "outputs": [],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "user", "type": "address" }
-    ],
-    "name": "addToCollectionAllowList",
+    "name": "addAdmin",
     "outputs": [],
     "stateMutability": "view",
     "type": "function"
@@ -293,16 +284,7 @@
     "inputs": [
       { "internalType": "address", "name": "admin", "type": "address" }
     ],
-    "name": "removeCollectionAdmin",
-    "outputs": [],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "user", "type": "address" }
-    ],
-    "name": "removeFromCollectionAllowList",
+    "name": "removeAdmin",
     "outputs": [],
     "stateMutability": "view",
     "type": "function"
@@ -416,6 +398,27 @@
   },
   {
     "inputs": [
+      { "internalType": "bool", "name": "enable", "type": "bool" },
+      {
+        "internalType": "address[]",
+        "name": "collections",
+        "type": "address[]"
+      }
+    ],
+    "name": "setNesting",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],
+    "name": "setNesting",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
       { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
       { "internalType": "string", "name": "key", "type": "string" },
       { "internalType": "bytes", "name": "value", "type": "bytes" }
modifiedtests/src/eth/proxy/nonFungibleProxy.test.tsdiffbeforeafterboth
--- a/tests/src/eth/proxy/nonFungibleProxy.test.ts
+++ b/tests/src/eth/proxy/nonFungibleProxy.test.ts
@@ -99,7 +99,7 @@
     const collectionEvmOwned = evmCollection(web3, owner, collectionIdAddress);
     const collectionEvm = evmCollection(web3, caller, collectionIdAddress);
     const contract = await proxyWrap(api, web3, collectionEvm);
-    await collectionEvmOwned.methods.addCollectionAdmin(contract.options.address).send();
+    await collectionEvmOwned.methods.addAdmin(contract.options.address).send();
 
     {
       const nextTokenId = await contract.methods.nextTokenId().call();
modifiedtests/src/interfaces/augment-api-rpc.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-rpc.ts
+++ b/tests/src/interfaces/augment-api-rpc.ts
@@ -18,7 +18,7 @@
 import type { EthAccount, EthCallRequest, EthFilter, EthFilterChanges, EthLog, EthReceipt, EthRichBlock, EthSubKind, EthSubParams, EthSyncStatus, EthTransaction, EthTransactionRequest, EthWork } from '@polkadot/types/interfaces/eth';
 import type { Extrinsic } from '@polkadot/types/interfaces/extrinsics';
 import type { EncodedFinalityProofs, JustificationNotification, ReportedRoundStates } from '@polkadot/types/interfaces/grandpa';
-import type { MmrLeafBatchProof, MmrLeafProof } from '@polkadot/types/interfaces/mmr';
+import type { MmrLeafProof } from '@polkadot/types/interfaces/mmr';
 import type { StorageKind } from '@polkadot/types/interfaces/offchain';
 import type { FeeDetails, RuntimeDispatchInfo } from '@polkadot/types/interfaces/payment';
 import type { RpcMethods } from '@polkadot/types/interfaces/rpc';
@@ -354,14 +354,10 @@
       subscribeJustifications: AugmentedRpc<() => Observable<JustificationNotification>>;
     };
     mmr: {
-      /**
-       * Generate MMR proof for the given leaf indices.
-       **/
-      generateBatchProof: AugmentedRpc<(leafIndices: Vec<u64> | (u64 | AnyNumber | Uint8Array)[], at?: BlockHash | string | Uint8Array) => Observable<MmrLeafProof>>;
       /**
        * Generate MMR proof for given leaf index.
        **/
-      generateProof: AugmentedRpc<(leafIndex: u64 | AnyNumber | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<MmrLeafBatchProof>>;
+      generateProof: AugmentedRpc<(leafIndex: u64 | AnyNumber | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<MmrLeafProof>>;
     };
     net: {
       /**
modifiedtests/src/interfaces/augment-types.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-types.ts
+++ b/tests/src/interfaces/augment-types.ts
@@ -3,7 +3,7 @@
 
 import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSupportWeightsWeightToFeeCoefficient, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUnqSchedulerCall, PalletUnqSchedulerError, PalletUnqSchedulerEvent, PalletUnqSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExData, UpDataStructsNestingRule, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './default';
 import type { Data, StorageKey } from '@polkadot/types';
-import type { BitVec, Bool, Bytes, I128, I16, I256, I32, I64, I8, Json, Null, OptionBool, Raw, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, i128, i16, i256, i32, i64, i8, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types-codec';
+import type { BitVec, Bool, Bytes, I128, I16, I256, I32, I64, I8, Json, Null, Raw, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, i128, i16, i256, i32, i64, i8, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types-codec';
 import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets';
 import type { BlockAttestations, IncludedBlocks, MoreAttestations } from '@polkadot/types/interfaces/attestations';
 import type { RawAuraPreDigest } from '@polkadot/types/interfaces/aura';
@@ -36,7 +36,7 @@
 import type { AuthIndex, AuthoritySignature, Heartbeat, HeartbeatTo244, OpaqueMultiaddr, OpaqueNetworkState, OpaquePeerId } from '@polkadot/types/interfaces/imOnline';
 import type { CallIndex, LotteryConfig } from '@polkadot/types/interfaces/lottery';
 import type { ErrorMetadataLatest, ErrorMetadataV10, ErrorMetadataV11, ErrorMetadataV12, ErrorMetadataV13, ErrorMetadataV14, ErrorMetadataV9, EventMetadataLatest, EventMetadataV10, EventMetadataV11, EventMetadataV12, EventMetadataV13, EventMetadataV14, EventMetadataV9, ExtrinsicMetadataLatest, ExtrinsicMetadataV11, ExtrinsicMetadataV12, ExtrinsicMetadataV13, ExtrinsicMetadataV14, FunctionArgumentMetadataLatest, FunctionArgumentMetadataV10, FunctionArgumentMetadataV11, FunctionArgumentMetadataV12, FunctionArgumentMetadataV13, FunctionArgumentMetadataV14, FunctionArgumentMetadataV9, FunctionMetadataLatest, FunctionMetadataV10, FunctionMetadataV11, FunctionMetadataV12, FunctionMetadataV13, FunctionMetadataV14, FunctionMetadataV9, MetadataAll, MetadataLatest, MetadataV10, MetadataV11, MetadataV12, MetadataV13, MetadataV14, MetadataV9, ModuleConstantMetadataV10, ModuleConstantMetadataV11, ModuleConstantMetadataV12, ModuleConstantMetadataV13, ModuleConstantMetadataV9, ModuleMetadataV10, ModuleMetadataV11, ModuleMetadataV12, ModuleMetadataV13, ModuleMetadataV9, PalletCallMetadataLatest, PalletCallMetadataV14, PalletConstantMetadataLatest, PalletConstantMetadataV14, PalletErrorMetadataLatest, PalletErrorMetadataV14, PalletEventMetadataLatest, PalletEventMetadataV14, PalletMetadataLatest, PalletMetadataV14, PalletStorageMetadataLatest, PalletStorageMetadataV14, PortableType, PortableTypeV14, SignedExtensionMetadataLatest, SignedExtensionMetadataV14, StorageEntryMetadataLatest, StorageEntryMetadataV10, StorageEntryMetadataV11, StorageEntryMetadataV12, StorageEntryMetadataV13, StorageEntryMetadataV14, StorageEntryMetadataV9, StorageEntryModifierLatest, StorageEntryModifierV10, StorageEntryModifierV11, StorageEntryModifierV12, StorageEntryModifierV13, StorageEntryModifierV14, StorageEntryModifierV9, StorageEntryTypeLatest, StorageEntryTypeV10, StorageEntryTypeV11, StorageEntryTypeV12, StorageEntryTypeV13, StorageEntryTypeV14, StorageEntryTypeV9, StorageHasher, StorageHasherV10, StorageHasherV11, StorageHasherV12, StorageHasherV13, StorageHasherV14, StorageHasherV9, StorageMetadataV10, StorageMetadataV11, StorageMetadataV12, StorageMetadataV13, StorageMetadataV9 } from '@polkadot/types/interfaces/metadata';
-import type { MmrLeafBatchProof, MmrLeafProof } from '@polkadot/types/interfaces/mmr';
+import type { MmrLeafProof } from '@polkadot/types/interfaces/mmr';
 import type { StorageKind } from '@polkadot/types/interfaces/offchain';
 import type { DeferredOffenceOf, Kind, OffenceDetails, Offender, OpaqueTimeSlot, ReportIdOf, Reporter } from '@polkadot/types/interfaces/offences';
 import type { AbridgedCandidateReceipt, AbridgedHostConfiguration, AbridgedHrmpChannel, AssignmentId, AssignmentKind, AttestedCandidate, AuctionIndex, AuthorityDiscoveryId, AvailabilityBitfield, AvailabilityBitfieldRecord, BackedCandidate, Bidder, BufferedSessionChange, CandidateCommitments, CandidateDescriptor, CandidateHash, CandidateInfo, CandidatePendingAvailability, CandidateReceipt, CollatorId, CollatorSignature, CommittedCandidateReceipt, CoreAssignment, CoreIndex, CoreOccupied, DisputeLocation, DisputeResult, DisputeState, DisputeStatement, DisputeStatementSet, DoubleVoteReport, DownwardMessage, ExplicitDisputeStatement, GlobalValidationData, GlobalValidationSchedule, GroupIndex, HeadData, HostConfiguration, HrmpChannel, HrmpChannelId, HrmpOpenChannelRequest, InboundDownwardMessage, InboundHrmpMessage, InboundHrmpMessages, IncomingParachain, IncomingParachainDeploy, IncomingParachainFixed, InvalidDisputeStatementKind, LeasePeriod, LeasePeriodOf, LocalValidationData, MessageIngestionType, MessageQueueChain, MessagingStateSnapshot, MessagingStateSnapshotEgressEntry, MultiDisputeStatementSet, NewBidder, OutboundHrmpMessage, ParaGenesisArgs, ParaId, ParaInfo, ParaLifecycle, ParaPastCodeMeta, ParaScheduling, ParaValidatorIndex, ParachainDispatchOrigin, ParachainInherentData, ParachainProposal, ParachainsInherentData, ParathreadClaim, ParathreadClaimQueue, ParathreadEntry, PersistedValidationData, QueuedParathread, RegisteredParachainInfo, RelayBlockNumber, RelayChainBlockNumber, RelayChainHash, RelayHash, Remark, ReplacementTimes, Retriable, Scheduling, ServiceQuality, SessionInfo, SessionInfoValidatorGroup, SignedAvailabilityBitfield, SignedAvailabilityBitfields, SigningContext, SlotRange, SlotRange10, Statement, SubId, SystemInherentData, TransientValidationData, UpgradeGoAhead, UpgradeRestriction, UpwardMessage, ValidDisputeStatementKind, ValidationCode, ValidationCodeHash, ValidationData, ValidationDataType, ValidationFunctionParams, ValidatorSignature, ValidityAttestation, VecInboundHrmpMessage, WinnersData, WinnersData10, WinnersDataTuple, WinnersDataTuple10, WinningData, WinningData10, WinningDataEntry } from '@polkadot/types/interfaces/parachains';
@@ -664,7 +664,6 @@
     MetadataV14: MetadataV14;
     MetadataV9: MetadataV9;
     MigrationStatusResult: MigrationStatusResult;
-    MmrLeafBatchProof: MmrLeafBatchProof;
     MmrLeafProof: MmrLeafProof;
     MmrRootHash: MmrRootHash;
     ModuleConstantMetadataV10: ModuleConstantMetadataV10;
@@ -732,7 +731,6 @@
     OpenTipTip: OpenTipTip;
     OpenTipTo225: OpenTipTo225;
     OperatingMode: OperatingMode;
-    OptionBool: OptionBool;
     Origin: Origin;
     OriginCaller: OriginCaller;
     OriginKindV0: OriginKindV0;