git.delta.rocks / unique-network / refs/commits / 1a56db748b0a

difftreelog

doc: architectural changes

Farhad Hakimov2022-07-11parent: #12c8c8f.patch.diff
in: master

10 files changed

modifiedclient/rpc/src/lib.rsdiffbeforeafterboth
--- a/client/rpc/src/lib.rs
+++ b/client/rpc/src/lib.rs
@@ -42,6 +42,7 @@
 #[rpc(server)]
 #[async_trait]
 pub trait UniqueApi<BlockHash, CrossAccountId, AccountId> {
+	/// Get tokens owned by account
 	#[method(name = "unique_accountTokens")]
 	fn account_tokens(
 		&self,
@@ -49,12 +50,14 @@
 		account: CrossAccountId,
 		at: Option<BlockHash>,
 	) -> Result<Vec<TokenId>>;
+	/// Get tokens contained in collection
 	#[method(name = "unique_collectionTokens")]
 	fn collection_tokens(
 		&self,
 		collection: CollectionId,
 		at: Option<BlockHash>,
 	) -> Result<Vec<TokenId>>;
+	/// Check if token exists
 	#[method(name = "unique_tokenExists")]
 	fn token_exists(
 		&self,
@@ -62,7 +65,7 @@
 		token: TokenId,
 		at: Option<BlockHash>,
 	) -> Result<bool>;
-
+	/// Get token owner
 	#[method(name = "unique_tokenOwner")]
 	fn token_owner(
 		&self,
@@ -70,6 +73,7 @@
 		token: TokenId,
 		at: Option<BlockHash>,
 	) -> Result<Option<CrossAccountId>>;
+	/// Get token owner, in case of nested token - find the parent recursively
 	#[method(name = "unique_topmostTokenOwner")]
 	fn topmost_token_owner(
 		&self,
@@ -77,6 +81,7 @@
 		token: TokenId,
 		at: Option<BlockHash>,
 	) -> Result<Option<CrossAccountId>>;
+	/// Get tokens nested directly into the token
 	#[method(name = "unique_tokenChildren")]
 	fn token_children(
 		&self,
@@ -84,7 +89,7 @@
 		token: TokenId,
 		at: Option<BlockHash>,
 	) -> Result<Vec<TokenChild>>;
-
+	/// Get collection properties
 	#[method(name = "unique_collectionProperties")]
 	fn collection_properties(
 		&self,
@@ -92,7 +97,7 @@
 		keys: Option<Vec<String>>,
 		at: Option<BlockHash>,
 	) -> Result<Vec<Property>>;
-
+	/// Get token properties
 	#[method(name = "unique_tokenProperties")]
 	fn token_properties(
 		&self,
@@ -101,7 +106,7 @@
 		keys: Option<Vec<String>>,
 		at: Option<BlockHash>,
 	) -> Result<Vec<Property>>;
-
+	/// Get property permissions
 	#[method(name = "unique_propertyPermissions")]
 	fn property_permissions(
 		&self,
@@ -109,7 +114,7 @@
 		keys: Option<Vec<String>>,
 		at: Option<BlockHash>,
 	) -> Result<Vec<PropertyKeyPermission>>;
-
+	/// Get token data
 	#[method(name = "unique_tokenData")]
 	fn token_data(
 		&self,
@@ -118,9 +123,10 @@
 		keys: Option<Vec<String>>,
 		at: Option<BlockHash>,
 	) -> Result<TokenData<CrossAccountId>>;
-
+	/// Get amount of unique collection tokens
 	#[method(name = "unique_totalSupply")]
 	fn total_supply(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<u32>;
+	/// Get owned amount of any user tokens
 	#[method(name = "unique_accountBalance")]
 	fn account_balance(
 		&self,
@@ -128,6 +134,7 @@
 		account: CrossAccountId,
 		at: Option<BlockHash>,
 	) -> Result<u32>;
+	/// Get owned amount of specific account token
 	#[method(name = "unique_balance")]
 	fn balance(
 		&self,
@@ -136,6 +143,7 @@
 		token: TokenId,
 		at: Option<BlockHash>,
 	) -> Result<String>;
+	/// Get allowed amount
 	#[method(name = "unique_allowance")]
 	fn allowance(
 		&self,
@@ -145,19 +153,21 @@
 		token: TokenId,
 		at: Option<BlockHash>,
 	) -> Result<String>;
-
+	/// Get admin list
 	#[method(name = "unique_adminlist")]
 	fn adminlist(
 		&self,
 		collection: CollectionId,
 		at: Option<BlockHash>,
 	) -> Result<Vec<CrossAccountId>>;
+	/// Get allowlist
 	#[method(name = "unique_allowlist")]
 	fn allowlist(
 		&self,
 		collection: CollectionId,
 		at: Option<BlockHash>,
 	) -> Result<Vec<CrossAccountId>>;
+	/// Check if user is allowed to use collection
 	#[method(name = "unique_allowed")]
 	fn allowed(
 		&self,
@@ -165,17 +175,20 @@
 		user: CrossAccountId,
 		at: Option<BlockHash>,
 	) -> Result<bool>;
+	/// Get last token ID created in a collection
 	#[method(name = "unique_lastTokenId")]
 	fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;
+	/// Get collection by specified ID
 	#[method(name = "unique_collectionById")]
 	fn collection_by_id(
 		&self,
 		collection: CollectionId,
 		at: Option<BlockHash>,
 	) -> Result<Option<RpcCollection<AccountId>>>;
+	/// Get collection stats
 	#[method(name = "unique_collectionStats")]
 	fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;
-
+	/// Get number of blocks when sponsored transaction is available
 	#[method(name = "unique_nextSponsored")]
 	fn next_sponsored(
 		&self,
@@ -184,14 +197,14 @@
 		token: TokenId,
 		at: Option<BlockHash>,
 	) -> Result<Option<u64>>;
-
+	/// Get effective collection limits
 	#[method(name = "unique_effectiveCollectionLimits")]
 	fn effective_collection_limits(
 		&self,
 		collection_id: CollectionId,
 		at: Option<BlockHash>,
 	) -> Result<Option<CollectionLimits>>;
-
+	/// Get total pieces of token
 	#[method(name = "unique_totalPieces")]
 	fn total_pieces(
 		&self,
@@ -304,6 +317,7 @@
 		fn base_parts(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Vec<PartType>>;
 
 		#[method(name = "rmrk_themeNames")]
+		/// Get Base's theme names
 		fn theme_names(
 			&self,
 			base_id: RmrkBaseId,
@@ -311,6 +325,7 @@
 		) -> Result<Vec<RmrkThemeName>>;
 
 		#[method(name = "rmrk_themes")]
+		/// Get Theme info -- name, properties, and inherit flag
 		fn theme(
 			&self,
 			base_id: RmrkBaseId,
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	RmrkBoundedTheme,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) -> DispatchResult {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 funds to perform action431		NotSufficientFounds,432433		/// User not passed nesting rule434		UserIsNotAllowedToNest,435		/// Only tokens from specific collections may nest tokens under this436		SourceCollectionIsNotAllowedToNest,437438		/// Tried to store more data than allowed in collection field439		CollectionFieldSizeExceeded,440441		/// Tried to store more property data than allowed442		NoSpaceForProperty,443444		/// Tried to store more property keys than allowed445		PropertyLimitReached,446447		/// Property key is too long448		PropertyKeyIsTooLong,449450		/// Only ASCII letters, digits, and '_', '-' are allowed451		InvalidCharacterInPropertyKey,452453		/// Empty property keys are forbidden454		EmptyPropertyKey,455456		/// Tried to access an external collection with an internal API457		CollectionIsExternal,458459		/// Tried to access an internal collection with an external API460		CollectionIsInternal,461	}462463	#[pallet::storage]464	pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;465	#[pallet::storage]466	pub type DestroyedCollectionCount<T> =467		StorageValue<Value = CollectionId, QueryKind = ValueQuery>;468469	/// Collection info470	#[pallet::storage]471	pub type CollectionById<T> = StorageMap<472		Hasher = Blake2_128Concat,473		Key = CollectionId,474		Value = Collection<<T as frame_system::Config>::AccountId>,475		QueryKind = OptionQuery,476	>;477478	/// Collection properties479	#[pallet::storage]480	#[pallet::getter(fn collection_properties)]481	pub type CollectionProperties<T> = StorageMap<482		Hasher = Blake2_128Concat,483		Key = CollectionId,484		Value = Properties,485		QueryKind = ValueQuery,486		OnEmpty = up_data_structs::CollectionProperties,487	>;488489	#[pallet::storage]490	#[pallet::getter(fn property_permissions)]491	pub type CollectionPropertyPermissions<T> = StorageMap<492		Hasher = Blake2_128Concat,493		Key = CollectionId,494		Value = PropertiesPermissionMap,495		QueryKind = ValueQuery,496	>;497498	#[pallet::storage]499	pub type AdminAmount<T> = StorageMap<500		Hasher = Blake2_128Concat,501		Key = CollectionId,502		Value = u32,503		QueryKind = ValueQuery,504	>;505506	/// List of collection admins507	#[pallet::storage]508	pub type IsAdmin<T: Config> = StorageNMap<509		Key = (510			Key<Blake2_128Concat, CollectionId>,511			Key<Blake2_128Concat, T::CrossAccountId>,512		),513		Value = bool,514		QueryKind = ValueQuery,515	>;516517	/// Allowlisted collection users518	#[pallet::storage]519	pub type Allowlist<T: Config> = StorageNMap<520		Key = (521			Key<Blake2_128Concat, CollectionId>,522			Key<Blake2_128Concat, T::CrossAccountId>,523		),524		Value = bool,525		QueryKind = ValueQuery,526	>;527528	/// Not used by code, exists only to provide some types to metadata529	#[pallet::storage]530	pub type DummyStorageValue<T: Config> = StorageValue<531		Value = (532			CollectionStats,533			CollectionId,534			TokenId,535			TokenChild,536			PhantomType<(537				TokenData<T::CrossAccountId>,538				RpcCollection<T::AccountId>,539				// RMRK540				RmrkCollectionInfo<T::AccountId>,541				RmrkInstanceInfo<T::AccountId>,542				RmrkResourceInfo,543				RmrkPropertyInfo,544				RmrkBaseInfo<T::AccountId>,545				RmrkPartType,546				RmrkBoundedTheme,547				RmrkNftChild,548			)>,549		),550		QueryKind = OptionQuery,551	>;552553	#[pallet::hooks]554	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {555		fn on_runtime_upgrade() -> Weight {556			if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {557				use up_data_structs::{CollectionVersion1, CollectionVersion2};558				<CollectionById<T>>::translate::<CollectionVersion1<T::AccountId>, _>(|id, v| {559					let mut props = Vec::new();560					if !v.offchain_schema.is_empty() {561						props.push(Property {562							key: b"_old_offchainSchema".to_vec().try_into().unwrap(),563							value: v564								.offchain_schema565								.clone()566								.into_inner()567								.try_into()568								.expect("offchain schema too big"),569						});570					}571					if !v.variable_on_chain_schema.is_empty() {572						props.push(Property {573							key: b"_old_variableOnChainSchema".to_vec().try_into().unwrap(),574							value: v575								.variable_on_chain_schema576								.clone()577								.into_inner()578								.try_into()579								.expect("offchain schema too big"),580						});581					}582					if !v.const_on_chain_schema.is_empty() {583						props.push(Property {584							key: b"_old_constOnChainSchema".to_vec().try_into().unwrap(),585							value: v586								.const_on_chain_schema587								.clone()588								.into_inner()589								.try_into()590								.expect("offchain schema too big"),591						});592					}593					props.push(Property {594						key: b"_old_schemaVersion".to_vec().try_into().unwrap(),595						value: match v.schema_version {596							SchemaVersion::ImageURL => b"ImageUrl".as_slice(),597							SchemaVersion::Unique => b"Unique".as_slice(),598						}599						.to_vec()600						.try_into()601						.unwrap(),602					});603					Self::set_scoped_collection_properties(604						id,605						PropertyScope::None,606						props.into_iter(),607					)608					.expect("existing data larger than properties");609					let mut new = CollectionVersion2::from(v.clone());610					new.permissions.access = Some(v.access);611					new.permissions.mint_mode = Some(v.mint_mode);612					Some(new)613				});614			}615616			0617		}618	}619}620621impl<T: Config> Pallet<T> {622	/// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens623	pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {624		ensure!(625			&T::CrossAccountId::from_eth(H160([0; 20])) != receiver,626			<Error<T>>::AddressIsZero627		);628		Ok(())629	}630	pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {631		<IsAdmin<T>>::iter_prefix((collection,))632			.map(|(a, _)| a)633			.collect()634	}635	pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {636		<Allowlist<T>>::iter_prefix((collection,))637			.map(|(a, _)| a)638			.collect()639	}640	pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {641		<Allowlist<T>>::get((collection, user))642	}643	pub fn collection_stats() -> CollectionStats {644		let created = <CreatedCollectionCount<T>>::get();645		let destroyed = <DestroyedCollectionCount<T>>::get();646		CollectionStats {647			created: created.0,648			destroyed: destroyed.0,649			alive: created.0 - destroyed.0,650		}651	}652653	pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {654		let collection = <CollectionById<T>>::get(collection);655		if collection.is_none() {656			return None;657		}658659		let collection = collection.unwrap();660		let limits = collection.limits;661		let effective_limits = CollectionLimits {662			account_token_ownership_limit: Some(limits.account_token_ownership_limit()),663			sponsored_data_size: Some(limits.sponsored_data_size()),664			sponsored_data_rate_limit: Some(665				limits666					.sponsored_data_rate_limit667					.unwrap_or(SponsoringRateLimit::SponsoringDisabled),668			),669			token_limit: Some(limits.token_limit()),670			sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(671				match collection.mode {672					CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,673					CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,674					CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,675				},676			)),677			sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),678			owner_can_transfer: Some(limits.owner_can_transfer()),679			owner_can_destroy: Some(limits.owner_can_destroy()),680			transfers_enabled: Some(limits.transfers_enabled()),681		};682683		Some(effective_limits)684	}685686	pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {687		let Collection {688			name,689			description,690			owner,691			mode,692			token_prefix,693			sponsorship,694			limits,695			permissions,696			external_collection,697		} = <CollectionById<T>>::get(collection)?;698699		let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)700			.into_iter()701			.map(|(key, permission)| PropertyKeyPermission { key, permission })702			.collect();703704		let properties = <CollectionProperties<T>>::get(collection)705			.into_iter()706			.map(|(key, value)| Property { key, value })707			.collect();708709		let permissions = CollectionPermissions {710			access: Some(permissions.access()),711			mint_mode: Some(permissions.mint_mode()),712			nesting: Some(permissions.nesting().clone()),713		};714715		Some(RpcCollection {716			name: name.into_inner(),717			description: description.into_inner(),718			owner,719			mode,720			token_prefix: token_prefix.into_inner(),721			sponsorship,722			limits,723			permissions,724			token_property_permissions,725			properties,726			read_only: external_collection,727		})728	}729}730731macro_rules! limit_default {732	($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{733		$(734			if let Some($new) = $new.$field {735				let $old = $old.$field($($arg)?);736				let _ = $new;737				let _ = $old;738				$check739			} else {740				$new.$field = $old.$field741			}742		)*743	}};744}745macro_rules! limit_default_clone {746	($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{747		$(748			if let Some($new) = $new.$field.clone() {749				let $old = $old.$field($($arg)?);750				let _ = $new;751				let _ = $old;752				$check753			} else {754				$new.$field = $old.$field.clone()755			}756		)*757	}};758}759760impl<T: Config> Pallet<T> {761	pub fn init_collection(762		owner: T::CrossAccountId,763		data: CreateCollectionData<T::AccountId>,764		is_external: bool,765	) -> Result<CollectionId, DispatchError> {766		{767			ensure!(768				data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,769				Error::<T>::CollectionTokenPrefixLimitExceeded770			);771		}772773		let created_count = <CreatedCollectionCount<T>>::get()774			.0775			.checked_add(1)776			.ok_or(ArithmeticError::Overflow)?;777		let destroyed_count = <DestroyedCollectionCount<T>>::get().0;778		let id = CollectionId(created_count);779780		// bound Total number of collections781		ensure!(782			created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,783			<Error<T>>::TotalCollectionsLimitExceeded784		);785786		// =========787788		let collection = Collection {789			owner: owner.as_sub().clone(),790			name: data.name,791			mode: data.mode.clone(),792			description: data.description,793			token_prefix: data.token_prefix,794			sponsorship: data795				.pending_sponsor796				.map(SponsorshipState::Unconfirmed)797				.unwrap_or_default(),798			limits: data799				.limits800				.map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))801				.unwrap_or_else(|| Ok(CollectionLimits::default()))?,802			permissions: data803				.permissions804				.map(|permissions| {805					Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)806				})807				.unwrap_or_else(|| Ok(CollectionPermissions::default()))?,808			external_collection: is_external,809		};810811		let mut collection_properties = up_data_structs::CollectionProperties::get();812		collection_properties813			.try_set_from_iter(data.properties.into_iter())814			.map_err(<Error<T>>::from)?;815816		CollectionProperties::<T>::insert(id, collection_properties);817818		let mut token_props_permissions = PropertiesPermissionMap::new();819		token_props_permissions820			.try_set_from_iter(data.token_property_permissions.into_iter())821			.map_err(<Error<T>>::from)?;822823		CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);824825		// Take a (non-refundable) deposit of collection creation826		{827			let mut imbalance =828				<<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();829			imbalance.subsume(830				<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(831					&T::TreasuryAccountId::get(),832					T::CollectionCreationPrice::get(),833				),834			);835			<T as Config>::Currency::settle(836				&owner.as_sub(),837				imbalance,838				WithdrawReasons::TRANSFER,839				ExistenceRequirement::KeepAlive,840			)841			.map_err(|_| Error::<T>::NotSufficientFounds)?;842		}843844		<CreatedCollectionCount<T>>::put(created_count);845		<Pallet<T>>::deposit_event(Event::CollectionCreated(846			id,847			data.mode.id(),848			owner.as_sub().clone(),849		));850		<PalletEvm<T>>::deposit_log(851			erc::CollectionHelpersEvents::CollectionCreated {852				owner: *owner.as_eth(),853				collection_id: eth::collection_id_to_address(id),854			}855			.to_log(T::ContractAddress::get()),856		);857		<CollectionById<T>>::insert(id, collection);858		Ok(id)859	}860861	pub fn destroy_collection(862		collection: CollectionHandle<T>,863		sender: &T::CrossAccountId,864	) -> DispatchResult {865		ensure!(866			collection.limits.owner_can_destroy(),867			<Error<T>>::NoPermission,868		);869		collection.check_is_owner(sender)?;870871		let destroyed_collections = <DestroyedCollectionCount<T>>::get()872			.0873			.checked_add(1)874			.ok_or(ArithmeticError::Overflow)?;875876		// =========877878		<DestroyedCollectionCount<T>>::put(destroyed_collections);879		<CollectionById<T>>::remove(collection.id);880		<AdminAmount<T>>::remove(collection.id);881		<IsAdmin<T>>::remove_prefix((collection.id,), None);882		<Allowlist<T>>::remove_prefix((collection.id,), None);883		<CollectionProperties<T>>::remove(collection.id);884885		<Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));886		Ok(())887	}888889	pub fn set_collection_property(890		collection: &CollectionHandle<T>,891		sender: &T::CrossAccountId,892		property: Property,893	) -> DispatchResult {894		collection.check_is_owner_or_admin(sender)?;895896		CollectionProperties::<T>::try_mutate(collection.id, |properties| {897			let property = property.clone();898			properties.try_set(property.key, property.value)899		})900		.map_err(<Error<T>>::from)?;901902		Self::deposit_event(Event::CollectionPropertySet(collection.id, property.key));903904		Ok(())905	}906907	pub fn set_scoped_collection_property(908		collection_id: CollectionId,909		scope: PropertyScope,910		property: Property,911	) -> DispatchResult {912		CollectionProperties::<T>::try_mutate(collection_id, |properties| {913			properties.try_scoped_set(scope, property.key, property.value)914		})915		.map_err(<Error<T>>::from)?;916917		Ok(())918	}919920	pub fn set_scoped_collection_properties(921		collection_id: CollectionId,922		scope: PropertyScope,923		properties: impl Iterator<Item = Property>,924	) -> DispatchResult {925		CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {926			stored_properties.try_scoped_set_from_iter(scope, properties)927		})928		.map_err(<Error<T>>::from)?;929930		Ok(())931	}932933	#[transactional]934	pub fn set_collection_properties(935		collection: &CollectionHandle<T>,936		sender: &T::CrossAccountId,937		properties: Vec<Property>,938	) -> DispatchResult {939		for property in properties {940			Self::set_collection_property(collection, sender, property)?;941		}942943		Ok(())944	}945946	pub fn delete_collection_property(947		collection: &CollectionHandle<T>,948		sender: &T::CrossAccountId,949		property_key: PropertyKey,950	) -> DispatchResult {951		collection.check_is_owner_or_admin(sender)?;952953		CollectionProperties::<T>::try_mutate(collection.id, |properties| {954			properties.remove(&property_key)955		})956		.map_err(<Error<T>>::from)?;957958		Self::deposit_event(Event::CollectionPropertyDeleted(959			collection.id,960			property_key,961		));962963		Ok(())964	}965966	#[transactional]967	pub fn delete_collection_properties(968		collection: &CollectionHandle<T>,969		sender: &T::CrossAccountId,970		property_keys: Vec<PropertyKey>,971	) -> DispatchResult {972		for key in property_keys {973			Self::delete_collection_property(collection, sender, key)?;974		}975976		Ok(())977	}978979	// For migrations980	pub fn set_property_permission_unchecked(981		collection: CollectionId,982		property_permission: PropertyKeyPermission,983	) -> DispatchResult {984		<CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {985			permissions.try_set(property_permission.key, property_permission.permission)986		})987		.map_err(<Error<T>>::from)?;988		Ok(())989	}990991	pub fn set_property_permission(992		collection: &CollectionHandle<T>,993		sender: &T::CrossAccountId,994		property_permission: PropertyKeyPermission,995	) -> DispatchResult {996		collection.check_is_owner_or_admin(sender)?;997998		let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);999		let current_permission = all_permissions.get(&property_permission.key);1000		if matches![1001			current_permission,1002			Some(PropertyPermission { mutable: false, .. })1003		] {1004			return Err(<Error<T>>::NoPermission.into());1005		}10061007		CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1008			let property_permission = property_permission.clone();1009			permissions.try_set(property_permission.key, property_permission.permission)1010		})1011		.map_err(<Error<T>>::from)?;10121013		Self::deposit_event(Event::PropertyPermissionSet(1014			collection.id,1015			property_permission.key,1016		));10171018		Ok(())1019	}10201021	#[transactional]1022	pub fn set_token_property_permissions(1023		collection: &CollectionHandle<T>,1024		sender: &T::CrossAccountId,1025		property_permissions: Vec<PropertyKeyPermission>,1026	) -> DispatchResult {1027		for prop_pemission in property_permissions {1028			Self::set_property_permission(collection, sender, prop_pemission)?;1029		}10301031		Ok(())1032	}10331034	pub fn get_collection_property(1035		collection_id: CollectionId,1036		key: &PropertyKey,1037	) -> Option<PropertyValue> {1038		Self::collection_properties(collection_id).get(key).cloned()1039	}10401041	pub fn bytes_keys_to_property_keys(1042		keys: Vec<Vec<u8>>,1043	) -> Result<Vec<PropertyKey>, DispatchError> {1044		keys.into_iter()1045			.map(|key| -> Result<PropertyKey, DispatchError> {1046				key.try_into()1047					.map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1048			})1049			.collect::<Result<Vec<PropertyKey>, DispatchError>>()1050	}10511052	pub fn filter_collection_properties(1053		collection_id: CollectionId,1054		keys: Option<Vec<PropertyKey>>,1055	) -> Result<Vec<Property>, DispatchError> {1056		let properties = Self::collection_properties(collection_id);10571058		let properties = keys1059			.map(|keys| {1060				keys.into_iter()1061					.filter_map(|key| {1062						properties.get(&key).map(|value| Property {1063							key,1064							value: value.clone(),1065						})1066					})1067					.collect()1068			})1069			.unwrap_or_else(|| {1070				properties1071					.into_iter()1072					.map(|(key, value)| Property { key, value })1073					.collect()1074			});10751076		Ok(properties)1077	}10781079	pub fn filter_property_permissions(1080		collection_id: CollectionId,1081		keys: Option<Vec<PropertyKey>>,1082	) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1083		let permissions = Self::property_permissions(collection_id);10841085		let key_permissions = keys1086			.map(|keys| {1087				keys.into_iter()1088					.filter_map(|key| {1089						permissions1090							.get(&key)1091							.map(|permission| PropertyKeyPermission {1092								key,1093								permission: permission.clone(),1094							})1095					})1096					.collect()1097			})1098			.unwrap_or_else(|| {1099				permissions1100					.into_iter()1101					.map(|(key, permission)| PropertyKeyPermission { key, permission })1102					.collect()1103			});11041105		Ok(key_permissions)1106	}11071108	pub fn toggle_allowlist(1109		collection: &CollectionHandle<T>,1110		sender: &T::CrossAccountId,1111		user: &T::CrossAccountId,1112		allowed: bool,1113	) -> DispatchResult {1114		collection.check_is_owner_or_admin(sender)?;11151116		// =========11171118		if allowed {1119			<Allowlist<T>>::insert((collection.id, user), true);1120		} else {1121			<Allowlist<T>>::remove((collection.id, user));1122		}11231124		Ok(())1125	}11261127	pub fn toggle_admin(1128		collection: &CollectionHandle<T>,1129		sender: &T::CrossAccountId,1130		user: &T::CrossAccountId,1131		admin: bool,1132	) -> DispatchResult {1133		collection.check_is_owner(sender)?;11341135		let was_admin = <IsAdmin<T>>::get((collection.id, user));1136		if was_admin == admin {1137			return Ok(());1138		}1139		let amount = <AdminAmount<T>>::get(collection.id);11401141		if admin {1142			let amount = amount1143				.checked_add(1)1144				.ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1145			ensure!(1146				amount <= Self::collection_admins_limit(),1147				<Error<T>>::CollectionAdminCountExceeded,1148			);11491150			// =========11511152			<AdminAmount<T>>::insert(collection.id, amount);1153			<IsAdmin<T>>::insert((collection.id, user), true);1154		} else {1155			<AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1156			<IsAdmin<T>>::remove((collection.id, user));1157		}11581159		Ok(())1160	}11611162	pub fn clamp_limits(1163		mode: CollectionMode,1164		old_limit: &CollectionLimits,1165		mut new_limit: CollectionLimits,1166	) -> Result<CollectionLimits, DispatchError> {1167		let limits = old_limit;1168		limit_default!(old_limit, new_limit,1169			account_token_ownership_limit => ensure!(1170				new_limit <= MAX_TOKEN_OWNERSHIP,1171				<Error<T>>::CollectionLimitBoundsExceeded,1172			),1173			sponsored_data_size => ensure!(1174				new_limit <= CUSTOM_DATA_LIMIT,1175				<Error<T>>::CollectionLimitBoundsExceeded,1176			),11771178			sponsored_data_rate_limit => {},1179			token_limit => ensure!(1180				old_limit >= new_limit && new_limit > 0,1181				<Error<T>>::CollectionTokenLimitExceeded1182			),11831184			sponsor_transfer_timeout(match mode {1185				CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1186				CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1187				CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1188			}) => ensure!(1189				new_limit <= MAX_SPONSOR_TIMEOUT,1190				<Error<T>>::CollectionLimitBoundsExceeded,1191			),1192			sponsor_approve_timeout => {},1193			owner_can_transfer => ensure!(1194				!limits.owner_can_transfer_instaled() ||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 => { /* todo check for permissive, if only it gets out of benchmarks */ },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_token_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 RefungibleExtensionsWeightInfo {1262	fn repartition() -> Weight;1263}12641265pub trait CommonCollectionOperations<T: Config> {1266	fn create_item(1267		&self,1268		sender: T::CrossAccountId,1269		to: T::CrossAccountId,1270		data: CreateItemData,1271		nesting_budget: &dyn Budget,1272	) -> DispatchResultWithPostInfo;1273	fn create_multiple_items(1274		&self,1275		sender: T::CrossAccountId,1276		to: T::CrossAccountId,1277		data: Vec<CreateItemData>,1278		nesting_budget: &dyn Budget,1279	) -> DispatchResultWithPostInfo;1280	fn create_multiple_items_ex(1281		&self,1282		sender: T::CrossAccountId,1283		data: CreateItemExData<T::CrossAccountId>,1284		nesting_budget: &dyn Budget,1285	) -> DispatchResultWithPostInfo;1286	fn burn_item(1287		&self,1288		sender: T::CrossAccountId,1289		token: TokenId,1290		amount: u128,1291	) -> DispatchResultWithPostInfo;1292	fn burn_item_recursively(1293		&self,1294		sender: T::CrossAccountId,1295		token: TokenId,1296		self_budget: &dyn Budget,1297		breadth_budget: &dyn Budget,1298	) -> DispatchResultWithPostInfo;1299	fn set_collection_properties(1300		&self,1301		sender: T::CrossAccountId,1302		properties: Vec<Property>,1303	) -> DispatchResultWithPostInfo;1304	fn delete_collection_properties(1305		&self,1306		sender: &T::CrossAccountId,1307		property_keys: Vec<PropertyKey>,1308	) -> DispatchResultWithPostInfo;1309	fn set_token_properties(1310		&self,1311		sender: T::CrossAccountId,1312		token_id: TokenId,1313		property: Vec<Property>,1314		nesting_budget: &dyn Budget,1315	) -> DispatchResultWithPostInfo;1316	fn delete_token_properties(1317		&self,1318		sender: T::CrossAccountId,1319		token_id: TokenId,1320		property_keys: Vec<PropertyKey>,1321		nesting_budget: &dyn Budget,1322	) -> DispatchResultWithPostInfo;1323	fn set_token_property_permissions(1324		&self,1325		sender: &T::CrossAccountId,1326		property_permissions: Vec<PropertyKeyPermission>,1327	) -> DispatchResultWithPostInfo;1328	fn transfer(1329		&self,1330		sender: T::CrossAccountId,1331		to: T::CrossAccountId,1332		token: TokenId,1333		amount: u128,1334		nesting_budget: &dyn Budget,1335	) -> DispatchResultWithPostInfo;1336	fn approve(1337		&self,1338		sender: T::CrossAccountId,1339		spender: T::CrossAccountId,1340		token: TokenId,1341		amount: u128,1342	) -> DispatchResultWithPostInfo;1343	fn transfer_from(1344		&self,1345		sender: T::CrossAccountId,1346		from: T::CrossAccountId,1347		to: T::CrossAccountId,1348		token: TokenId,1349		amount: u128,1350		nesting_budget: &dyn Budget,1351	) -> DispatchResultWithPostInfo;1352	fn burn_from(1353		&self,1354		sender: T::CrossAccountId,1355		from: T::CrossAccountId,1356		token: TokenId,1357		amount: u128,1358		nesting_budget: &dyn Budget,1359	) -> DispatchResultWithPostInfo;13601361	fn check_nesting(1362		&self,1363		sender: T::CrossAccountId,1364		from: (CollectionId, TokenId),1365		under: TokenId,1366		nesting_budget: &dyn Budget,1367	) -> DispatchResult;13681369	fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));13701371	fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));13721373	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;1374	fn collection_tokens(&self) -> Vec<TokenId>;1375	fn token_exists(&self, token: TokenId) -> bool;1376	fn last_token_id(&self) -> TokenId;13771378	fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;1379	fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;1380	fn token_properties(&self, token_id: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;1381	/// Amount of unique collection tokens1382	fn total_supply(&self) -> u32;1383	/// Amount of different tokens account has (Applicable to nonfungible/refungible)1384	fn account_balance(&self, account: T::CrossAccountId) -> u32;1385	/// Amount of specific token account have (Applicable to fungible/refungible)1386	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;1387	/// Amount of token pieces1388	fn total_pieces(&self, token: TokenId) -> Option<u128>;1389	fn allowance(1390		&self,1391		sender: T::CrossAccountId,1392		spender: T::CrossAccountId,1393		token: TokenId,1394	) -> u128;1395	fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;1396}13971398pub trait RefungibleExtensions<T>1399where1400	T: Config,1401{1402	fn repartition(1403		&self,1404		owner: &T::CrossAccountId,1405		token: TokenId,1406		amount: u128,1407	) -> DispatchResultWithPostInfo;1408}14091410// Flexible enough for implementing CommonCollectionOperations1411pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {1412	let post_info = PostDispatchInfo {1413		actual_weight: Some(weight),1414		pays_fee: Pays::Yes,1415	};1416	match res {1417		Ok(()) => Ok(post_info),1418		Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),1419	}1420}14211422impl<T: Config> From<PropertiesError> for Error<T> {1423	fn from(error: PropertiesError) -> Self {1424		match error {1425			PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,1426			PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,1427			PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,1428			PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,1429			PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,1430		}1431	}1432}
modifiedpallets/fungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -44,6 +44,7 @@
 pub mod erc;
 pub mod weights;
 
+/// todo:doc?
 pub type CreateItemData<T> = (<T as pallet_evm::account::Config>::CrossAccountId, u128);
 pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
 
@@ -78,10 +79,12 @@
 	#[pallet::generate_store(pub(super) trait Store)]
 	pub struct Pallet<T>(_);
 
+	/// Total amount of fungible tokens inside a collection.
 	#[pallet::storage]
 	pub type TotalSupply<T: Config> =
 		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u128, QueryKind = ValueQuery>;
 
+	/// Amount of tokens owned by an account inside a collection.
 	#[pallet::storage]
 	pub type Balance<T: Config> = StorageNMap<
 		Key = (
@@ -92,6 +95,7 @@
 		QueryKind = ValueQuery,
 	>;
 
+	/// todo:doc
 	#[pallet::storage]
 	pub type Allowance<T: Config> = StorageNMap<
 		Key = (
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -56,6 +56,8 @@
 pub type CreateItemData<T> = CreateNftExData<<T as pallet_evm::account::Config>::CrossAccountId>;
 pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
 
+/// Token data, stored independently from other data used to describe it.
+/// Notably contains the owner account address.
 #[struct_versioning::versioned(version = 2, upper)]
 #[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]
 pub struct ItemData<CrossAccountId> {
@@ -102,13 +104,17 @@
 	#[pallet::generate_store(pub(super) trait Store)]
 	pub struct Pallet<T>(_);
 
+	/// Total amount of minted tokens in a collection.
 	#[pallet::storage]
 	pub type TokensMinted<T: Config> =
 		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
+
+	/// Amount of burnt tokens in a collection.
 	#[pallet::storage]
 	pub type TokensBurnt<T: Config> =
 		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
 
+	/// Token data, used to partially describe a token.
 	#[pallet::storage]
 	pub type TokenData<T: Config> = StorageNMap<
 		Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
@@ -116,6 +122,7 @@
 		QueryKind = OptionQuery,
 	>;
 
+	/// Key-value pairs, describing the metadata of a token.
 	#[pallet::storage]
 	#[pallet::getter(fn token_properties)]
 	pub type TokenProperties<T: Config> = StorageNMap<
@@ -125,6 +132,7 @@
 		OnEmpty = up_data_structs::TokenProperties,
 	>;
 
+	/// Scoped, auxiliary properties of a token, primarily used for on-chain operations.
 	#[pallet::storage]
 	#[pallet::getter(fn token_aux_property)]
 	pub type TokenAuxProperties<T: Config> = StorageNMap<
@@ -138,7 +146,7 @@
 		QueryKind = OptionQuery,
 	>;
 
-	/// Used to enumerate tokens owned by account
+	/// Used to enumerate tokens owned by account.
 	#[pallet::storage]
 	pub type Owned<T: Config> = StorageNMap<
 		Key = (
@@ -150,7 +158,7 @@
 		QueryKind = ValueQuery,
 	>;
 
-	/// Used to enumerate token's children
+	/// Used to enumerate token's children.
 	#[pallet::storage]
 	#[pallet::getter(fn token_children)]
 	pub type TokenChildren<T: Config> = StorageNMap<
@@ -163,6 +171,7 @@
 		QueryKind = ValueQuery,
 	>;
 
+	/// Amount of tokens owned in a collection.s
 	#[pallet::storage]
 	pub type AccountBalance<T: Config> = StorageNMap<
 		Key = (
@@ -173,6 +182,7 @@
 		QueryKind = ValueQuery,
 	>;
 
+	/// todo doc
 	#[pallet::storage]
 	pub type Allowance<T: Config> = StorageNMap<
 		Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
@@ -180,6 +190,7 @@
 		QueryKind = OptionQuery,
 	>;
 
+	/// Upgrade from the old schema to properties.
 	#[pallet::hooks]
 	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
 		fn on_runtime_upgrade() -> Weight {
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -38,6 +38,8 @@
 pub mod weights;
 pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
 
+/// Token data, stored independently from other data used to describe it.
+/// Notably contains the token metadata.
 #[struct_versioning::versioned(version = 2, upper)]
 #[derive(Encode, Decode, Default, TypeInfo, MaxEncodedLen)]
 pub struct ItemData {
@@ -86,13 +88,17 @@
 	#[pallet::generate_store(pub(super) trait Store)]
 	pub struct Pallet<T>(_);
 
+	/// Total amount of minted tokens in a collection.
 	#[pallet::storage]
 	pub type TokensMinted<T: Config> =
 		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
+	
+	/// Amount of tokens burnt in a collection.
 	#[pallet::storage]
 	pub type TokensBurnt<T: Config> =
 		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
 
+	/// Token data, used to partially describe a token.
 	#[pallet::storage]
 	pub type TokenData<T: Config> = StorageNMap<
 		Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
@@ -100,6 +106,7 @@
 		QueryKind = ValueQuery,
 	>;
 
+	/// Amount of pieces a refungible token is split into.
 	#[pallet::storage]
 	pub type TotalSupply<T: Config> = StorageNMap<
 		Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
@@ -107,7 +114,7 @@
 		QueryKind = ValueQuery,
 	>;
 
-	/// Used to enumerate tokens owned by account
+	/// Used to enumerate tokens owned by account.
 	#[pallet::storage]
 	pub type Owned<T: Config> = StorageNMap<
 		Key = (
@@ -119,6 +126,7 @@
 		QueryKind = ValueQuery,
 	>;
 
+	/// Amount of tokens (not pieces) partially owned by an account within a collection.
 	#[pallet::storage]
 	pub type AccountBalance<T: Config> = StorageNMap<
 		Key = (
@@ -130,6 +138,7 @@
 		QueryKind = ValueQuery,
 	>;
 
+	/// Amount of pieces of a token owned by an account.
 	#[pallet::storage]
 	pub type Balance<T: Config> = StorageNMap<
 		Key = (
@@ -142,6 +151,7 @@
 		QueryKind = ValueQuery,
 	>;
 
+	/// todo:doc
 	#[pallet::storage]
 	pub type Allowance<T: Config> = StorageNMap<
 		Key = (
@@ -248,7 +258,7 @@
 		// TODO: ERC721 transfer event
 		Ok(())
 	}
-
+	
 	pub fn burn(
 		collection: &RefungibleHandle<T>,
 		owner: &T::CrossAccountId,
@@ -595,6 +605,7 @@
 		Ok(())
 	}
 
+	/// todo:doc oh look, a precedent. not pub, too. but it has an unclear use-case.
 	/// Returns allowance, which should be set after transaction
 	fn check_allowed(
 		collection: &RefungibleHandle<T>,
modifiedpallets/scheduler/src/lib.rsdiffbeforeafterboth
--- a/pallets/scheduler/src/lib.rs
+++ b/pallets/scheduler/src/lib.rs
@@ -258,6 +258,7 @@
 
 	/// A Scheduler-Runtime interface for finer payment handling.
 	pub trait DispatchCall<T: frame_system::Config + Config, SelfContainedSignedInfo> {
+		/// Reserve the maximum spendings on a call.
 		fn reserve_balance(
 			id: ScheduledId,
 			sponsor: <T as frame_system::Config>::AccountId,
@@ -265,6 +266,7 @@
 			count: u32,
 		) -> Result<(), DispatchError>;
 
+		/// Pay for call dispatch (un-reserve) from the reserved funds, returning the change.
 		fn pay_for_call(
 			id: ScheduledId,
 			sponsor: <T as frame_system::Config>::AccountId,
@@ -280,6 +282,7 @@
 			TransactionValidityError,
 		>;
 
+		/// Release reserved funds.
 		fn cancel_reserve(
 			id: ScheduledId,
 			sponsor: <T as frame_system::Config>::AccountId,
modifiedpallets/structure/src/lib.rsdiffbeforeafterboth
--- a/pallets/structure/src/lib.rs
+++ b/pallets/structure/src/lib.rs
@@ -25,19 +25,19 @@
 
 	#[pallet::error]
 	pub enum Error<T> {
-		/// While searched for owner, got already checked account
+		/// While searching for the owner, encountered an already checked account, detecting a loop.
 		OuroborosDetected,
-		/// While searched for owner, encountered depth limit
+		/// While searching for the owner, reached the depth limit.
 		DepthLimit,
-		/// While iterating over children, encountered breadth limit
+		/// While iterating over children, reached the breadth limit.
 		BreadthLimit,
-		/// While searched for owner, found token owner by not-yet-existing token
+		/// Couldn't find the token owner that is a token. Perhaps, it does not yet exist. todo:doc? rephrase?
 		TokenNotFound,
 	}
 
 	#[pallet::event]
 	pub enum Event<T> {
-		/// Executed call on behalf of token
+		/// Executed call on behalf of the token.
 		Executed(DispatchResult),
 	}
 
@@ -73,11 +73,11 @@
 
 #[derive(PartialEq)]
 pub enum Parent<CrossAccountId> {
-	/// Token owned by normal account
+	/// Token owned by a normal account.
 	User(CrossAccountId),
-	/// Passed token not found
+	/// Could not find the token provided as the owner.
 	TokenNotFound,
-	/// Token owner is another token (target token still may not exist)
+	/// Token owner is another token (still, the target token may not exist).
 	Token(CollectionId, TokenId),
 }
 
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -101,7 +101,7 @@
 		/// * admin:  Admin address.
 		CollectionAdminAdded(CollectionId, CrossAccountId),
 
-		/// Collection owned was change
+		/// Collection owned was changed
 		///
 		/// # Arguments
 		///
@@ -137,7 +137,7 @@
 		/// * admin:  Admin address.
 		CollectionAdminRemoved(CollectionId, CrossAccountId),
 
-		/// Address was remove from allow list
+		/// Address was removed from the allow list
 		///
 		/// # Arguments
 		///
@@ -146,7 +146,7 @@
 		/// * user:  Address.
 		AllowListAddressRemoved(CollectionId, CrossAccountId),
 
-		/// Address was add to allow list
+		/// Address was added to the allow list
 		///
 		/// # Arguments
 		///
@@ -155,13 +155,18 @@
 		/// * user:  Address.
 		AllowListAddressAdded(CollectionId, CrossAccountId),
 
-		/// Collection limits was set
+		/// Collection limits were set
 		///
 		/// # Arguments
 		///
 		/// * collection_id: Globally unique collection identifier.
 		CollectionLimitSet(CollectionId),
 
+		/// Collection permissions were set
+		/// 
+		/// # Arguments
+		/// 
+		/// * collection_id: Globally unique collection identifier.
 		CollectionPermissionSet(CollectionId),
 	}
 }
@@ -198,7 +203,7 @@
 		ChainVersion: u64;
 		//#endregion
 
-		//#region Tokens transfer rate limit baskets
+		//#region Tokens transfer sponosoring rate limit baskets
 		/// (Collection id (controlled?2), who created (real))
 		/// TODO: Off chain worker should remove from this map when collection gets removed
 		pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;
@@ -214,11 +219,14 @@
 		/// Collection id (controlled?2), token id (controlled?2)
 		#[deprecated]
 		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
+		/// Last sponsoring of token property setting // todo:doc rephrase this and the following 
 		pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
 
-		/// Approval sponsoring
+		/// Last sponsoring of NFT approval in a collection
 		pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
+		/// Last sponsoring of fungible tokens approval in a collection
 		pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
+		/// Last sponsoring of RFT approval in a collection
 		pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
 	}
 }
@@ -278,9 +286,16 @@
 			Self::create_collection_ex(origin, data)
 		}
 
-		/// This method creates a collection
+		/// Create a collection with explicit parameters.
+		/// Prefer it to the deprecated [`created_collection`] method.
+		///
+		/// # Permissions
+		///
+		/// * Anyone.
 		///
-		/// Prefer it to deprecated [`created_collection`] method
+		/// # Arguments
+		/// 
+		/// * data: explicit create-collection data.
 		#[weight = <SelfWeightOf<T>>::create_collection()]
 		#[transactional]
 		pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {
@@ -293,11 +308,11 @@
 			Ok(())
 		}
 
-		/// Destroys collection if no tokens within this collection
+		/// Destroy the collection if no tokens exist within.
 		///
 		/// # Permissions
 		///
-		/// * Collection Owner.
+		/// * Collection Owner
 		///
 		/// # Arguments
 		///
@@ -398,7 +413,7 @@
 		///
 		/// # Permissions
 		///
-		/// * Collection Owner.
+		/// * Collection Owner
 		///
 		/// # Arguments
 		///
@@ -424,40 +439,40 @@
 			target_collection.save()
 		}
 
-		/// Adds an admin of the Collection.
+		/// Adds an admin of the collection.
 		/// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership.
 		///
 		/// # Permissions
 		///
-		/// * Collection Owner.
-		/// * Collection Admin.
+		/// * Collection Owner
+		/// * Collection Admin
 		///
 		/// # Arguments
 		///
 		/// * collection_id: ID of the Collection to add admin for.
 		///
-		/// * new_admin_id: Address of new admin to add.
+		/// * new_admin: Address of new admin to add.
 		#[weight = <SelfWeightOf<T>>::add_collection_admin()]
 		#[transactional]
-		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {
+		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin: T::CrossAccountId) -> DispatchResult {
 			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
 			let collection = <CollectionHandle<T>>::try_get(collection_id)?;
 			collection.check_is_internal()?;
 
 			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(
 				collection_id,
-				new_admin_id.clone()
+				new_admin.clone()
 			));
 
-			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)
+			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin, true)
 		}
 
 		/// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.
 		///
 		/// # Permissions
 		///
-		/// * Collection Owner.
-		/// * Collection Admin.
+		/// * Collection Owner
+		/// * Collection Admin
 		///
 		/// # Arguments
 		///
@@ -479,9 +494,12 @@
 			<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)
 		}
 
+		/// Set (invite) a new collection sponsor. If successful, confirmation from the sponsor-to-be will be pending.
+		/// 
 		/// # Permissions
 		///
 		/// * Collection Owner
+		/// * Collection Admin
 		///
 		/// # Arguments
 		///
@@ -507,9 +525,11 @@
 			target_collection.save()
 		}
 
+		/// Confirm own sponsorship of a collection.
+		/// 
 		/// # Permissions
 		///
-		/// * Sponsor.
+		/// * The sponsor to-be
 		///
 		/// # Arguments
 		///
@@ -538,7 +558,7 @@
 		///
 		/// # Permissions
 		///
-		/// * Collection owner.
+		/// * Collection Owner
 		///
 		/// # Arguments
 		///
@@ -560,12 +580,12 @@
 			target_collection.save()
 		}
 
-		/// This method creates a concrete instance of NFT Collection created with CreateCollection method.
+		/// Create a concrete instance of NFT Collection created with CreateCollection method.
 		///
 		/// # Permissions
 		///
-		/// * Collection Owner.
-		/// * Collection Admin.
+		/// * Collection Owner
+		/// * Collection Admin
 		/// * Anyone if
 		///     * Allow List is enabled, and
 		///     * Address is added to allow list, and
@@ -587,12 +607,12 @@
 			dispatch_tx::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))
 		}
 
-		/// This method creates multiple items in a collection created with CreateCollection method.
+		/// Create multiple items in a collection created with CreateCollection method.
 		///
 		/// # Permissions
 		///
-		/// * Collection Owner.
-		/// * Collection Admin.
+		/// * Collection Owner
+		/// * Collection Admin
 		/// * Anyone if
 		///     * Allow List is enabled, and
 		///     * Address is added to allow list, and
@@ -615,6 +635,18 @@
 			dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))
 		}
 
+		/// Add or change collection properties.
+		///
+		/// # Permissions
+		///
+		/// * Collection Owner
+		/// * Collection Admin
+		///
+		/// # Arguments
+		///
+		/// * collection_id.
+		///
+		/// * properties: a vector of key-value pairs stored as the collection's metadata. Keys support Latin letters, '-', '_', and '.' as symbols.
 		#[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]
 		#[transactional]
 		pub fn set_collection_properties(
@@ -629,6 +661,18 @@
 			dispatch_tx::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))
 		}
 
+		/// Delete specified collection properties.
+		///
+		/// # Permissions
+		///
+		/// * Collection Owner
+		/// * Collection Admin
+		///
+		/// # Arguments
+		///
+		/// * collection_id.
+		///
+		/// * property_keys: a vector of keys of the properties to be deleted.
 		#[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]
 		#[transactional]
 		pub fn delete_collection_properties(
@@ -643,6 +687,22 @@
 			dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))
 		}
 
+		/// Add or change token properties according to collection's permissions.
+		///
+		/// # Permissions
+		///
+		/// * Depends on collection's token property permissions and specified property mutability:
+		/// 	* Collection Owner
+		/// 	* Collection Admin
+		/// 	* Token Owner
+		///
+		/// # Arguments
+		///
+		/// * collection_id.
+		/// 
+		/// * token_id.
+		///
+		/// * properties: a vector of key-value pairs stored as the token's metadata. Keys support Latin letters, '-', '_', and '.' as symbols.
 		#[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]
 		#[transactional]
 		pub fn set_token_properties(
@@ -659,6 +719,22 @@
 			dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget))
 		}
 
+		/// Delete specified token properties.
+		///
+		/// # Permissions
+		///
+		/// * Depends on collection's token property permissions and specified property mutability:
+		/// 	* Collection Owner
+		/// 	* Collection Admin
+		/// 	* Token Owner
+		///
+		/// # Arguments
+		///
+		/// * collection_id.
+		///
+		/// * token_id.
+		///
+		/// * property_keys: a vector of keys of the properties to be deleted.
 		#[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]
 		#[transactional]
 		pub fn delete_token_properties(
@@ -675,6 +751,18 @@
 			dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys, &budget))
 		}
 
+		/// Add or change token property permissions of a collection.
+		///
+		/// # Permissions
+		///
+		/// * Collection Owner
+		/// * Collection Admin
+		///
+		/// # Arguments
+		///
+		/// * collection_id.
+		///
+		/// * property_permissions: a vector of permissions for property keys. Keys support Latin letters, '-', '_', and '.' as symbols.
 		#[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]
 		#[transactional]
 		pub fn set_token_property_permissions(
@@ -689,6 +777,22 @@
 			dispatch_tx::<T, _>(collection_id, |d| d.set_token_property_permissions(&sender, property_permissions))
 		}
 
+		/// Create multiple items inside a collection with explicitly specified initial parameters.
+		/// 
+		/// # Permissions
+		///
+		/// * Collection Owner
+		/// * Collection Admin
+		/// * Anyone if
+		///     * Allow List is enabled, and
+		///     * Address is added to allow list, and
+		///     * MintPermission is enabled (see SetMintPermission method)
+		///
+		/// # Arguments
+		///
+		/// * collection_id: ID of the collection.
+		///
+		/// * data: explicit item creation data. 
 		#[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]
 		#[transactional]
 		pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {
@@ -698,11 +802,11 @@
 			dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))
 		}
 
-		/// Set transfers_enabled value for particular collection
+		/// Set transfers_enabled value for particular collection.
 		///
 		/// # Permissions
 		///
-		/// * Collection Owner.
+		/// * Collection Owner
 		///
 		/// # Arguments
 		///
@@ -723,13 +827,13 @@
 			target_collection.save()
 		}
 
-		/// Destroys a concrete instance of NFT.
+		/// Destroy a concrete instance of NFT.
 		///
 		/// # Permissions
 		///
-		/// * Collection Owner.
-		/// * Collection Admin.
-		/// * Current NFT Owner.
+		/// * Collection Owner
+		/// * Collection Admin
+		/// * Current NFT Owner
 		///
 		/// # Arguments
 		///
@@ -752,7 +856,7 @@
 			Ok(post_info)
 		}
 
-		/// Destroys a concrete instance of NFT on behalf of the owner
+		/// Destroy a concrete instance of NFT on behalf of the owner.
 		/// See also: [`approve`]
 		///
 		/// # Permissions
@@ -835,6 +939,7 @@
 		/// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.
 		///
 		/// # Permissions
+		/// 
 		/// * Collection Owner
 		/// * Collection Admin
 		/// * Current NFT owner
@@ -860,6 +965,18 @@
 			dispatch_tx::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))
 		}
 
+		/// Set specific limits of a collection. Empty, or None fields mean chain default.
+		///.
+		/// # Permissions
+		/// 
+		/// * Collection Owner
+		/// * Collection Admin
+		///
+		/// # Arguments
+		///
+		/// * collection_id.
+		///
+		/// * new_limit: The new limits of the collection. They will overwrite the current ones.
 		#[weight = <SelfWeightOf<T>>::set_collection_limits()]
 		#[transactional]
 		pub fn set_collection_limits(
@@ -882,12 +999,24 @@
 			target_collection.save()
 		}
 
+		/// Set specific permissions of a collection. Empty, or None fields mean chain default.
+		///
+		/// # Permissions
+		/// 
+		/// * Collection Owner
+		/// * Collection Admin
+		///
+		/// # Arguments
+		///
+		/// * collection_id.
+		///
+		/// * new_permission: The new permissions of the collection. They will overwrite the current ones.
 		#[weight = <SelfWeightOf<T>>::set_collection_limits()]
 		#[transactional]
 		pub fn set_collection_permissions(
 			origin,
 			collection_id: CollectionId,
-			new_limit: CollectionPermissions,
+			new_permission: CollectionPermissions,
 		) -> DispatchResult {
 			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
 			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
@@ -895,7 +1024,7 @@
 			target_collection.check_is_owner_or_admin(&sender)?;
 			let old_limit = &target_collection.permissions;
 
-			target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;
+			target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_permission)?;
 
 			<Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(
 				collection_id
@@ -904,6 +1033,19 @@
 			target_collection.save()
 		}
 
+		/// Re-partition a refungible token, while owning all of its parts.
+		///
+		/// # Permissions
+		/// 
+		/// * Token Owner (must own every part)
+		///
+		/// # Arguments
+		///
+		/// * collection_id.
+		/// 
+		/// * token: the ID of the RFT. 
+		///
+		/// * amount: The new number of parts into which the token shall be partitioned.
 		#[weight = T::RefungibleExtensionsWeightInfo::repartition()]
 		#[transactional]
 		pub fn repartition(
modifiedprimitives/data-structs/src/lib.rsdiffbeforeafterboth
--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -197,7 +197,6 @@
 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub enum CollectionMode {
 	NFT,
-	// decimal points
 	Fungible(DecimalPoints),
 	ReFungible,
 }
@@ -252,12 +251,14 @@
 pub enum SponsorshipState<AccountId> {
 	/// The fees are applied to the transaction sender
 	Disabled,
+	/// Pending confirmation from a sponsor-to-be
 	Unconfirmed(AccountId),
 	/// Transactions are sponsored by specified account
 	Confirmed(AccountId),
 }
 
 impl<AccountId> SponsorshipState<AccountId> {
+	/// Get the acting sponsor account, if present
 	pub fn sponsor(&self) -> Option<&AccountId> {
 		match self {
 			Self::Confirmed(sponsor) => Some(sponsor),
@@ -265,6 +266,7 @@
 		}
 	}
 
+	/// Get the sponsor account currently pending confirmation, if present
 	pub fn pending_sponsor(&self) -> Option<&AccountId> {
 		match self {
 			Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),
@@ -272,6 +274,7 @@
 		}
 	}
 
+	/// Is sponsorship set and acting
 	pub fn confirmed(&self) -> bool {
 		matches!(self, Self::Confirmed(_))
 	}
@@ -283,7 +286,7 @@
 	}
 }
 
-/// Used in storage
+/// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version)
 #[struct_versioning::versioned(version = 2, upper)]
 #[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
 pub struct Collection<AccountId> {
@@ -324,7 +327,7 @@
 	pub meta_update_permission: MetaUpdatePermission,
 }
 
-/// Used in RPC calls
+/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version)
 #[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]
 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub struct RpcCollection<AccountId> {
@@ -362,12 +365,15 @@
 
 pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;
 
-/// All fields are wrapped in `Option`s, where None means chain default
+/// Limits and restrictions of a collection.
+/// All fields are wrapped in `Option`s, where None means chain default.
 // When adding/removing fields from this struct - don't forget to also update clamp_limits
 #[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub struct CollectionLimits {
+	/// Maximum number of owned tokens per account
 	pub account_token_ownership_limit: Option<u32>,
+	/// Maximum size of data of a sponsored transaction
 	pub sponsored_data_size: Option<u32>,
 
 	/// FIXME should we delete this or repurpose it?
@@ -375,13 +381,18 @@
 	/// Some(v) - setVariableMetadata is sponsored
 	///           if there is v block between txs
 	pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,
+	/// Maximum amount of tokens inside the collection
 	pub token_limit: Option<u32>,
 
-	// Timeouts for item types in passed blocks
+	/// Timeout for sponsoring a token transfer in passed blocks
 	pub sponsor_transfer_timeout: Option<u32>,
+	/// Timeout for sponsoring an approval in passed blocks
 	pub sponsor_approve_timeout: Option<u32>,
+	/// Can a token be transferred by the owner
 	pub owner_can_transfer: Option<bool>,
+	/// Can a token be burned by the owner
 	pub owner_can_destroy: Option<bool>,
+	/// Can a token be transferred at all
 	pub transfers_enabled: Option<bool>,
 }
 
@@ -509,6 +520,7 @@
 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub enum SponsoringRateLimit {
 	SponsoringDisabled,
+	/// Once per how many blocks can sponsorship of a transaction type occur
 	Blocks(u32),
 }
 
@@ -516,6 +528,7 @@
 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 #[derivative(Debug)]
 pub struct CreateNftData {
+	/// Key-value pairs used to describe the token as metadata
 	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
 	#[derivative(Debug(format_with = "bounded::vec_debug"))]
 	pub properties: CollectionPropertiesVec,
@@ -524,6 +537,7 @@
 #[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]
 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub struct CreateFungibleData {
+	/// Number of fungible tokens minted
 	pub value: u128,
 }
 
@@ -534,6 +548,7 @@
 	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
 	#[derivative(Debug(format_with = "bounded::vec_debug"))]
 	pub const_data: BoundedVec<u8, CustomDataLimit>,
+	/// Number of pieces the RFT is split into
 	pub pieces: u128,
 }
 
@@ -553,6 +568,7 @@
 	ReFungible(CreateReFungibleData),
 }
 
+/// Explicit NFT creation data with meta parameters
 #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
 #[derivative(Debug)]
 pub struct CreateNftExData<CrossAccountId> {
@@ -561,6 +577,7 @@
 	pub owner: CrossAccountId,
 }
 
+/// Explicit RFT creation data with meta parameters
 #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
 #[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]
 pub struct CreateRefungibleExData<CrossAccountId> {
@@ -570,6 +587,7 @@
 	pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,
 }
 
+/// Explicit item creation data with meta parameters, namely the owner
 #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
 #[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]
 pub enum CreateItemExData<CrossAccountId> {
@@ -617,6 +635,7 @@
 	}
 }
 
+/// Token's address, dictated by its collection and token IDs
 #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]
 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 // todo possibly rename to be used generally as an address pair
modifiedtests/src/interfaces/unique/definitions.tsdiffbeforeafterboth
--- a/tests/src/interfaces/unique/definitions.ts
+++ b/tests/src/interfaces/unique/definitions.ts
@@ -43,13 +43,13 @@
     accountTokens: fun('Get tokens owned by account', [collectionParam, crossAccountParam()], 'Vec<u32>'),
     collectionTokens: fun('Get tokens contained in collection', [collectionParam], 'Vec<u32>'),
 
-    lastTokenId: fun('Get last token id', [collectionParam], 'u32'),
+    lastTokenId: fun('Get last token ID created in a collection', [collectionParam], 'u32'),
     totalSupply: fun('Get amount of unique collection tokens', [collectionParam], 'u32'),
-    accountBalance: fun('Get amount of different user tokens', [collectionParam, crossAccountParam()], 'u32'),
-    balance: fun('Get amount of specific account token', [collectionParam, crossAccountParam(), tokenParam], 'u128'),
+    accountBalance: fun('Get owned amount of any user tokens', [collectionParam, crossAccountParam()], 'u32'),
+    balance: fun('Get owned amount of specific account token', [collectionParam, crossAccountParam(), tokenParam], 'u128'),
     allowance: fun('Get allowed amount', [collectionParam, crossAccountParam('sender'), crossAccountParam('spender'), tokenParam], 'u128'),
     tokenOwner: fun('Get token owner', [collectionParam, tokenParam], `Option<${CROSS_ACCOUNT_ID_TYPE}>`),
-    topmostTokenOwner: fun('Get token owner, in case of nested token - find parent recursive', [collectionParam, tokenParam], `Option<${CROSS_ACCOUNT_ID_TYPE}>`),
+    topmostTokenOwner: fun('Get token owner, in case of nested token - find the parent recursively', [collectionParam, tokenParam], `Option<${CROSS_ACCOUNT_ID_TYPE}>`),
     tokenChildren: fun('Get tokens nested directly into the token', [collectionParam, tokenParam], 'Vec<UpDataStructsTokenChild>'),
     constMetadata: fun('Get token constant metadata', [collectionParam, tokenParam], 'Vec<u8>'),
     variableMetadata: fun('Get token variable metadata', [collectionParam, tokenParam], 'Vec<u8>'),
@@ -74,7 +74,7 @@
       'UpDataStructsTokenData',
     ),
     tokenExists: fun('Check if token exists', [collectionParam, tokenParam], 'bool'),
-    collectionById: fun('Get collection by specified id', [collectionParam], 'Option<UpDataStructsRpcCollection>'),
+    collectionById: fun('Get collection by specified ID', [collectionParam], 'Option<UpDataStructsRpcCollection>'),
     collectionStats: fun('Get collection stats', [], 'UpDataStructsCollectionStats'),
     allowed: fun('Check if user is allowed to use collection', [collectionParam, crossAccountParam()], 'bool'),
     nextSponsored: fun('Get number of blocks when sponsored transaction is available', [collectionParam, crossAccountParam(), tokenParam], 'Option<u64>'),